diff --git a/src/Dropdown.tsx b/src/Dropdown.tsx index 12e4cd8361..a3a5aa641b 100644 --- a/src/Dropdown.tsx +++ b/src/Dropdown.tsx @@ -46,7 +46,14 @@ const propTypes = { /** * Determines the direction and location of the Menu in relation to it's Toggle. */ - drop: PropTypes.oneOf(['up', 'start', 'end', 'down']), + drop: PropTypes.oneOf([ + 'up', + 'up-centered', + 'start', + 'end', + 'down', + 'down-centered', + ]), as: PropTypes.elementType, @@ -116,6 +123,7 @@ const defaultProps: Partial = { navbar: false, align: 'start', autoClose: true, + drop: 'down', }; const Dropdown: BsPrefixRefForwardingComponent<'div', DropdownProps> = @@ -178,6 +186,15 @@ const Dropdown: BsPrefixRefForwardingComponent<'div', DropdownProps> = [align, drop, isRTL], ); + const directionClasses = { + down: prefix, + 'down-centered': `${prefix}-center`, + up: 'dropup', + 'up-centered': 'dropup-center dropup', + end: 'dropend', + start: 'dropstart', + }; + return ( = className={classNames( className, show && 'show', - (!drop || drop === 'down') && prefix, - drop === 'up' && 'dropup', - drop === 'end' && 'dropend', - drop === 'start' && 'dropstart', + directionClasses[drop!], )} /> )} diff --git a/src/DropdownContext.ts b/src/DropdownContext.ts index 79f74224c6..62d0218be8 100644 --- a/src/DropdownContext.ts +++ b/src/DropdownContext.ts @@ -1,7 +1,13 @@ import * as React from 'react'; import { AlignType } from './types'; -export type DropDirection = 'up' | 'start' | 'end' | 'down'; +export type DropDirection = + | 'up' + | 'up-centered' + | 'start' + | 'end' + | 'down' + | 'down-centered'; export type DropdownContextValue = { align?: AlignType; diff --git a/src/DropdownMenu.tsx b/src/DropdownMenu.tsx index d062cc72cb..36dda9718e 100644 --- a/src/DropdownMenu.tsx +++ b/src/DropdownMenu.tsx @@ -112,6 +112,8 @@ export function getDropdownMenuPlacement( placement = alignEnd ? rightEnd : rightStart; else if (dropDirection === 'start') placement = alignEnd ? leftEnd : leftStart; + else if (dropDirection === 'down-centered') placement = 'bottom'; + else if (dropDirection === 'up-centered') placement = 'top'; return placement; } diff --git a/test/DropdownMenuSpec.tsx b/test/DropdownMenuSpec.tsx index d46752d3a4..cd2e9c8cae 100644 --- a/test/DropdownMenuSpec.tsx +++ b/test/DropdownMenuSpec.tsx @@ -110,11 +110,13 @@ describe('', () => { it('should return top placement', () => { getDropdownMenuPlacement(false, 'up', false).should.equal('top-start'); getDropdownMenuPlacement(true, 'up', false).should.equal('top-end'); + getDropdownMenuPlacement(true, 'up-centered', false).should.equal('top'); }); it('should return top placement for RTL', () => { getDropdownMenuPlacement(false, 'up', true).should.equal('top-end'); getDropdownMenuPlacement(true, 'up', true).should.equal('top-start'); + getDropdownMenuPlacement(true, 'up-centered', true).should.equal('top'); }); it('should return end placement', () => { @@ -132,11 +134,17 @@ describe('', () => { 'bottom-start', ); getDropdownMenuPlacement(true, 'down', false).should.equal('bottom-end'); + getDropdownMenuPlacement(true, 'down-centered', false).should.equal( + 'bottom', + ); }); it('should return bottom placement for RTL', () => { getDropdownMenuPlacement(false, 'down', true).should.equal('bottom-end'); getDropdownMenuPlacement(true, 'down', true).should.equal('bottom-start'); + getDropdownMenuPlacement(true, 'down-centered', true).should.equal( + 'bottom', + ); }); it('should return start placement', () => { diff --git a/test/DropdownSpec.tsx b/test/DropdownSpec.tsx index f494d2375e..e0a00167c9 100644 --- a/test/DropdownSpec.tsx +++ b/test/DropdownSpec.tsx @@ -40,6 +40,31 @@ describe('', () => { }); }); + it('renders div with drop=down-centered', () => { + const { container } = render( + + {dropdownChildren} + , + ); + + container.firstElementChild!.classList.should.not.contain(['dropdown']); + container.firstElementChild!.classList.should.contain([`dropdown-center`]); + }); + + it('renders div with drop=up-centered', () => { + const { container } = render( + + {dropdownChildren} + , + ); + + container.firstElementChild!.classList.should.not.contain(['dropdown']); + container.firstElementChild!.classList.should.contain([ + 'dropup-center', + 'dropup', + ]); + }); + it('renders toggle with Dropdown.Toggle', () => { const { getByText } = render(simpleDropdown); diff --git a/www/src/examples/Dropdown/DropDirections.js b/www/src/examples/Dropdown/DropDirections.js index d8ae6225d8..ed5c19d054 100644 --- a/www/src/examples/Dropdown/DropDirections.js +++ b/www/src/examples/Dropdown/DropDirections.js @@ -6,40 +6,44 @@ function DropDirectioExample() { return ( <>
- {['up', 'down', 'start', 'end'].map((direction) => ( - - Action - Another action - Something else here - - Separated link - - ))} + {['up', 'up-centered', 'down', 'down-centered', 'start', 'end'].map( + (direction) => ( + + Action + Another action + Something else here + + Separated link + + ), + )}
- {['up', 'down', 'start', 'end'].map((direction) => ( - - Action - Another action - Something else here - - Separated link - - ))} + {['up', 'up-centered', 'down', 'down-centered', 'start', 'end'].map( + (direction) => ( + + Action + Another action + Something else here + + Separated link + + ), + )}
);