8000 feat(ScrollLock): export `useScroll` to scroll locked layout by EldarMuhamethanov · Pull Request #8137 · VKCOM/VKUI · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(ScrollLock): export useScroll to scroll locked layout #8137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 171 additions & 10 deletions packages/vkui/src/components/AppRoot/ScrollContext.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ describe(useScrollLock, () => {

clearWindowMeasuresMock();
});

test('unmount check', () => {
const h = renderHook(useScrollLock, {
wrapper: ({ children }) => (
Expand All @@ -101,32 +100,93 @@ describe(useScrollLock, () => {
expect(jestWorkaroundGetOverscrollBehaviorPropertyValue(document.documentElement)).toBe('');
});

test('context api', () => {
test.each([true, false])('context api with locked=%s', (locked) => {
const contextRef = createRef<ScrollContextInterface>();
render(
const Fixture = () => (
<GlobalScrollController elRef={createRef<HTMLElement>()}>
<ChildWithContext contextRef={contextRef} />
</GlobalScrollController>,
</GlobalScrollController>
);

const { rerender } = render(<Fixture />);

const clearWindowMeasuresMock = mockWindowMeasures(50, 50);
const clearElementScrollMock = mockElementScroll(document.body, 100, 100);
const clearMockWindowScrollToMock = mockWindowScrollTo();

if (locked) {
contextRef.current?.incrementScrollLockCounter();
rerender(<Fixture />);
}

expect(contextRef.current?.getScroll()).toEqual({ x: 0, y: 50 });
expect(contextRef.current?.getScroll({ compensateKeyboardHeight: false })).toEqual({
x: 0,
y: 0,
});
contextRef.current?.scrollTo(10, 10);
expect(contextRef.current?.getScroll()).toEqual({ x: 10, y: 60 });

if (locked) {
expect(getPositionOfBody()).toEqual([`-${10}px`, `-${10}px`]);
expect(window.pageYOffset).toBe(0);
} else {
expect(window.pageYOffset).toBe(10);
}

contextRef.current?.scrollTo();
expect(contextRef.current?.getScroll()).toEqual({ x: 0, y: 50 });

clearWindowMeasuresMock();
clearElementScrollMock();
clearMockWindowScrollToMock();
});

test('scroll when not locked and then when locked', () => {
const contextRef = createRef<ScrollContextInterface>();
const Fixture = () => (
<GlobalScrollController elRef={createRef<HTMLElement>()}>
<ChildWithContext contextRef={contextRef} />
</GlobalScrollController>
);

const { rerender } = render(<Fixture />);

const clearWindowMeasuresMock = mockWindowMeasures(50, 50);
const clearElementScrollMock = mockElementScroll(document.body, 100, 100);
const clearMockWindowScrollToMock = mockWindowScrollTo();

// Скролим не залоченный скролл
contextRef.current?.scrollTo(10, 10);
expect(window.pageYOffset).toBe(10);
expect(window.pageXOffset).toBe(10);

// Блокируем скролл
contextRef.current?.incrementScrollLockCounter();
rerender(<Fixture />);

// Блокируем скролл - отступы остаются те же
expect(window.pageYOffset).toBe(10);
expect(window.pageXOffset).toBe(10);
expect(getPositionOfBody()).toEqual([`-${10}px`, `-${10}px`]);

// Скролим залоченный скролл
contextRef.current?.scrollTo(25, 25);

expect(getPositionOfBody()).toEqual([`-${25}px`, `-${25}px`]);

// Выключаем блокировку скролла
contextRef.current?.decrementScrollLockCounter();
rerender(<Fixture />);

// Отступы window должны пересчитаться
expect(window.pageYOffset).toBe(25);
expect(window.pageXOffset).toBe(25);

clearWindowMeasuresMock();
clearElementScrollMock();
clearMockWindowScrollToMock();
});
});

describe(ElementScrollController, () => {
Expand Down Expand Up @@ -227,6 +287,97 @@ describe(useScrollLock, () => {
contextRef.current?.scrollTo();
expect(contextRef.current?.getScroll()).toEqual({ x: 0, y: 0 });
});

test.each([true, false])('context api with locked=%s', (locked) => {
const contextRef = createRef<ScrollContextInterface>();
const elRef = createRef<HTMLElement>();
setRef(document.createElement('div'), elRef);
const Fixture = () => (
<ElementScrollController elRef={elRef}>
<ChildWithContext contextRef={contextRef} />
</ElementScrollController>
);

const { rerender } = render(<Fixture />);

const clearElementMeasuresMock = mockElementMeasures(elRef.current!, 50, 50);
const clearElementScrollMock = mockElementScroll(elRef.current!, 100, 100);
const clearElementScrollToMock = mockElementScrollTo(elRef.current!);

if (locked) {
contextRef.current?.incrementScrollLockCounter();
rerender(<Fixture />);
}

expect(contextRef.current?.getScroll()).toEqual({
x: 0,
y: 0,
});
contextRef.current?.scrollTo(10, 10);
expect(contextRef.current?.getScroll()).toEqual({ x: 10, y: 10 });

if (locked) {
expect(getPositionOfElement(elRef.current)).toEqual([`-${10}px`, `-${10}px`]);
expect(elRef.current?.scrollTop).toBe(0);
} else {
expect(elRef.current?.scrollTop).toBe(10);
}

contextRef.current?.scrollTo();
expect(contextRef.current?.getScroll()).toEqual({ x: 0, y: 0 });

clearElementMeasuresMock();
clearElementScrollMock();
clearElementScrollToMock();
});

test('scroll when not locked and then when locked', () => {
const contextRef = createRef<ScrollContextInterface>();
const elRef = createRef<HTMLElement>();
setRef(document.createElement('div'), elRef);
const Fixture = () => (
<ElementScrollController elRef={elRef}>
<ChildWithContext contextRef={contextRef} />
</ElementScrollController>
);

const { rerender } = render(<Fixture />);

const clearElementMeasuresMock = mockElementMeasures(elRef.current!, 50, 50);
const clearElementScrollMock = mockElementScroll(elRef.current!, 100, 100);
const clearElementScrollToMock = mockElementScrollTo(elRef.current!);

// Скролим не залоченный скролл
contextRef.current?.scrollTo(10, 10);
expect(elRef.current?.scrollLeft).toBe(10);
expect(elRef.current?.scrollTop).toBe(10);

// Блокируем скролл
contextRef.current?.incrementScrollLockCounter();
rerender(<Fixture />);

// Блокируем скролл - отступы остаются те же
expect(elRef.current?.scrollLeft).toBe(10);
expect(elRef.current?.scrollTop).toBe(10);
expect(getPositionOfElement(elRef.current)).toEqual([`-${10}px`, `-${10}px`]);

// Скролим залоченный скролл
contextRef.current?.scrollTo(25, 25);

expect(getPositionOfElement(elRef.current)).toEqual([`-${25}px`, `-${25}px`]);

// Выключаем блокировку скролла
contextRef.current?.decrementScrollLockCounter();
rerender(<Fixture />);

// Отступы window должны пересчитаться
expect(elRef.current?.scrollLeft).toBe(25);
expect(elRef.current?.scrollTop).toBe(25);

clearElementMeasuresMock();
clearElementScrollMock();
clearElementScrollToMock();
});
});
});

Expand Down Expand Up @@ -258,6 +409,16 @@ function getStyleAttributeObject(el: HTMLElement | null) {
);
}

function getPositionOfBody() {
const styles = getStyleAttributeObject(document.body);
return styles && [styles.left, styles.top];
}

function getPositionOfElement(element: HTMLElement | null) {
const styles = element && getStyleAttributeObject(element);
return styles && [styles.left, styles.top];
}

function mockWindowMeasures(width: number, height: number) {
const originalW = window.innerWidth;
const originalH = window.innerHeight;
Expand All @@ -273,9 +434,9 @@ function mockWindowScrollTo() {
const original = window.scrollTo;
Object.defineProperty(window, 'scrollTo', {
configurable: true,
value: (x: number, y: number) => {
Object.defineProperty(window, 'pageXOffset', { configurable: true, value: x });
Object.defineProperty(window, 'pageYOffset', { configurable: true, value: y });
value: ({ left, top }: { left: number; top: number }) => {
Object.defineProperty(window, 'pageXOffset', { configurable: true, value: left });
Object.defineProperty(window, 'pageYOffset', { configurable: true, value: top });
},
});
return function clearMock() {
Expand Down Expand Up @@ -309,9 +470,9 @@ function mockElementScrollTo(el: HTMLElement) {
const original = el.scrollTo.bind(el);
Object.defineProperty(el, 'scrollTo', {
configurable: true,
value: (x: number, y: number) => {
Object.defineProperty(el, 'scrollLeft', { configurable: true, value: x });
Object.defineProperty(el, 'scrollTop', { configurable: true, value: y });
value: ({ left, top }: { left: number; top: number }) => {
Object.defineProperty(el, 'scrollLeft', { configurable: true, value: left });
Object.defineProperty(el, 'scrollTop', { configurable: true, value: top });
},
});
return function clearMock() {
Expand Down
Loading
Loading
0