Skip to content

Commit

Permalink
fix: Dropdown will jump react-bootstrap#6715
Browse files Browse the repository at this point in the history
  • Loading branch information
Chris-Gan committed Nov 8, 2023
1 parent a58a0cd commit 3eb959f
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/Dropdown.tsx
Expand Up @@ -29,6 +29,7 @@ export interface DropdownProps
focusFirstItemOnShow?: boolean | 'keyboard';
navbar?: boolean;
autoClose?: boolean | 'outside' | 'inside';
lockPlacement?: boolean;
}

const propTypes = {
Expand Down
4 changes: 4 additions & 0 deletions src/DropdownButton.tsx
Expand Up @@ -17,6 +17,7 @@ export interface DropdownButtonProps
rootCloseEvent?: 'click' | 'mousedown';
menuVariant?: DropdownMenuVariant;
flip?: boolean;
lockPlacement?: boolean;
}

const propTypes = {
Expand Down Expand Up @@ -80,6 +81,7 @@ const propTypes = {
variant: PropTypes.string,
/** @ignore */
size: PropTypes.string,
lockPlacement: PropTypes.bool,
};

/**
Expand Down Expand Up @@ -110,6 +112,7 @@ const DropdownButton: BsPrefixRefForwardingComponent<
id,
menuVariant,
flip,
lockPlacement,
...props
},
ref,
Expand All @@ -126,6 +129,7 @@ const DropdownButton: BsPrefixRefForwardingComponent<
{title}
</DropdownToggle>
<DropdownMenu
lockPlacement={lockPlacement}
role={menuRole}
renderOnMount={renderMenuOnMount}
rootCloseEvent={rootCloseEvent}
Expand Down
26 changes: 25 additions & 1 deletion src/DropdownMenu.tsx
Expand Up @@ -25,6 +25,7 @@ export interface DropdownMenuProps
show?: boolean;
renderOnMount?: boolean;
flip?: boolean;
lockPlacement?: boolean;
align?: AlignType;
rootCloseEvent?: 'click' | 'mousedown';
popperConfig?: UseDropdownMenuOptions['popperConfig'];
Expand Down Expand Up @@ -86,6 +87,13 @@ const propTypes = {
* Omitting this will use the default light color.
*/
variant: PropTypes.string,

/**
*Lock the drop down menu such that when scroll down outside
* or within the viewport, the dropdown will not jump
*
*/
lockPlacement: PropTypes.bool, // New prop to lock the placement
};

export function getDropdownMenuPlacement(
Expand Down Expand Up @@ -128,6 +136,7 @@ const DropdownMenu: BsPrefixRefForwardingComponent<'div', DropdownMenuProps> =
as: Component = 'div',
popperConfig,
variant,
lockPlacement,
...props
},
ref,
Expand Down Expand Up @@ -165,13 +174,28 @@ const DropdownMenu: BsPrefixRefForwardingComponent<'div', DropdownMenuProps> =

const placement = getDropdownMenuPlacement(alignEnd, drop, isRTL);

const mergedPopperConfig = {
...popperConfig,
modifiers: [
...(popperConfig?.modifiers || []),
{
name: 'flip',
enabled: !lockPlacement, // Disable flip modifier when lockPlacement is true
},
{
name: 'preventOverflow',
enabled: !lockPlacement, // Disable preventOverflow modifier when lockPlacement is true
},
],
};

const [menuProps, { hasShown, popper, show, toggle }] = useDropdownMenu({
flip,
rootCloseEvent,
show: showProps,
usePopper: !isNavbar && alignClasses.length === 0,
offset: [0, 2],
popperConfig,
popperConfig: mergedPopperConfig, // Use the merged popperConfig here
placement,
});

Expand Down
46 changes: 46 additions & 0 deletions test/DropdownButtonSpec.tsx
@@ -1,5 +1,6 @@
import { render, fireEvent } from '@testing-library/react';
import sinon from 'sinon';
import { expect } from 'chai';
import DropdownButton from '../src/DropdownButton';
import DropdownItem from '../src/DropdownItem';

Expand Down Expand Up @@ -140,4 +141,49 @@ describe('<DropdownButton>', () => {
const button = getByTestId('test-id').firstElementChild!;
button.classList.contains('my-button-primary').should.be.true;
});

it('maintains its placement when lockPlacement is set to true', () => {
const { container, getByTestId } = render(
<DropdownButton
title="title"
data-testid="test-id"
bsPrefix="my-button"
lockPlacement
align="start"
>
<DropdownItem eventKey="1">Item 1</DropdownItem>
<DropdownItem eventKey="2">Item 2</DropdownItem>
<DropdownItem eventKey="3">Item 3</DropdownItem>
<DropdownItem eventKey="4">Item 4</DropdownItem>
<DropdownItem eventKey="5">Item 5</DropdownItem>
<DropdownItem eventKey="6">Item 6</DropdownItem>
<DropdownItem eventKey="7">Item 7</DropdownItem>
<DropdownItem eventKey="8">Item 8</DropdownItem>
<DropdownItem eventKey="9">Item 9</DropdownItem>
<DropdownItem eventKey="10">Item 10</DropdownItem>
<DropdownItem eventKey="11">Item 11</DropdownItem>
<DropdownItem eventKey="12">Item 12</DropdownItem>
<DropdownItem eventKey="13">Item 13</DropdownItem>
<DropdownItem eventKey="14">Item 14</DropdownItem>
<DropdownItem eventKey="15">Item 15</DropdownItem>
</DropdownButton>,
);
console.log({ container });

fireEvent.click(getByTestId('test-id').firstElementChild!);
const initialPlacement = container
.querySelector('div[x-placement]')!
.getAttribute('x-placement');

// Simulate a scroll event
// Note: You may need to dispatch this event on the window or another scrollable parent,
// depending on where you have attached your scroll listener.
fireEvent.scroll(window, { target: { scrollY: 100 } });

const finalPlacement = container
.querySelector('div[x-placement]')!
.getAttribute('x-placement');

expect(initialPlacement).equal(finalPlacement);
});
});

0 comments on commit 3eb959f

Please sign in to comment.