Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DataViews: simplify selection setting #62846

Merged
merged 1 commit into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions packages/dataviews/src/bulk-actions-toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { useRegistry } from '@wordpress/data';
import { ActionWithModal } from './item-actions';
import type { Action } from './types';
import type { ActionTriggerProps } from './item-actions';
import type { SetSelection } from './private-types';

interface ActionButtonProps< Item > {
action: Action< Item >;
Expand All @@ -32,14 +33,14 @@ interface ToolbarContentProps< Item > {
selection: string[];
actionsToShow: Action< Item >[];
selectedItems: Item[];
onSelectionChange: ( selection: Item[] ) => void;
onSelectionChange: SetSelection;
}

interface BulkActionsToolbarProps< Item > {
data: Item[];
selection: string[];
actions: Action< Item >[];
onSelectionChange: ( selection: Item[] ) => void;
onSelectionChange: SetSelection;
getItemId: ( item: Item ) => string;
}

Expand Down Expand Up @@ -131,7 +132,7 @@ function renderToolbarContent< Item >(
selectedItems: Item[],
actionInProgress: string | null,
setActionInProgress: ( actionId: string | null ) => void,
onSelectionChange: ( selection: Item[] ) => void
onSelectionChange: SetSelection
) {
return (
<>
Expand Down
9 changes: 7 additions & 2 deletions packages/dataviews/src/bulk-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useRegistry } from '@wordpress/data';
*/
import { unlock } from './lock-unlock';
import type { Action, ActionModal } from './types';
import type { SetSelection } from './private-types';

const {
DropdownMenuV2: DropdownMenu,
Expand Down Expand Up @@ -46,7 +47,7 @@ interface BulkActionsProps< Item > {
data: Item[];
actions: Action< Item >[];
selection: string[];
onSelectionChange: ( selection: Item[] ) => void;
onSelectionChange: SetSelection;
getItemId: ( item: Item ) => string;
}

Expand Down Expand Up @@ -248,7 +249,11 @@ export default function BulkActions< Item >( {
disabled={ areAllSelected }
hideOnClick={ false }
onClick={ () => {
onSelectionChange( selectableItems );
onSelectionChange(
oandregal marked this conversation as resolved.
Show resolved Hide resolved
selectableItems.map( ( item ) =>
getItemId( item )
)
);
} }
suffix={ numberSelectableItems }
>
Expand Down
43 changes: 20 additions & 23 deletions packages/dataviews/src/dataviews.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { ComponentType } from 'react';
* WordPress dependencies
*/
import { __experimentalHStack as HStack } from '@wordpress/components';
import { useMemo, useState, useCallback } from '@wordpress/element';
import { useMemo, useState } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -22,6 +22,7 @@ import BulkActions from './bulk-actions';
import { normalizeFields } from './normalize-fields';
import BulkActionsToolbar from './bulk-actions-toolbar';
import type { Action, Field, View, ViewBaseProps } from './types';
import type { SetSelection, SelectionOrUpdater } from './private-types';

type ItemWithId = { id: string };

Expand All @@ -40,7 +41,7 @@ type DataViewsProps< Item > = {
};
supportedLayouts: string[];
selection?: string[];
setSelection?: ( selection: string[] ) => void;
setSelection?: SetSelection;
onSelectionChange?: ( items: Item[] ) => void;
} & ( Item extends ItemWithId
? { getItemId?: ( item: Item ) => string }
Expand Down Expand Up @@ -83,26 +84,22 @@ export default function DataViews< Item >( {
onSelectionChange = defaultOnSelectionChange,
}: DataViewsProps< Item > ) {
const [ selectionState, setSelectionState ] = useState< string[] >( [] );
let selection, setSelection;
if (
selectionProperty !== undefined &&
setSelectionProperty !== undefined
) {
selection = selectionProperty;
setSelection = setSelectionProperty;
} else {
selection = selectionState;
setSelection = setSelectionState;
}
const isUncontrolled =
selectionProperty === undefined || setSelectionProperty === undefined;
const selection = isUncontrolled ? selectionState : selectionProperty;
const setSelection = isUncontrolled
? setSelectionState
: setSelectionProperty;
const [ openedFilter, setOpenedFilter ] = useState< string | null >( null );

const onSetSelection = useCallback(
( items: Item[] ) => {
setSelection( items.map( ( item ) => getItemId( item ) ) );
onSelectionChange( items );
},
[ setSelection, getItemId, onSelectionChange ]
);
function setSelectionWithChange( value: SelectionOrUpdater ) {
const newValue =
typeof value === 'function' ? value( selection ) : value;
onSelectionChange(
data.filter( ( item ) => newValue.includes( getItemId( item ) ) )
);
return setSelection( value );
}

const ViewComponent = VIEW_LAYOUTS.find( ( v ) => v.type === view.type )
?.component as ComponentType< ViewBaseProps< Item > >;
Expand Down Expand Up @@ -149,7 +146,7 @@ export default function DataViews< Item >( {
<BulkActions
actions={ actions }
data={ data }
onSelectionChange={ onSetSelection }
onSelectionChange={ setSelectionWithChange }
selection={ _selection }
getItemId={ getItemId }
/>
Expand All @@ -168,7 +165,7 @@ export default function DataViews< Item >( {
getItemId={ getItemId }
isLoading={ isLoading }
onChangeView={ onChangeView }
onSelectionChange={ onSetSelection }
onSelectionChange={ setSelectionWithChange }
selection={ _selection }
setOpenedFilter={ setOpenedFilter }
view={ view }
Expand All @@ -184,7 +181,7 @@ export default function DataViews< Item >( {
data={ data }
actions={ actions }
selection={ _selection }
onSelectionChange={ onSetSelection }
onSelectionChange={ setSelectionWithChange }
getItemId={ getItemId }
/>
) }
Expand Down
2 changes: 2 additions & 0 deletions packages/dataviews/src/private-types.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type SelectionOrUpdater = string[] | ( ( prev: string[] ) => string[] );
export type SetSelection = ( selection: SelectionOrUpdater ) => void;
37 changes: 11 additions & 26 deletions packages/dataviews/src/single-selection-checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import { CheckboxControl } from '@wordpress/components';
* Internal dependencies
*/
import type { Field } from './types';
import type { SetSelection } from './private-types';

interface SingleSelectionCheckboxProps< Item > {
selection: string[];
onSelectionChange: ( selection: Item[] ) => void;
onSelectionChange: SetSelection;
item: Item;
data: Item[];
getItemId: ( item: Item ) => string;
primaryField?: Field< Item >;
disabled: boolean;
Expand All @@ -23,23 +23,22 @@ export default function SingleSelectionCheckbox< Item >( {
selection,
onSelectionChange,
item,
data,
getItemId,
primaryField,
disabled,
}: SingleSelectionCheckboxProps< Item > ) {
const id = getItemId( item );
const isSelected = ! disabled && selection.includes( id );
const checked = ! disabled && selection.includes( id );
let selectionLabel;
if ( primaryField?.getValue && item ) {
// eslint-disable-next-line @wordpress/valid-sprintf
selectionLabel = sprintf(
/* translators: %s: item title. */
isSelected ? __( 'Deselect item: %s' ) : __( 'Select item: %s' ),
checked ? __( 'Deselect item: %s' ) : __( 'Select item: %s' ),
primaryField.getValue( { item } )
);
} else {
selectionLabel = isSelected
selectionLabel = checked
? __( 'Select a new item' )
: __( 'Deselect item' );
}
Expand All @@ -49,31 +48,17 @@ export default function SingleSelectionCheckbox< Item >( {
__nextHasNoMarginBottom
aria-label={ selectionLabel }
aria-disabled={ disabled }
checked={ isSelected }
checked={ checked }
onChange={ () => {
if ( disabled ) {
return;
}

if ( ! isSelected ) {
onSelectionChange(
data.filter( ( _item ) => {
const itemId = getItemId?.( _item );
return (
itemId === id || selection.includes( itemId )
);
} )
);
} else {
onSelectionChange(
data.filter( ( _item ) => {
const itemId = getItemId?.( _item );
return (
itemId !== id && selection.includes( itemId )
);
} )
);
}
onSelectionChange(
selection.includes( id )
? selection.filter( ( itemId ) => id !== itemId )
: [ ...selection, id ]
);
} }
/>
);
Expand Down
7 changes: 6 additions & 1 deletion packages/dataviews/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
*/
import type { ReactElement, ReactNode } from 'react';

/**
* Internal dependencies
*/
import type { SetSelection } from './private-types';

export type SortDirection = 'asc' | 'desc';

/**
Expand Down Expand Up @@ -383,7 +388,7 @@ export interface ViewBaseProps< Item > {
getItemId: ( item: Item ) => string;
isLoading?: boolean;
onChangeView( view: View ): void;
onSelectionChange: ( items: Item[] ) => void;
onSelectionChange: SetSelection;
selection: string[];
setOpenedFilter: ( fieldId: string ) => void;
view: View;
Expand Down
33 changes: 7 additions & 26 deletions packages/dataviews/src/view-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import ItemActions from './item-actions';
import SingleSelectionCheckbox from './single-selection-checkbox';
import { useHasAPossibleBulkAction } from './bulk-actions';
import type { Action, NormalizedField, ViewGridProps } from './types';
import type { SetSelection } from './private-types';

interface GridItemProps< Item > {
selection: string[];
data: Item[];
onSelectionChange: ( items: Item[] ) => void;
onSelectionChange: SetSelection;
getItemId: ( item: Item ) => string;
item: Item;
actions: Action< Item >[];
Expand All @@ -40,7 +40,6 @@ interface GridItemProps< Item > {

function GridItem< Item >( {
selection,
data,
ellatrix marked this conversation as resolved.
Show resolved Hide resolved
onSelectionChange,
getItemId,
item,
Expand Down Expand Up @@ -68,27 +67,11 @@ function GridItem< Item >( {
if ( ! hasBulkAction ) {
return;
}
if ( ! isSelected ) {
onSelectionChange(
data.filter( ( _item ) => {
const itemId = getItemId?.( _item );
return (
itemId === id ||
selection.includes( itemId )
);
} )
);
} else {
onSelectionChange(
data.filter( ( _item ) => {
const itemId = getItemId?.( _item );
return (
itemId !== id &&
selection.includes( itemId )
);
} )
);
}
onSelectionChange(
selection.includes( id )
? selection.filter( ( itemId ) => id !== itemId )
: [ ...selection, id ]
);
}
} }
>
Expand All @@ -104,7 +87,6 @@ function GridItem< Item >( {
selection={ selection }
onSelectionChange={ onSelectionChange }
getItemId={ getItemId }
data={ data }
primaryField={ primaryField }
disabled={ ! hasBulkAction }
/>
Expand Down Expand Up @@ -239,7 +221,6 @@ export default function ViewGrid< Item >( {
<GridItem
key={ getItemId( item ) }
selection={ selection }
data={ data }
onSelectionChange={ onSelectionChange }
getItemId={ getItemId }
item={ item }
Expand Down
6 changes: 2 additions & 4 deletions packages/dataviews/src/view-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -333,10 +333,8 @@ export default function ViewList< Item >( props: ViewListProps< Item > ) {
)
);

const onSelect = useCallback(
( item: Item ) => onSelectionChange( [ item ] ),
[ onSelectionChange ]
);
const onSelect = ( item: Item ) =>
onSelectionChange( [ getItemId( item ) ] );
Copy link
Member Author

Choose a reason for hiding this comment

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

Note that useCallback is not needed here, it's just used in a React event callback down the line.


const getItemDomId = useCallback(
( item?: Item ) =>
Expand Down
Loading
Loading