Skip to content

Commit

Permalink
Refactor experimental dropdown menu usages to latest version (#55625)
Browse files Browse the repository at this point in the history
* First attempt at refactoring the view actions dropdown

* note

* Force all menus to have "left-start" placement.

This helps having a more coherent keyboard navigation behaviour

* Port changes missed in rebase due to file renames

* ViewActions: remove unnecessary icon management

* ViewTable: remove SubMenu, SubMenuTrigger, and use RadioItem

* Use Ariakit versions in FilterSummary as well

* AddFilter: migrate to Ariakit

* Update sort behavior to match RadioItem (cannot be unset once set)

* Remove old add-filter file

* Use ariakit components

* remove submenu trigger

* DropdownMenuItem: substitute onSelect by onClick

* Remove old filter-summary file

* FilterSummary: use the ariakit versions

* Substitute trigger by menuitem

* DropdownMenuItem: substitute onSelect by onClick

* Remove old view-table file

* ViewTable: update to ariakit

* Remove trigger

* DropdownMenuItem: substitute onSelect by onClick

* Remove DropdownSubMenu in favor of DropdownMenu across dataviews package

* Remove suffix chevronrightsmall across dataviews

* Fix rebase for view-actions

* Remove unnecessary label

* Use DropdownMenuItemLabel component

* Hide suffix contents to assistive tech

* Refactor conditions to use radio menu items

* Hide suffix contents to assistive technology

* Fix checked check

* Remove unnecessary preventDefault() calls

* Improve isActive check, remove extra icons, remove extra preventDefault(), add import

* Fix label not appearing

* Use filter name instead of field, as done for other menus in the file

* Rename isActive to isChecked

* Remove unneeded radio semantics from hide button

* Refactor to DropdownMenuRadioItem

* Refactor to DrodownMenuRadioItem

* Refactor OPERATORS

* Refactor filter summery operators to radio items

* Fix OPERATORS object && checks

* Remove comment

* Refactor operators code in view table

* Add custom spacing for custom menu radio items

* Remove comment

* Replace preventDefault with hideOnClick={false}

* Move OPERATORS object to common constants file

* Use the `onChange` event instead of `onClick` on radio items + the event value

* Extract custom dropdown radio item implementation

* Swap actual menu radio items with custom implementation

* Fix warning when onChange prop is not defined

* Add a few min widths to dropdowns to avoid jumping and for better while space balance

* Extract sorting directions to constants

* Refactor more code to use the OPERATORS constant instead of individual operators

* Remove un-needed workaround, now that Truncate has been updated

* Remove hardcoded left placements

* Use dot icon instead of check for custom radios

* Use real DropdownMenuRadioItem where possible

* Fix e2e test

* Sort dependencies

* Update package.lock

---------

Co-authored-by: André Maneiro <[email protected]>
  • Loading branch information
ciampo and oandregal authored Dec 21, 2023
1 parent 6435b27 commit 73954e3
Show file tree
Hide file tree
Showing 10 changed files with 436 additions and 471 deletions.
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/dataviews/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"@wordpress/i18n": "file:../i18n",
"@wordpress/icons": "file:../icons",
"@wordpress/keycodes": "file:../keycodes",
"@wordpress/primitives": "file:../primitives",
"@wordpress/private-apis": "file:../private-apis",
"classnames": "^2.3.1",
"remove-accents": "^0.5.0"
Expand Down
240 changes: 104 additions & 136 deletions packages/dataviews/src/add-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@
import {
privateApis as componentsPrivateApis,
Button,
Icon,
} from '@wordpress/components';
import { chevronRightSmall, funnel, check } from '@wordpress/icons';
import { funnel } from '@wordpress/icons';
import { __, sprintf } from '@wordpress/i18n';
import { Children, Fragment } from '@wordpress/element';

/**
* Internal dependencies
*/
import { unlock } from './lock-unlock';
import { LAYOUT_LIST, OPERATOR_IN, OPERATOR_NOT_IN } from './constants';
import { LAYOUT_LIST, OPERATORS } from './constants';
import { DropdownMenuRadioItemCustom } from './dropdown-menu-helper';

const {
DropdownMenuV2: DropdownMenu,
DropdownMenuGroupV2: DropdownMenuGroup,
DropdownSubMenuV2: DropdownSubMenu,
DropdownSubMenuTriggerV2: DropdownSubMenuTrigger,
DropdownMenuItemV2: DropdownMenuItem,
DropdownMenuSeparatorV2: DropdownMenuSeparator,
DropdownMenuV2Ariakit: DropdownMenu,
DropdownMenuGroupV2Ariakit: DropdownMenuGroup,
DropdownMenuItemV2Ariakit: DropdownMenuItem,
DropdownMenuRadioItemV2Ariakit: DropdownMenuRadioItem,
DropdownMenuSeparatorV2Ariakit: DropdownMenuSeparator,
DropdownMenuItemLabelV2Ariakit: DropdownMenuItemLabel,
} = unlock( componentsPrivateApis );

function WithSeparators( { children } ) {
Expand Down Expand Up @@ -50,7 +50,6 @@ export default function AddFilter( { filters, view, onChangeView } ) {

return (
<DropdownMenu
label={ __( 'Filters' ) }
trigger={
<Button
__experimentalIsFocusable
Expand All @@ -66,6 +65,9 @@ export default function AddFilter( { filters, view, onChangeView } ) {
) : null }
</Button>
}
style={ {
minWidth: '230px',
} }
>
<WithSeparators>
<DropdownMenuGroup>
Expand All @@ -82,31 +84,29 @@ export default function AddFilter( { filters, view, onChangeView } ) {
const activeOperator =
filterInView?.operator || filter.operators[ 0 ];
return (
<DropdownSubMenu
<DropdownMenu
key={ filter.field }
trigger={
<DropdownSubMenuTrigger
<DropdownMenuItem
suffix={
<>
{ activeElement &&
activeOperator ===
OPERATOR_IN &&
__( 'Is' ) }
{ activeElement &&
activeOperator ===
OPERATOR_NOT_IN &&
__( 'Is not' ) }
{ activeElement && ' ' }
{ activeElement?.label }
<Icon
icon={ chevronRightSmall }
/>
</>
activeElement && (
<span aria-hidden="true">
{ activeOperator in
OPERATORS &&
`${ OPERATORS[ activeOperator ].label } ` }
{ activeElement.label }
</span>
)
}
>
{ filter.name }
</DropdownSubMenuTrigger>
<DropdownMenuItemLabel>
{ filter.name }
</DropdownMenuItemLabel>
</DropdownMenuItem>
}
style={ {
minWidth: '200px',
} }
>
<WithSeparators>
<DropdownMenuGroup>
Expand All @@ -115,19 +115,12 @@ export default function AddFilter( { filters, view, onChangeView } ) {
activeElement?.value ===
element.value;
return (
<DropdownMenuItem
<DropdownMenuRadioItemCustom
key={ element.value }
role="menuitemradio"
aria-checked={ isActive }
prefix={
isActive && (
<Icon
icon={ check }
/>
)
}
onSelect={ ( event ) => {
event.preventDefault();
name={ `add-filter-${ filter.field.id }` }
value={ element.value }
checked={ isActive }
onClick={ () => {
onChangeView( {
...view,
page: 1,
Expand All @@ -145,106 +138,77 @@ export default function AddFilter( { filters, view, onChangeView } ) {
} );
} }
>
{ element.label }
</DropdownMenuItem>
<DropdownMenuItemLabel>
{ element.label }
</DropdownMenuItemLabel>
</DropdownMenuRadioItemCustom>
);
} ) }
</DropdownMenuGroup>
{ filter.operators.length > 1 && (
<DropdownSubMenu
<DropdownMenu
trigger={
<DropdownSubMenuTrigger
<DropdownMenuItem
suffix={
<>
{ activeOperator ===
OPERATOR_IN &&
__( 'Is' ) }
{ activeOperator ===
OPERATOR_NOT_IN &&
__( 'Is not' ) }
<Icon
icon={
chevronRightSmall
}
/>{ ' ' }
</>
<span aria-hidden="true">
{
OPERATORS[
activeOperator
]?.label
}
</span>
}
>
{ __( 'Conditions' ) }
</DropdownSubMenuTrigger>
<DropdownMenuItemLabel>
{ __( 'Conditions' ) }
</DropdownMenuItemLabel>
</DropdownMenuItem>
}
>
<DropdownMenuItem
key="in-filter"
role="menuitemradio"
aria-checked={
activeOperator ===
OPERATOR_IN
}
prefix={
activeOperator ===
OPERATOR_IN && (
<Icon icon={ check } />
)
}
onSelect={ ( event ) => {
event.preventDefault();
onChangeView( {
...view,
page: 1,
filters: [
...otherFilters,
{
field: filter.field,
operator:
OPERATOR_IN,
value: filterInView?.value,
},
],
} );
} }
>
{ __( 'Is' ) }
</DropdownMenuItem>
<DropdownMenuItem
key="not-in-filter"
role="menuitemradio"
aria-checked={
activeOperator ===
OPERATOR_NOT_IN
}
prefix={
activeOperator ===
OPERATOR_NOT_IN && (
<Icon icon={ check } />
)
}
onSelect={ ( event ) => {
event.preventDefault();
onChangeView( {
...view,
page: 1,
filters: [
...otherFilters,
{
field: filter.field,
operator:
OPERATOR_NOT_IN,
value: filterInView?.value,
},
],
} );
} }
>
{ __( 'Is not' ) }
</DropdownMenuItem>
</DropdownSubMenu>
{ Object.entries( OPERATORS ).map(
( [
operator,
{ label, key },
] ) => (
<DropdownMenuRadioItem
key={ key }
name={ `add-filter-${ filter.name }-conditions` }
value={ operator }
checked={
activeOperator ===
operator
}
onChange={ ( e ) => {
onChangeView( {
...view,
page: 1,
filters: [
...otherFilters,
{
field: filter.field,
operator:
e
.target
.value,
value: filterInView?.value,
},
],
} );
} }
>
<DropdownMenuItemLabel>
{ label }
</DropdownMenuItemLabel>
</DropdownMenuRadioItem>
)
) }
</DropdownMenu>
) }
<DropdownMenuItem
key={ 'reset-filter-' + filter.name }
disabled={ ! activeElement }
onSelect={ ( event ) => {
event.preventDefault();
hideOnClick={ false }
onClick={ () => {
onChangeView( ( currentView ) => ( {
...currentView,
page: 1,
Expand All @@ -257,31 +221,35 @@ export default function AddFilter( { filters, view, onChangeView } ) {
} ) );
} }
>
{ sprintf(
/* translators: 1: Filter name. e.g.: "Reset Author". */
__( 'Reset %1$s' ),
filter.name.toLowerCase()
) }
<DropdownMenuItemLabel>
{ sprintf(
/* translators: 1: Filter name. e.g.: "Reset Author". */
__( 'Reset %1$s' ),
filter.name.toLowerCase()
) }
</DropdownMenuItemLabel>
</DropdownMenuItem>
</WithSeparators>
</DropdownSubMenu>
</DropdownMenu>
);
} ) }
</DropdownMenuGroup>
<DropdownMenuItem
disabled={
view.search === '' && view.filters?.length === 0
}
onSelect={ ( event ) => {
event.preventDefault();
hideOnClick={ false }
onClick={ () => {
onChangeView( ( currentView ) => ( {
...currentView,
page: 1,
filters: [],
} ) );
} }
>
{ __( 'Reset filters' ) }
<DropdownMenuItemLabel>
{ __( 'Reset filters' ) }
</DropdownMenuItemLabel>
</DropdownMenuItem>
</WithSeparators>
</DropdownMenu>
Expand Down
16 changes: 16 additions & 0 deletions packages/dataviews/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@ export const ENUMERATION_TYPE = 'enumeration';
// Filter operators.
export const OPERATOR_IN = 'in';
export const OPERATOR_NOT_IN = 'notIn';
export const OPERATORS = {
[ OPERATOR_IN ]: {
key: 'in-filter',
label: __( 'Is' ),
},
[ OPERATOR_NOT_IN ]: {
key: 'not-in-filter',
label: __( 'Is not' ),
},
};

// Sorting
export const SORTING_DIRECTIONS = {
asc: { label: __( 'Sort ascending' ) },
desc: { label: __( 'Sort descending' ) },
};

// View layouts.
export const LAYOUT_TABLE = 'table';
Expand Down
Loading

0 comments on commit 73954e3

Please sign in to comment.