diff --git a/change/@fluentui-react-drawer-dcd47fc8-4a10-4374-b279-78f83587474c.json b/change/@fluentui-react-drawer-dcd47fc8-4a10-4374-b279-78f83587474c.json new file mode 100644 index 00000000000000..5f937c634bb70a --- /dev/null +++ b/change/@fluentui-react-drawer-dcd47fc8-4a10-4374-b279-78f83587474c.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "fix(react-drawer): allow 1px tolerance in scroll-bottom detection so the footer divider hides at >100% zoom", + "packageName": "@fluentui/react-drawer", + "email": "adam.znamenacek@eway-crm.com", + "dependentChangeType": "patch" +} diff --git a/packages/react-components/react-drawer/library/src/components/DrawerBody/DrawerBody.test.tsx b/packages/react-components/react-drawer/library/src/components/DrawerBody/DrawerBody.test.tsx index 5b2a912d41d320..6d44d0e8c0efe1 100644 --- a/packages/react-components/react-drawer/library/src/components/DrawerBody/DrawerBody.test.tsx +++ b/packages/react-components/react-drawer/library/src/components/DrawerBody/DrawerBody.test.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { render } from '@testing-library/react'; import { DrawerBody } from './DrawerBody'; +import { getScrollState } from './useDrawerBody'; import { isConformant } from '../../testing/isConformant'; describe('DrawerBody', () => { @@ -21,4 +22,66 @@ describe('DrawerBody', () => { `); }); + + describe('getScrollState', () => { + it('returns none when content does not overflow', () => { + const state = getScrollState({ + scrollTop: 0, + scrollHeight: 100, + clientHeight: 100, + } as HTMLElement); + + expect(state).toBe('none'); + }); + + it('returns top when at the start of scrollable content', () => { + const state = getScrollState({ + scrollTop: 0, + scrollHeight: 200, + clientHeight: 100, + } as HTMLElement); + + expect(state).toBe('top'); + }); + + it('returns middle when between top and bottom', () => { + const state = getScrollState({ + scrollTop: 50, + scrollHeight: 200, + clientHeight: 100, + } as HTMLElement); + + expect(state).toBe('middle'); + }); + + it('returns bottom when at the end of scrollable content', () => { + const state = getScrollState({ + scrollTop: 100, + scrollHeight: 200, + clientHeight: 100, + } as HTMLElement); + + expect(state).toBe('bottom'); + }); + + it('returns bottom when within 1px tolerance at the end', () => { + const state = getScrollState({ + scrollTop: 89.4, + scrollHeight: 190, + clientHeight: 100, + } as HTMLElement); + + expect(state).toBe('bottom'); + }); + + it('returns middle when more than 1px away from the end', () => { + const state = getScrollState({ + scrollTop: 88.9, + scrollHeight: 190, + clientHeight: 100, + } as HTMLElement); + + expect(state).toBe('middle'); + }); + }); }); diff --git a/packages/react-components/react-drawer/library/src/components/DrawerBody/useDrawerBody.ts b/packages/react-components/react-drawer/library/src/components/DrawerBody/useDrawerBody.ts index 881fc003aa0066..e2852d8e4caaa1 100644 --- a/packages/react-components/react-drawer/library/src/components/DrawerBody/useDrawerBody.ts +++ b/packages/react-components/react-drawer/library/src/components/DrawerBody/useDrawerBody.ts @@ -16,13 +16,19 @@ import type { DrawerScrollState } from '../../shared/DrawerBase.types'; import type { DrawerBodyProps, DrawerBodyState } from './DrawerBody.types'; import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts'; +/** + * Treat values within 1px of the scroll height as "bottom" to account for + * fractional scroll measurements caused by browser zoom and display scaling. + */ +const SCROLL_BOTTOM_TOLERANCE = 1; + /** * Get the current scroll state of the DrawerBody. * * @internal * @param element - HTMLElement to check scroll state of */ -const getScrollState = ({ scrollTop, scrollHeight, clientHeight }: HTMLElement): DrawerScrollState => { +export const getScrollState = ({ scrollTop, scrollHeight, clientHeight }: HTMLElement): DrawerScrollState => { if (scrollHeight <= clientHeight) { return 'none'; } @@ -31,7 +37,9 @@ const getScrollState = ({ scrollTop, scrollHeight, clientHeight }: HTMLElement): return 'top'; } - if (scrollTop + clientHeight === scrollHeight) { + const distanceFromBottom = scrollHeight - (scrollTop + clientHeight); + + if (distanceFromBottom <= SCROLL_BOTTOM_TOLERANCE) { return 'bottom'; }