Skip to content

Commit

Permalink
Popover: refine position-to-placement conversion logic, add tests (#4…
Browse files Browse the repository at this point in the history
…4377)

* Allow single-value `position` prop with yAxis values

* Add more comprehensive list of position-to-placement tests

* Replace custom logic in positionToPlacement with a map of position-to-placement values

* CHANGELOG

* Fix typo
  • Loading branch information
ciampo authored Sep 23, 2022
1 parent 2a429fe commit 0a9402a
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 30 deletions.
13 changes: 10 additions & 3 deletions packages/components/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## Unreleased

### Bug Fix

- `Popover`: fix limitShift logic by adding iframe offset correctly [#42950](https://github.com/WordPress/gutenberg/pull/42950)).
- `Popover`: refine position-to-placement conversion logic, add tests ([#44377](https://github.com/WordPress/gutenberg/pull/44377)).

### Internal

- `Mobile` updated to ignore `react/exhaustive-deps` eslint rule ([#44207](https://github.com/WordPress/gutenberg/pull/44207)).
- `Popover`: refactor unit tests to TypeScript and modern RTL assertions ([#44373](https://github.com/WordPress/gutenberg/pull/44373)).

## 21.1.0 (2022-09-21)

### Deprecations
Expand All @@ -12,16 +22,13 @@

- `Button`: Remove unexpected `has-text` class when empty children are passed ([#44198](https://github.com/WordPress/gutenberg/pull/44198)).
- The `LinkedButton` to unlink sides in `BoxControl`, `BorderBoxControl` and `BorderRadiusControl` have changed from a rectangular primary button to an icon-only button, with a sentence case tooltip, and default-size icon for better legibility. The `Button` component has been fixed so when `isSmall` and `icon` props are set, and no text is present, the button shape is square rather than rectangular.
- `Popover`: fix limitShift logic by adding iframe offset correctly [#42950](https://github.com/WordPress/gutenberg/pull/42950)).

### Internal

- `NavigationMenu` updated to ignore `react/exhaustive-deps` eslint rule ([#44090](https://github.com/WordPress/gutenberg/pull/44090)).
- `Mobile` updated to ignore `react/exhaustive-deps` eslint rule ([#44207](https://github.com/WordPress/gutenberg/pull/44207)).
- `RangeControl`: updated to pass `react/exhaustive-deps` eslint rule ([#44271](https://github.com/WordPress/gutenberg/pull/44271)).
- `UnitControl` updated to pass the `react/exhaustive-deps` eslint rule ([#44161](https://github.com/WordPress/gutenberg/pull/44161)).
- `Notice`: updated to satisfy `react/exhaustive-deps` eslint rule ([#44157](https://github.com/WordPress/gutenberg/pull/44157))
- `Popover`: refactor unit tests to TypeScript and modern RTL assertions ([#44373](https://github.com/WordPress/gutenberg/pull/44373)).

## 21.0.0 (2022-09-13)

Expand Down
70 changes: 59 additions & 11 deletions packages/components/src/popover/test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,64 @@ type PlacementToInitialTranslationTuple = [
CSSProperties[ 'translate' ]
];

// There's no matching `placement` for 'middle center' positions,
// fallback to 'bottom' (same as `floating-ui`'s default.)
const FALLBACK_FOR_MIDDLE_CENTER_POSITIONS = 'bottom';

const ALL_POSITIONS_TO_EXPECTED_PLACEMENTS: PositionToPlacementTuple[] = [
// Format: [yAxis]
[ 'middle', FALLBACK_FOR_MIDDLE_CENTER_POSITIONS ],
[ 'bottom', 'bottom' ],
[ 'top', 'top' ],
// Format: [yAxis] [xAxis]
[ 'middle left', 'left' ],
[ 'middle center', FALLBACK_FOR_MIDDLE_CENTER_POSITIONS ],
[ 'middle right', 'right' ],
[ 'bottom left', 'bottom-end' ],
[ 'bottom center', 'bottom' ],
[ 'bottom right', 'bottom-start' ],
[ 'top left', 'top-end' ],
[ 'top center', 'top' ],
[ 'top right', 'top-start' ],
// Format: [yAxis] [xAxis] [corner]
[ 'middle left left', 'left' ],
[ 'middle left right', 'left' ],
[ 'middle left bottom', 'left-end' ],
[ 'middle left top', 'left-start' ],
[ 'middle center left', FALLBACK_FOR_MIDDLE_CENTER_POSITIONS ],
[ 'middle center right', FALLBACK_FOR_MIDDLE_CENTER_POSITIONS ],
[ 'middle center bottom', FALLBACK_FOR_MIDDLE_CENTER_POSITIONS ],
[ 'middle center top', FALLBACK_FOR_MIDDLE_CENTER_POSITIONS ],
[ 'middle right left', 'right' ],
[ 'middle right right', 'right' ],
[ 'middle right bottom', 'right-end' ],
[ 'middle right top', 'right-start' ],
[ 'bottom left left', 'bottom-end' ],
[ 'bottom left right', 'bottom-end' ],
[ 'bottom left bottom', 'bottom-end' ],
[ 'bottom left top', 'bottom-end' ],
[ 'bottom center left', 'bottom' ],
[ 'bottom center right', 'bottom' ],
[ 'bottom center bottom', 'bottom' ],
[ 'bottom center top', 'bottom' ],
[ 'bottom right left', 'bottom-start' ],
[ 'bottom right right', 'bottom-start' ],
[ 'bottom right bottom', 'bottom-start' ],
[ 'bottom right top', 'bottom-start' ],
[ 'top left left', 'top-end' ],
[ 'top left right', 'top-end' ],
[ 'top left bottom', 'top-end' ],
[ 'top left top', 'top-end' ],
[ 'top center left', 'top' ],
[ 'top center right', 'top' ],
[ 'top center bottom', 'top' ],
[ 'top center top', 'top' ],
[ 'top right left', 'top-start' ],
[ 'top right right', 'top-start' ],
[ 'top right bottom', 'top-start' ],
[ 'top right top', 'top-start' ],
];

describe( 'Popover', () => {
describe( 'Component', () => {
describe( 'basic behavior', () => {
Expand Down Expand Up @@ -93,17 +151,7 @@ describe( 'Popover', () => {
} );

describe( 'positionToPlacement', () => {
it.each( [
[ 'top left', 'top-end' ],
[ 'top center', 'top' ],
[ 'top right', 'top-start' ],
[ 'middle left', 'left' ],
[ 'middle center', 'center' ],
[ 'middle right', 'right' ],
[ 'bottom left', 'bottom-end' ],
[ 'bottom center', 'bottom' ],
[ 'bottom right', 'bottom-start' ],
] as PositionToPlacementTuple[] )(
it.each( ALL_POSITIONS_TO_EXPECTED_PLACEMENTS )(
'converts `%s` to `%s`',
( inputPosition, expectedPlacement ) => {
expect( positionToPlacement( inputPosition ) ).toEqual(
Expand Down
1 change: 1 addition & 0 deletions packages/components/src/popover/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export type PopoverProps = {
* _Note: this prop is deprecated. Use the `placement` prop instead._
*/
position?:
| `${ PositionYAxis }`
| `${ PositionYAxis } ${ PositionXAxis }`
| `${ PositionYAxis } ${ PositionXAxis } ${ PositionCorner }`;
/**
Expand Down
74 changes: 58 additions & 16 deletions packages/components/src/popover/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,62 @@ import type {
PopoverAnchorRefTopBottom,
} from './types';

const POSITION_TO_PLACEMENT: Record<
NonNullable< PopoverProps[ 'position' ] >,
NonNullable< PopoverProps[ 'placement' ] >
> = {
bottom: 'bottom',
top: 'top',
'middle left': 'left',
'middle right': 'right',
'bottom left': 'bottom-end',
'bottom center': 'bottom',
'bottom right': 'bottom-start',
'top left': 'top-end',
'top center': 'top',
'top right': 'top-start',
'middle left left': 'left',
'middle left right': 'left',
'middle left bottom': 'left-end',
'middle left top': 'left-start',
'middle right left': 'right',
'middle right right': 'right',
'middle right bottom': 'right-end',
'middle right top': 'right-start',
'bottom left left': 'bottom-end',
'bottom left right': 'bottom-end',
'bottom left bottom': 'bottom-end',
'bottom left top': 'bottom-end',
'bottom center left': 'bottom',
'bottom center right': 'bottom',
'bottom center bottom': 'bottom',
'bottom center top': 'bottom',
'bottom right left': 'bottom-start',
'bottom right right': 'bottom-start',
'bottom right bottom': 'bottom-start',
'bottom right top': 'bottom-start',
'top left left': 'top-end',
'top left right': 'top-end',
'top left bottom': 'top-end',
'top left top': 'top-end',
'top center left': 'top',
'top center right': 'top',
'top center bottom': 'top',
'top center top': 'top',
'top right left': 'top-start',
'top right right': 'top-start',
'top right bottom': 'top-start',
'top right top': 'top-start',
// `middle`/`middle center [corner?]` positions are associated to a fallback
// `bottom` placement because there aren't any corresponding placement values.
middle: 'bottom',
'middle center': 'bottom',
'middle center bottom': 'bottom',
'middle center left': 'bottom',
'middle center right': 'bottom',
'middle center top': 'bottom',
};

/**
* Converts the `Popover`'s legacy "position" prop to the new "placement" prop
* (used by `floating-ui`).
Expand All @@ -23,22 +79,8 @@ import type {
*/
export const positionToPlacement = (
position: NonNullable< PopoverProps[ 'position' ] >
): NonNullable< PopoverProps[ 'placement' ] > => {
const [ x, y, z ] = position.split( ' ' );

if ( [ 'top', 'bottom' ].includes( x ) ) {
let suffix = '';
if ( ( !! z && z === 'left' ) || y === 'right' ) {
suffix = '-start';
} else if ( ( !! z && z === 'right' ) || y === 'left' ) {
suffix = '-end';
}

return ( x + suffix ) as NonNullable< PopoverProps[ 'placement' ] >;
}

return y as NonNullable< PopoverProps[ 'placement' ] >;
};
): NonNullable< PopoverProps[ 'placement' ] > =>
POSITION_TO_PLACEMENT[ position ] ?? 'bottom';

/**
* @typedef AnimationOrigin
Expand Down

0 comments on commit 0a9402a

Please sign in to comment.