Skip to content

Commit 730f5d2

Browse files
authored
chore: Add positioning test for scroll jumps (#28043)
1 parent 4b637ce commit 730f5d2

1 file changed

Lines changed: 137 additions & 1 deletion

File tree

apps/vr-tests-react-components/src/stories/Positioning.stories.tsx

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { makeStyles, mergeClasses, shorthands } from '@griffel/react';
1010
import { useMergedRefs } from '@fluentui/react-utilities';
1111
import { tokens } from '@fluentui/react-theme';
1212
import { storiesOf } from '@storybook/react';
13+
import * as ReactDOM from 'react-dom';
1314
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';
1415
import { Steps, StoryWright } from 'storywright';
1516

@@ -481,7 +482,6 @@ const DisableTether = () => {
481482
position: 'above',
482483
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
483484
// @ts-ignore
484-
// eslint-disable-next-line @typescript-eslint/naming-convention
485485
unstable_disableTether: 'all',
486486
});
487487

@@ -710,6 +710,129 @@ const FallbackPositioning = () => {
710710
);
711711
};
712712

713+
const ScrollJump: React.FC = () => {
714+
const buttonRef = React.useRef<HTMLButtonElement>(null);
715+
const [open, setOpen] = React.useState(false);
716+
717+
React.useEffect(() => {
718+
if (open && buttonRef.current) {
719+
const scrollY = window.scrollY;
720+
buttonRef.current.focus();
721+
722+
setTimeout(() => {
723+
const element = document.querySelector<HTMLElement>(
724+
scrollY === window.scrollY ? '#test-passed' : '#test-failed',
725+
);
726+
727+
if (element) {
728+
element.style.setProperty('display', 'block');
729+
element.setAttribute('id', 'test-completed');
730+
}
731+
}, 500);
732+
}
733+
}, [open]);
734+
735+
const { containerRef, targetRef } = usePositioning({
736+
position: 'above',
737+
align: 'start',
738+
});
739+
740+
const floating = ReactDOM.createPortal(
741+
<Box ref={containerRef}>
742+
Focusable element <button ref={buttonRef}>Focus me</button>
743+
</Box>,
744+
document.body,
745+
);
746+
747+
return (
748+
<>
749+
<div
750+
style={{
751+
position: 'fixed',
752+
top: 10,
753+
left: 100,
754+
right: 100,
755+
756+
background: 'white',
757+
}}
758+
>
759+
<p style={{ fontWeight: 20, border: '8px dotted magenta', padding: 10, margin: 0 }}>
760+
This example simulates a scroll jump on autofocus when opening a floating element. The example uses a layout
761+
effect to focus on the content of the floating box before usePopper is called. This results in the focus
762+
executing before the layout effect to position the floating is executed. The scroll jump is fixed internally
763+
in usePositioning by using position: fixed on the floating element before it is first positioned.
764+
</p>
765+
<div
766+
id="test-failed"
767+
style={{
768+
border: '8px dotted magenta',
769+
borderTop: 'none',
770+
padding: 10,
771+
fontSize: 20,
772+
color: 'red',
773+
display: 'none',
774+
}}
775+
>
776+
Test failed, scroll jump occurred 💥
777+
</div>
778+
<div
779+
id="test-passed"
780+
style={{
781+
border: '8px dotted magenta',
782+
borderTop: 'none',
783+
padding: 10,
784+
fontSize: 20,
785+
color: 'green',
786+
display: 'none',
787+
}}
788+
>
789+
Test passed ✅
790+
</div>
791+
</div>
792+
793+
<div
794+
style={{
795+
display: 'flex',
796+
flexDirection: 'column',
797+
background: 'repeating-linear-gradient(45deg, transparent, transparent 10px, #ccc 10px, #ccc 20px)',
798+
}}
799+
>
800+
<div style={{ background: 'white', border: '4px dotted green', margin: 10 }}>
801+
<div
802+
style={{
803+
display: 'flex',
804+
805+
background:
806+
'repeating-linear-gradient(135deg, transparent, transparent 15px, #0f6cbd 10px, #0f6cbd 20px)',
807+
margin: 30,
808+
height: '100vh',
809+
}}
810+
/>
811+
812+
<div style={{ border: '4px dotted black', padding: 10, margin: 100 }}>
813+
<button id="target" ref={targetRef} onClick={() => setOpen(s => !s)}>
814+
Target
815+
</button>
816+
</div>
817+
818+
<div
819+
style={{
820+
display: 'flex',
821+
822+
background:
823+
'repeating-linear-gradient(135deg, transparent, transparent 15px, #0f6cbd 10px, #0f6cbd 20px)',
824+
margin: 30,
825+
height: '100vh',
826+
}}
827+
/>
828+
829+
{open && floating}
830+
</div>
831+
</div>
832+
</>
833+
);
834+
};
835+
713836
storiesOf('Positioning', module)
714837
.addDecorator(story => (
715838
<div
@@ -765,3 +888,16 @@ storiesOf('Positioning', module)
765888
() => <PositionAndAlignProps positionFixed useTransform={false} />,
766889
{ includeRtl: true },
767890
);
891+
892+
storiesOf('Positioning (no decorator)', module).addStory('scroll jumps', () => (
893+
<StoryWright
894+
steps={new Steps()
895+
.focus('#target')
896+
.click('#target')
897+
.wait('#test-completed')
898+
.snapshot('positions without scroll jump')
899+
.end()}
900+
>
901+
<ScrollJump />
902+
</StoryWright>
903+
));

0 commit comments

Comments
 (0)