Skip to content

Commit

Permalink
DataViews: allow users to add filters dynamically (#55992)
Browse files Browse the repository at this point in the history
  • Loading branch information
oandregal authored Nov 15, 2023
1 parent 90aa9bd commit 1bf654b
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 61 deletions.
5 changes: 1 addition & 4 deletions packages/edit-site/src/components/dataviews/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ Example:
{ field: 'author', operator: 'in', value: 2 },
{ field: 'status', operator: 'in', value: 'publish,draft' }
],
visibleFilters: [ 'author', 'status' ],
hiddenFields: [ 'date', 'featured-image' ],
layout: {},
}
Expand All @@ -62,8 +61,7 @@ Example:
- `filters`: the filters applied to the dataset. Each item describes:
- `field`: which field this filter is bound to.
- `operator`: which type of filter it is. Only `in` available at the moment.
- `vaule`: the actual value selected by the user.
- `visibleFilters`: the `id` of the filters that are visible in the UI.
- `value`: the actual value selected by the user.
- `hiddenFields`: the `id` of the fields that are hidden in the UI.
- `layout`: ...

Expand All @@ -88,7 +86,6 @@ function MyCustomPageList() {
{ field: 'author', operator: 'in', value: 2 },
{ field: 'status', operator: 'in', value: 'publish,draft' }
],
visibleFilters: [ 'author', 'status' ],
hiddenFields: [ 'date', 'featured-image' ],
layout: {},
} );
Expand Down
111 changes: 111 additions & 0 deletions packages/edit-site/src/components/dataviews/add-filter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* WordPress dependencies
*/
import {
privateApis as componentsPrivateApis,
Button,
Icon,
} from '@wordpress/components';
import { chevronRightSmall, plus } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import { unlock } from '../../lock-unlock';
import { OPERATOR_IN } from './in-filter';

const {
DropdownMenuV2,
DropdownSubMenuV2,
DropdownSubMenuTriggerV2,
DropdownMenuItemV2,
} = unlock( componentsPrivateApis );

const VALID_OPERATORS = [ OPERATOR_IN ];

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

field.filters.forEach( ( filter ) => {
if ( VALID_OPERATORS.some( ( operator ) => operator === filter ) ) {
filters.push( {
field: field.id,
name: field.header,
operator: filter,
elements: field.elements || [],
isVisible: view.filters.some(
( f ) => f.field === field.id && f.operator === filter
),
} );
}
} );
} );

if ( filters.length === 0 ) {
return null;
}

return (
<DropdownMenuV2
label={ __( 'Add filter' ) }
trigger={
<Button
disabled={ filters.length === view.filters?.length }
__experimentalIsFocusable
icon={ plus }
variant="tertiary"
size="compact"
>
{ __( 'Add filter' ) }
</Button>
}
>
{ filters.map( ( filter ) => {
if ( filter.isVisible ) {
return null;
}

return (
<DropdownSubMenuV2
key={ filter.field }
trigger={
<DropdownSubMenuTriggerV2
suffix={ <Icon icon={ chevronRightSmall } /> }
>
{ filter.name }
</DropdownSubMenuTriggerV2>
}
>
{ filter.elements.map( ( element ) => (
<DropdownMenuItemV2
key={ element.value }
onSelect={ () => {
onChangeView( ( currentView ) => ( {
...currentView,
page: 1,
filters: [
...currentView.filters,
{
field: filter.field,
operator: 'in',
value: element.value,
},
],
} ) );
} }
role="menuitemcheckbox"
>
{ element.label }
</DropdownMenuItemV2>
) ) }
</DropdownSubMenuV2>
);
} ) }
</DropdownMenuV2>
);
}
66 changes: 34 additions & 32 deletions packages/edit-site/src/components/dataviews/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
import { default as InFilter, OPERATOR_IN } from './in-filter';
import AddFilter from './add-filter';
import ResetFilters from './reset-filters';

const VALID_OPERATORS = [ OPERATOR_IN ];

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

