mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Add flag enableFragmentRefsScrollIntoView, prefix with experimental_
This commit is contained in:
@@ -55,11 +55,11 @@ export default function ScrollIntoViewCase() {
|
||||
const scrollContainerRef = useRef(null);
|
||||
|
||||
const scrollVertical = () => {
|
||||
fragmentRef.current.scrollIntoView(alignToTop);
|
||||
fragmentRef.current.experimental_scrollIntoView(alignToTop);
|
||||
};
|
||||
|
||||
const scrollVerticalNoChildren = () => {
|
||||
noChildRef.current.scrollIntoView(alignToTop);
|
||||
noChildRef.current.experimental_scrollIntoView(alignToTop);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
+38
-35
@@ -124,6 +124,7 @@ import {
|
||||
enableSrcObject,
|
||||
enableViewTransition,
|
||||
enableHydrationChangeEvent,
|
||||
enableFragmentRefsScrollIntoView,
|
||||
} from 'shared/ReactFeatureFlags';
|
||||
import {
|
||||
HostComponent,
|
||||
@@ -3248,46 +3249,48 @@ function validateDocumentPositionWithFiberTree(
|
||||
return false;
|
||||
}
|
||||
|
||||
// $FlowFixMe[prop-missing]
|
||||
FragmentInstance.prototype.scrollIntoView = function (
|
||||
this: FragmentInstanceType,
|
||||
alignToTop?: boolean,
|
||||
): void {
|
||||
if (typeof alignToTop === 'object') {
|
||||
throw new Error(
|
||||
'FragmentInstance.scrollIntoView() does not support ' +
|
||||
'scrollIntoViewOptions. Use the alignToTop boolean instead.',
|
||||
);
|
||||
}
|
||||
// First, get the children nodes
|
||||
const children: Array<Fiber> = [];
|
||||
traverseFragmentInstance(this._fragmentFiber, collectChildren, children);
|
||||
if (enableFragmentRefsScrollIntoView) {
|
||||
// $FlowFixMe[prop-missing]
|
||||
FragmentInstance.prototype.experimental_scrollIntoView = function (
|
||||
this: FragmentInstanceType,
|
||||
alignToTop?: boolean,
|
||||
): void {
|
||||
if (typeof alignToTop === 'object') {
|
||||
throw new Error(
|
||||
'FragmentInstance.experimental_scrollIntoView() does not support ' +
|
||||
'scrollIntoViewOptions. Use the alignToTop boolean instead.',
|
||||
);
|
||||
}
|
||||
// First, get the children nodes
|
||||
const children: Array<Fiber> = [];
|
||||
traverseFragmentInstance(this._fragmentFiber, collectChildren, children);
|
||||
|
||||
// If there are no children, we can use the parent and siblings to determine a position
|
||||
if (children.length === 0) {
|
||||
const hostSiblings = getFragmentInstanceSiblings(this._fragmentFiber);
|
||||
const targetFiber =
|
||||
(alignToTop === false
|
||||
? hostSiblings[0] || hostSiblings[1]
|
||||
: hostSiblings[1] || hostSiblings[0]) ||
|
||||
getFragmentParentHostFiber(this._fragmentFiber);
|
||||
if (targetFiber === null) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'You are attempting to scroll a FragmentInstance that has no ' +
|
||||
'children, siblings, or parent. No scroll was performed.',
|
||||
);
|
||||
// If there are no children, we can use the parent and siblings to determine a position
|
||||
if (children.length === 0) {
|
||||
const hostSiblings = getFragmentInstanceSiblings(this._fragmentFiber);
|
||||
const targetFiber =
|
||||
(alignToTop === false
|
||||
? hostSiblings[0] || hostSiblings[1]
|
||||
: hostSiblings[1] || hostSiblings[0]) ||
|
||||
getFragmentParentHostFiber(this._fragmentFiber);
|
||||
if (targetFiber === null) {
|
||||
if (__DEV__) {
|
||||
console.error(
|
||||
'You are attempting to scroll a FragmentInstance that has no ' +
|
||||
'children, siblings, or parent. No scroll was performed.',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const target = getInstanceFromHostFiber<Instance>(targetFiber);
|
||||
target.scrollIntoView(alignToTop);
|
||||
return;
|
||||
}
|
||||
const target = getInstanceFromHostFiber<Instance>(targetFiber);
|
||||
target.scrollIntoView(alignToTop);
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are children, handle them per scroll container
|
||||
scrollIntoViewByScrollContainer(children, alignToTop !== false);
|
||||
};
|
||||
// If there are children, handle them per scroll container
|
||||
scrollIntoViewByScrollContainer(children, alignToTop !== false);
|
||||
};
|
||||
}
|
||||
|
||||
function isInstanceScrollable(inst: Instance): 0 | 1 | 2 {
|
||||
const style = getComputedStyle(inst);
|
||||
|
||||
+20
-20
@@ -1844,7 +1844,7 @@ describe('FragmentRefs', () => {
|
||||
});
|
||||
|
||||
describe('scrollIntoView', () => {
|
||||
// @gate enableFragmentRefs
|
||||
// @gate enableFragmentRefs && enableFragmentRefsScrollIntoView
|
||||
it('does not yet support options', async () => {
|
||||
const fragmentRef = React.createRef();
|
||||
const root = ReactDOMClient.createRoot(container);
|
||||
@@ -1853,15 +1853,15 @@ describe('FragmentRefs', () => {
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
fragmentRef.current.scrollIntoView({block: 'start'});
|
||||
fragmentRef.current.experimental_scrollIntoView({block: 'start'});
|
||||
}).toThrowError(
|
||||
'FragmentInstance.scrollIntoView() does not support ' +
|
||||
'FragmentInstance.experimental_scrollIntoView() does not support ' +
|
||||
'scrollIntoViewOptions. Use the alignToTop boolean instead.',
|
||||
);
|
||||
});
|
||||
|
||||
describe('with children', () => {
|
||||
// @gate enableFragmentRefs
|
||||
// @gate enableFragmentRefs && enableFragmentRefsScrollIntoView
|
||||
it('calls scrollIntoView on the first child by default, or if alignToTop=true', async () => {
|
||||
const fragmentRef = React.createRef();
|
||||
const childARef = React.createRef();
|
||||
@@ -1883,19 +1883,19 @@ describe('FragmentRefs', () => {
|
||||
childBRef.current.scrollIntoView = jest.fn();
|
||||
|
||||
// Default call
|
||||
fragmentRef.current.scrollIntoView();
|
||||
fragmentRef.current.experimental_scrollIntoView();
|
||||
expect(childARef.current.scrollIntoView).toHaveBeenCalledTimes(1);
|
||||
expect(childBRef.current.scrollIntoView).toHaveBeenCalledTimes(0);
|
||||
|
||||
childARef.current.scrollIntoView.mockClear();
|
||||
|
||||
// alignToTop=true
|
||||
fragmentRef.current.scrollIntoView(true);
|
||||
fragmentRef.current.experimental_scrollIntoView(true);
|
||||
expect(childARef.current.scrollIntoView).toHaveBeenCalledTimes(1);
|
||||
expect(childBRef.current.scrollIntoView).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
// @gate enableFragmentRefs
|
||||
// @gate enableFragmentRefs && enableFragmentRefsScrollIntoView
|
||||
it('calls scrollIntoView on the last child if alignToTop is false', async () => {
|
||||
const fragmentRef = React.createRef();
|
||||
const childARef = React.createRef();
|
||||
@@ -1913,12 +1913,12 @@ describe('FragmentRefs', () => {
|
||||
childARef.current.scrollIntoView = jest.fn();
|
||||
childBRef.current.scrollIntoView = jest.fn();
|
||||
|
||||
fragmentRef.current.scrollIntoView(false);
|
||||
fragmentRef.current.experimental_scrollIntoView(false);
|
||||
expect(childARef.current.scrollIntoView).toHaveBeenCalledTimes(0);
|
||||
expect(childBRef.current.scrollIntoView).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// @gate enableFragmentRefs
|
||||
// @gate enableFragmentRefs && enableFragmentRefsScrollIntoView
|
||||
it('handles portaled elements -- same scroll container', async () => {
|
||||
const fragmentRef = React.createRef();
|
||||
const childARef = React.createRef();
|
||||
@@ -1950,12 +1950,12 @@ describe('FragmentRefs', () => {
|
||||
childBRef.current.scrollIntoView = jest.fn();
|
||||
|
||||
// Default call
|
||||
fragmentRef.current.scrollIntoView();
|
||||
fragmentRef.current.experimental_scrollIntoView();
|
||||
expect(childARef.current.scrollIntoView).toHaveBeenCalledTimes(1);
|
||||
expect(childBRef.current.scrollIntoView).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
// @gate enableFragmentRefs
|
||||
// @gate enableFragmentRefs && enableFragmentRefsScrollIntoView
|
||||
it('handles portaled elements -- different scroll container', async () => {
|
||||
const fragmentRef = React.createRef();
|
||||
const headerChildRef = React.createRef();
|
||||
@@ -2086,7 +2086,7 @@ describe('FragmentRefs', () => {
|
||||
});
|
||||
|
||||
// Default call
|
||||
fragmentRef.current.scrollIntoView();
|
||||
fragmentRef.current.experimental_scrollIntoView();
|
||||
expect(childCRef.current.scrollIntoView).toHaveBeenCalledTimes(1);
|
||||
// In the same group as A, we use the first child
|
||||
expect(childBRef.current.scrollIntoView).toHaveBeenCalledTimes(0);
|
||||
@@ -2102,7 +2102,7 @@ describe('FragmentRefs', () => {
|
||||
logs = [];
|
||||
|
||||
// // alignToTop=false
|
||||
fragmentRef.current.scrollIntoView(false);
|
||||
fragmentRef.current.experimental_scrollIntoView(false);
|
||||
expect(headerChildRef.current.scrollIntoView).toHaveBeenCalledTimes(1);
|
||||
// In the same group as B, only attempt B which is the last child
|
||||
expect(childARef.current.scrollIntoView).toHaveBeenCalledTimes(0);
|
||||
@@ -2116,7 +2116,7 @@ describe('FragmentRefs', () => {
|
||||
});
|
||||
|
||||
describe('without children', () => {
|
||||
// @gate enableFragmentRefs
|
||||
// @gate enableFragmentRefs && enableFragmentRefsScrollIntoView
|
||||
it('calls scrollIntoView on the next sibling by default, or if alignToTop=true', async () => {
|
||||
const fragmentRef = React.createRef();
|
||||
const siblingARef = React.createRef();
|
||||
@@ -2138,19 +2138,19 @@ describe('FragmentRefs', () => {
|
||||
siblingBRef.current.scrollIntoView = jest.fn();
|
||||
|
||||
// Default call
|
||||
fragmentRef.current.scrollIntoView();
|
||||
fragmentRef.current.experimental_scrollIntoView();
|
||||
expect(siblingARef.current.scrollIntoView).toHaveBeenCalledTimes(0);
|
||||
expect(siblingBRef.current.scrollIntoView).toHaveBeenCalledTimes(1);
|
||||
|
||||
siblingBRef.current.scrollIntoView.mockClear();
|
||||
|
||||
// alignToTop=true
|
||||
fragmentRef.current.scrollIntoView(true);
|
||||
fragmentRef.current.experimental_scrollIntoView(true);
|
||||
expect(siblingARef.current.scrollIntoView).toHaveBeenCalledTimes(0);
|
||||
expect(siblingBRef.current.scrollIntoView).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
// @gate enableFragmentRefs
|
||||
// @gate enableFragmentRefs && enableFragmentRefsScrollIntoView
|
||||
it('calls scrollIntoView on the prev sibling if alignToTop is false', async () => {
|
||||
const fragmentRef = React.createRef();
|
||||
const siblingARef = React.createRef();
|
||||
@@ -2182,12 +2182,12 @@ describe('FragmentRefs', () => {
|
||||
siblingBRef.current.scrollIntoView = jest.fn();
|
||||
|
||||
// alignToTop=false
|
||||
fragmentRef.current.scrollIntoView(false);
|
||||
fragmentRef.current.experimental_scrollIntoView(false);
|
||||
expect(siblingARef.current.scrollIntoView).toHaveBeenCalledTimes(1);
|
||||
expect(siblingBRef.current.scrollIntoView).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
// @gate enableFragmentRefs
|
||||
// @gate enableFragmentRefs && enableFragmentRefsScrollIntoView
|
||||
it('calls scrollIntoView on the parent if there are no siblings', async () => {
|
||||
const fragmentRef = React.createRef();
|
||||
const parentRef = React.createRef();
|
||||
@@ -2203,7 +2203,7 @@ describe('FragmentRefs', () => {
|
||||
});
|
||||
|
||||
parentRef.current.scrollIntoView = jest.fn();
|
||||
fragmentRef.current.scrollIntoView();
|
||||
fragmentRef.current.experimental_scrollIntoView();
|
||||
expect(parentRef.current.scrollIntoView).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -152,6 +152,7 @@ export const transitionLaneExpirationMs = 5000;
|
||||
export const enableInfiniteRenderLoopDetection: boolean = false;
|
||||
|
||||
export const enableFragmentRefs = __EXPERIMENTAL__;
|
||||
export const enableFragmentRefsScrollIntoView = __EXPERIMENTAL__;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Ready for next major.
|
||||
|
||||
@@ -25,4 +25,5 @@ export const enableEagerAlternateStateNodeCleanup = __VARIANT__;
|
||||
export const passChildrenWhenCloningPersistedNodes = __VARIANT__;
|
||||
export const renameElementSymbol = __VARIANT__;
|
||||
export const enableFragmentRefs = __VARIANT__;
|
||||
export const enableFragmentRefsScrollIntoView = __VARIANT__;
|
||||
export const enableComponentPerformanceTrack = __VARIANT__;
|
||||
|
||||
@@ -27,6 +27,7 @@ export const {
|
||||
passChildrenWhenCloningPersistedNodes,
|
||||
renameElementSymbol,
|
||||
enableFragmentRefs,
|
||||
enableFragmentRefsScrollIntoView,
|
||||
} = dynamicFlags;
|
||||
|
||||
// The rest of the flags are static for better dead code elimination.
|
||||
|
||||
@@ -73,6 +73,7 @@ export const enableDefaultTransitionIndicator: boolean = false;
|
||||
export const ownerStackLimit = 1e4;
|
||||
|
||||
export const enableFragmentRefs: boolean = false;
|
||||
export const enableFragmentRefsScrollIntoView: boolean = false;
|
||||
|
||||
// Profiling Only
|
||||
export const enableProfilerTimer: boolean = __PROFILE__;
|
||||
|
||||
@@ -75,6 +75,7 @@ export const enableDefaultTransitionIndicator: boolean = false;
|
||||
export const ownerStackLimit = 1e4;
|
||||
|
||||
export const enableFragmentRefs: boolean = false;
|
||||
export const enableFragmentRefsScrollIntoView: boolean = false;
|
||||
|
||||
// TODO: This must be in sync with the main ReactFeatureFlags file because
|
||||
// the Test Renderer's value must be the same as the one used by the
|
||||
|
||||
@@ -68,6 +68,7 @@ export const enableSrcObject = false;
|
||||
export const enableHydrationChangeEvent = false;
|
||||
export const enableDefaultTransitionIndicator = false;
|
||||
export const enableFragmentRefs = false;
|
||||
export const enableFragmentRefsScrollIntoView = false;
|
||||
export const ownerStackLimit = 1e4;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
|
||||
@@ -82,6 +82,7 @@ export const enableHydrationChangeEvent: boolean = false;
|
||||
export const enableDefaultTransitionIndicator: boolean = false;
|
||||
|
||||
export const enableFragmentRefs: boolean = false;
|
||||
export const enableFragmentRefsScrollIntoView: boolean = false;
|
||||
export const ownerStackLimit = 1e4;
|
||||
|
||||
// Flow magic to verify the exports of this file match the original version.
|
||||
|
||||
@@ -35,6 +35,7 @@ export const enableViewTransition: boolean = __VARIANT__;
|
||||
export const enableComponentPerformanceTrack: boolean = __VARIANT__;
|
||||
export const enableScrollEndPolyfill: boolean = __VARIANT__;
|
||||
export const enableFragmentRefs: boolean = __VARIANT__;
|
||||
export const enableFragmentRefsScrollIntoView: boolean = __VARIANT__;
|
||||
|
||||
// TODO: These flags are hard-coded to the default values used in open source.
|
||||
// Update the tests so that they pass in either mode, then set these
|
||||
|
||||
@@ -33,6 +33,7 @@ export const {
|
||||
enableComponentPerformanceTrack,
|
||||
enableScrollEndPolyfill,
|
||||
enableFragmentRefs,
|
||||
enableFragmentRefsScrollIntoView,
|
||||
} = dynamicFeatureFlags;
|
||||
|
||||
// On WWW, __EXPERIMENTAL__ is used for a new modern build.
|
||||
|
||||
@@ -551,5 +551,5 @@
|
||||
"563": "This render completed successfully. All cacheSignals are now aborted to allow clean up of any unused resources.",
|
||||
"564": "Unknown command. The debugChannel was not wired up properly.",
|
||||
"565": "resolveDebugMessage/closeDebugChannel should not be called for a Request that wasn't kept alive. This is a bug in React.",
|
||||
"566": "FragmentInstance.scrollIntoView() does not support scrollIntoViewOptions. Use the alignToTop boolean instead."
|
||||
"566": "FragmentInstance.experimental_scrollIntoView() does not support scrollIntoViewOptions. Use the alignToTop boolean instead."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user