Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
Original file line number Diff line number Diff line change
@@ -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"
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,49 @@ describe('DrawerBody', () => {
// wait for any rAF-based updates and then assert the scrollState
.then(() => cy.get('#scroll-state').should('have.text', 'bottom'));
});

it('treats near-bottom fractional scroll values as bottom', () => {
const Example = () => {
const context = useDrawerContextValue();

return (
<DrawerProvider value={context}>
<div id="scroll-state">{context.scrollState}</div>
<DrawerBody id="drawer-body" style={{ height: '200px' }}>
Content
</DrawerBody>
</DrawerProvider>
);
};

mountFluent(<Example />);

cy.get('#drawer-body').then($e => {
Comment thread
PaulGMardling marked this conversation as resolved.
Outdated
const element = $e[0] as HTMLDivElement;
let mockedScrollTop = 89.4;

Object.defineProperty(element, 'clientHeight', {
value: 100,
configurable: true,
});

Object.defineProperty(element, 'scrollHeight', {
value: 190,
configurable: true,
});

Object.defineProperty(element, 'scrollTop', {
configurable: true,
get: () => mockedScrollTop,
set: value => {
mockedScrollTop = value;
},
});

mockedScrollTop = 89.4;
element.dispatchEvent(new Event('scroll', { bubbles: true }));
});

cy.get('#scroll-state').should('have.text', 'bottom');
});
});
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand All @@ -21,4 +22,66 @@ describe('DrawerBody', () => {
</div>
`);
});

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');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@ import type { DrawerScrollState } from '../../shared/DrawerBase.types';
import type { DrawerBodyProps, DrawerBodyState } from './DrawerBody.types';
import { useFluent_unstable as useFluent } from '@fluentui/react-shared-contexts';

// Allow a 1px tolerance so fractional scroll metrics (browser zoom, display scaling)
// still resolve to 'bottom' instead of getting stuck at 'middle'
const SCROLL_BOTTOM_TOLERANCE = 1;
Comment thread
Aidam1 marked this conversation as resolved.
Outdated

/**
* 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';
}
Expand All @@ -31,7 +35,7 @@ const getScrollState = ({ scrollTop, scrollHeight, clientHeight }: HTMLElement):
return 'top';
}

if (scrollTop + clientHeight === scrollHeight) {
if (scrollTop + clientHeight >= scrollHeight - SCROLL_BOTTOM_TOLERANCE) {
Comment thread
Aidam1 marked this conversation as resolved.
Outdated
return 'bottom';
}

Expand Down