field.filters.forEach( ( filter ) => {
if ( VALID_OPERATORS.some( ( operator ) => operator === filter ) ) {
filtersRegistered.push( {
filters.push( {
field: field.id,
name: field.header,
operator: filter,
Expand All @@ -31,46 +32,47 @@ export default function Filters( { fields, view, onChangeView } ) {
},
...( field.elements || [] ),
],
isVisible: view.filters.some(
( f ) => f.field === field.id && f.operator === filter
),
} );
}
} );
} );

const visibleFilters = view.visibleFilters
?.map( ( fieldName ) => {
const visibleFiltersForField = filtersRegistered.filter(
( f ) => f.field === fieldName
const filterComponents = filters?.map( ( filter ) => {
if ( ! filter.isVisible ) {
return null;
}

if ( OPERATOR_IN === filter.operator ) {
return (
<InFilter
key={ filter.field + '.' + filter.operator }
filter={ filter }
view={ view }
onChangeView={ onChangeView }
/>
);
}

if ( visibleFiltersForField.length === 0 ) {
return null;
}
return null;
} );

return visibleFiltersForField.map( ( filter ) => {
if ( OPERATOR_IN === filter.operator ) {
return (
<InFilter
key={ fieldName + '.' + filter.operator }
filter={ visibleFiltersForField[ 0 ] }
view={ view }
onChangeView={ onChangeView }
/>
);
}
return null;
} );
} )
.filter( Boolean );
filterComponents.push(
<AddFilter
key="add-filter"
fields={ fields }
view={ view }
onChangeView={ onChangeView }
/>
);

if ( visibleFilters?.length > 0 ) {
visibleFilters.push(
<ResetFilters
key="reset-filters"
view={ view }
onChangeView={ onChangeView }
/>
if ( filterComponents.length > 1 ) {
filterComponents.push(
<ResetFilters view={ view } onChangeView={ onChangeView } />
);
}

return visibleFilters;
return filterComponents;
}
14 changes: 7 additions & 7 deletions packages/edit-site/src/components/dataviews/in-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default ( { filter, view, onChangeView } ) => {
return (
<SelectControl
id={ id }
__nextHasNoMarginBottom
value={ activeValue }
prefix={
<InputControlPrefixWrapper
Expand All @@ -44,13 +45,12 @@ export default ( { filter, view, onChangeView } ) => {
( f ) =>
f.field !== filter.field || f.operator !== OPERATOR_IN
);
if ( value !== '' ) {
filters.push( {
field: filter.field,
operator: OPERATOR_IN,
value,
} );
}

filters.push( {
field: filter.field,
operator: OPERATOR_IN,
value,
} );

onChangeView( ( currentView ) => ( {
...currentView,
Expand Down
34 changes: 17 additions & 17 deletions packages/edit-site/src/components/dataviews/reset-filters.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
/**
* WordPress dependencies
*/
import { BaseControl, Button } from '@wordpress/components';
import { Button } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

export default ( { view, onChangeView } ) => {
return (
<BaseControl>
<Button
disabled={ view.search === '' && view.filters?.length === 0 }
variant="tertiary"
onClick={ () => {
onChangeView( ( currentView ) => ( {
...currentView,
page: 1,
search: '',
filters: [],
} ) );
} }
>
{ __( 'Reset filters' ) }
</Button>
</BaseControl>
<Button
disabled={ view.search === '' && view.filters?.length === 0 }
__experimentalIsFocusable
size="compact"
variant="tertiary"
onClick={ () => {
onChangeView( ( currentView ) => ( {
...currentView,
page: 1,
search: '',
filters: [],
} ) );
} }
>
{ __( 'Reset filters' ) }
</Button>
);
};
1 change: 1 addition & 0 deletions packages/edit-site/src/components/dataviews/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export default function Search( { label, view, onChangeView } ) {
const searchLabel = label || __( 'Filter list' );
return (
<SearchControl
__nextHasNoMarginBottom
onChange={ setSearch }
value={ search }
label={ searchLabel }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ const DEFAULT_PAGE_BASE = {
field: 'date',
direction: 'desc',
},
visibleFilters: [ 'author', 'status' ],
// All fields are visible by default, so it's
// better to keep track of the hidden ones.
hiddenFields: [ 'date', 'featured-image' ],
Expand Down

0 comments on commit 1bf654b

Please sign in to comment.