Skip to content

Commit

Permalink
DataViews: make filters footprint more condensed (#56983)
Browse files Browse the repository at this point in the history
Co-authored-by: James Koster <james@jameskoster.co.uk>
  • Loading branch information
oandregal and jameskoster authored Dec 13, 2023
1 parent 000872e commit a51534b
Show file tree
Hide file tree
Showing 8 changed files with 343 additions and 90 deletions.
328 changes: 263 additions & 65 deletions packages/dataviews/src/add-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,101 +6,299 @@ import {
Button,
Icon,
} from '@wordpress/components';
import { chevronRightSmall, plus } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
import { chevronRightSmall, funnel, check } from '@wordpress/icons';
import { __, sprintf } from '@wordpress/i18n';
import { Children, Fragment } from '@wordpress/element';

/**
* Internal dependencies
*/
import { unlock } from './lock-unlock';
import { ENUMERATION_TYPE, OPERATOR_IN } from './constants';
import { LAYOUT_LIST, OPERATOR_IN, OPERATOR_NOT_IN } from './constants';

const {
DropdownMenuV2: DropdownMenu,
DropdownMenuGroupV2: DropdownMenuGroup,
DropdownSubMenuV2: DropdownSubMenu,
DropdownSubMenuTriggerV2: DropdownSubMenuTrigger,
DropdownMenuItemV2: DropdownMenuItem,
DropdownMenuSeparatorV2: DropdownMenuSeparator,
} = unlock( componentsPrivateApis );

export default function AddFilter( { fields, view, onChangeView } ) {
const filters = [];
fields.forEach( ( field ) => {
if ( ! field.type ) {
return;
}

switch ( field.type ) {
case ENUMERATION_TYPE:
filters.push( {
field: field.id,
name: field.header,
elements: field.elements || [],
isVisible: view.filters.some(
( f ) => f.field === field.id
),
} );
}
} );
function WithSeparators( { children } ) {
return Children.toArray( children )
.filter( Boolean )
.map( ( child, i ) => (
<Fragment key={ i }>
{ i > 0 && <DropdownMenuSeparator /> }
{ child }
</Fragment>
) );
}

export default function AddFilter( { filters, view, onChangeView } ) {
if ( filters.length === 0 ) {
return null;
}

const filterCount = view.filters.reduce( ( acc, filter ) => {
if ( filter.value !== undefined ) {
return acc + 1;
}
return acc;
}, 0 );

return (
<DropdownMenu
label={ __( 'Add filter' ) }
label={ __( 'Filters' ) }
trigger={
<Button
disabled={ filters.length === view.filters?.length }
__experimentalIsFocusable
label={ __( 'Filters' ) }
variant="tertiary"
size="compact"
icon={ funnel }
className="dataviews-filters-button"
>
<Icon icon={ plus } style={ { flexShrink: 0 } } />
{ __( 'Add filter' ) }
{ view.type === LAYOUT_LIST && filterCount > 0 ? (
<span className="dataviews-filters-count">
{ filterCount }
</span>
) : null }
</Button>
}
>
{ filters.map( ( filter ) => {
if ( filter.isVisible ) {
return null;
}

return (
<DropdownSubMenu
key={ filter.field }
trigger={
<DropdownSubMenuTrigger
suffix={ <Icon icon={ chevronRightSmall } /> }
<WithSeparators>
<DropdownMenuGroup>
{ filters.map( ( filter ) => {
const filterInView = view.filters.find(
( f ) => f.field === filter.field
);
const activeElement = filter.elements.find(
( element ) => element.value === filterInView?.value
);
const activeOperator =
filterInView?.operator || filter.operators[ 0 ];
return (
<DropdownSubMenu
key={ filter.field }
trigger={
<DropdownSubMenuTrigger
suffix={
<>
{ activeElement &&
activeOperator ===
OPERATOR_IN &&
__( 'Is' ) }
{ activeElement &&
activeOperator ===
OPERATOR_NOT_IN &&
__( 'Is not' ) }
{ activeElement && ' ' }
{ activeElement?.label }
<Icon
icon={ chevronRightSmall }
/>
</>
}
>
{ filter.name }
</DropdownSubMenuTrigger>
}
>
{ filter.name }
</DropdownSubMenuTrigger>
}
>
{ filter.elements.map( ( element ) => (
<DropdownMenuItem
key={ element.value }
onSelect={ () => {
onChangeView( ( currentView ) => ( {
...currentView,
page: 1,
filters: [
...currentView.filters,
{
field: filter.field,
operator: OPERATOR_IN,
value: element.value,
},
],
} ) );
} }
>
{ element.label }
</DropdownMenuItem>
) ) }
</DropdownSubMenu>
);
} ) }
<WithSeparators>
<DropdownMenuGroup>
{ filter.elements.map( ( element ) => (
<DropdownMenuItem
key={ element.value }
role="menuitemradio"
aria-checked={
activeElement?.value ===
element.value
}
prefix={
activeElement?.value ===
element.value && (
<Icon icon={ check } />
)
}
onSelect={ ( event ) => {
event.preventDefault();
onChangeView(
( currentView ) => ( {
...currentView,
page: 1,
filters: [
...currentView.filters.filter(
( f ) =>
f.field !==
filter.field
),
{
field: filter.field,
operator:
activeOperator,
value:
activeElement?.value ===
element.value
? undefined
: element.value,
},
],
} )
);
} }
>
{ element.label }
</DropdownMenuItem>
) ) }
</DropdownMenuGroup>
{ filter.operators.length > 1 && (
<DropdownSubMenu
trigger={
<DropdownSubMenuTrigger
suffix={
<>
{ activeOperator ===
OPERATOR_IN &&
__( 'Is' ) }
{ activeOperator ===
OPERATOR_NOT_IN &&
__( 'Is not' ) }
<Icon
icon={
chevronRightSmall
}
/>{ ' ' }
</>
}
>
{ __( 'Conditions' ) }
</DropdownSubMenuTrigger>
}
>
<DropdownMenuItem
key="in-filter"
role="menuitemradio"
aria-checked={
activeOperator ===
OPERATOR_IN
}
prefix={
activeOperator ===
OPERATOR_IN && (
<Icon icon={ check } />
)
}
onSelect={ ( event ) => {
event.preventDefault();
onChangeView(
( currentView ) => ( {
...currentView,
page: 1,
filters: [
...view.filters.filter(
( f ) =>
f.field !==
filter.field
),
{
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(
( currentView ) => ( {
...currentView,
page: 1,
filters: [
...view.filters.filter(
( f ) =>
f.field !==
filter.field
),
{
field: filter.field,
operator:
OPERATOR_NOT_IN,
value: filterInView?.value,
},
],
} )
);
} }
>
{ __( 'Is not' ) }
</DropdownMenuItem>
</DropdownSubMenu>
) }
<DropdownMenuItem
key={ 'reset-filter-' + filter.name }
disabled={ ! activeElement }
onSelect={ ( event ) => {
event.preventDefault();
onChangeView( ( currentView ) => ( {
...currentView,
page: 1,
filters:
currentView.filters.filter(
( f ) =>
f.field !==
filter.field
),
} ) );
} }
>
{ sprintf(
/* translators: 1: Filter name. e.g.: "Reset Author". */
__( 'Reset %1$s' ),
filter.name.toLowerCase()
) }
</DropdownMenuItem>
</WithSeparators>
</DropdownSubMenu>
);
} ) }
</DropdownMenuGroup>
<DropdownMenuItem
disabled={
view.search === '' && view.filters?.length === 0
}
onSelect={ ( event ) => {
event.preventDefault();
onChangeView( ( currentView ) => ( {
...currentView,
page: 1,
filters: [],
} ) );
} }
>
{ __( 'Reset filters' ) }
</DropdownMenuItem>
</WithSeparators>
</DropdownMenu>
);
}
6 changes: 5 additions & 1 deletion packages/dataviews/src/filter-summary.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { Children, Fragment } from '@wordpress/element';
/**
* Internal dependencies
*/
import { OPERATOR_IN, OPERATOR_NOT_IN } from './constants';
import { OPERATOR_IN, OPERATOR_NOT_IN, LAYOUT_LIST } from './constants';
import { unlock } from './lock-unlock';

const {
Expand Down Expand Up @@ -73,6 +73,10 @@ function WithSeparators( { children } ) {
}

export default function FilterSummary( { filter, view, onChangeView } ) {
if ( view.type === LAYOUT_LIST ) {
return null;
}

const filterInView = view.filters.find( ( f ) => f.field === filter.field );
const activeElement = filter.elements.find(
( element ) => element.value === filterInView?.value
Expand Down
Loading

1 comment on commit a51534b

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in a51534b.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/7197224686
📝 Reported issues:

Please sign in to comment.