Skip to content

Commit

Permalink
fix(ScrollContext): Fix scroll position after opening ActionSheet fro…
Browse files Browse the repository at this point in the history
…m ModalPage (#6614)

Если проскролить панель, открыть `ModalPage`, а затем из него `ActionSheet`, то скролл панели сбросится и мы увидим верхний контент страницы (за оверлеем).

Оказалось, что и `ModalPage` и `ActionSheet` используют один и тот же контекст при открытии для запрета скролла.
Но фукция, запрещающая скролл не проверяет, были ли он уже запрещён или нет.
Если он уже был запрещён, значит нужные отступы у body уже выставлены, и повторный запуск логики по запрету скролла неправильно перебивает отступы.
  • Loading branch information
mendrew authored Feb 28, 2024
1 parent 08ec8c6 commit 25e647c
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 3 deletions.
53 changes: 52 additions & 1 deletion packages/vkui/src/components/AppRoot/AppRoot.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import { noop } from '@vkontakte/vkjs';
import { Appearance } from '../../lib/appearance';
import { Platform } from '../../lib/platform';
import { DEFAULT_TOKENS_CLASS_NAMES } from '../../lib/tokens';
Expand All @@ -8,7 +9,7 @@ import { AdaptivityProvider } from '../AdaptivityProvider/AdaptivityProvider';
import { ConfigProvider } from '../ConfigProvider/ConfigProvider';
import { AppRoot, AppRootProps } from './AppRoot';
import { AppRootContext } from './AppRootContext';
import { useScrollLock } from './ScrollContext';
import { ScrollContext, ScrollContextInterface, useScrollLock } from './ScrollContext';
import { CUSTOM_PROPERTY_INSET_PREFIX } from './helpers';
import styles from './AppRoot.module.css';

Expand Down Expand Up @@ -83,6 +84,56 @@ describe('AppRoot', () => {
expect(result.getByTestId('app-root')).not.toHaveStyle('position: absolute');
});

it('should not call enableScrollLock if scroll is already locked', () => {
let isScrollLockStub = false;
const enableScrollLockStub = jest.fn(() => (isScrollLockStub = true));
const disableScrollLockStub = jest.fn();
const scrollContextStub: ScrollContextInterface = {
getScroll: () => ({ x: 0, y: 0 }),
scrollTo: noop,
get isScrollLock() {
return Boolean(isScrollLockStub);
},
enableScrollLock: enableScrollLockStub,
disableScrollLock: disableScrollLockStub,
};

const ScrollToggler = () => {
useScrollLock(true);
return null;
};

const ScrollStub = ({ children }: { children: React.ReactNode }) => {
return <ScrollContext.Provider value={scrollContextStub}>{children}</ScrollContext.Provider>;
};

const Template = (props: Pick<AppRootProps, 'scroll'>) => {
const [showAnotherToggler, setShowAnotherToggler] = React.useState(false);
return (
<AppRoot mode="full" data-testid="app-root" {...props}>
<ScrollStub>
<ScrollToggler />
{showAnotherToggler && <ScrollToggler />}
<button onClick={() => setShowAnotherToggler(true)}>Show another toggler</button>
</ScrollStub>
</AppRoot>
);
};

const { unmount } = render(<Template />);
// первый компонент вызвал scrollLock
expect(enableScrollLockStub).toHaveBeenCalledTimes(1);
// второй появившийся компонент вызвал scrollLock
fireEvent.click(screen.getByText('Show another toggler'));

// enableScrollLock должен быть вызван лишь раз
expect(enableScrollLockStub).toHaveBeenCalledTimes(1);

unmount();
// disableScrollLock должен быть вызван лишь раз
expect(disableScrollLockStub).toHaveBeenCalledTimes(1);
});

describe('portalRoot in mode="embedded"', () => {
it.each(['embedded', 'partial'] as const)(
'should create and inject portal in %s mode',
Expand Down
4 changes: 2 additions & 2 deletions packages/vkui/src/components/AppRoot/ScrollContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,9 @@ export const useScrollLockEffect = (effect: React.EffectCallback, deps: React.De
};

export const useScrollLock = (enabled = true) => {
const { enableScrollLock, disableScrollLock } = useScroll();
const { enableScrollLock, disableScrollLock, isScrollLock } = useScroll();
useIsomorphicLayoutEffect(() => {
if (enabled) {
if (enabled && !isScrollLock) {
enableScrollLock();
return disableScrollLock;
}
Expand Down

0 comments on commit 25e647c

Please sign in to comment.