Skip to content

Commit

Permalink
Add: Bulk actions to dataviews with the new design.
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgefilipecosta committed Dec 20, 2023
1 parent 1ef449f commit 35605cc
Show file tree
Hide file tree
Showing 9 changed files with 475 additions and 41 deletions.
182 changes: 182 additions & 0 deletions packages/dataviews/src/bulk-actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/**
* WordPress dependencies
*/
import {
privateApis as componentsPrivateApis,
Button,
Modal,
} from '@wordpress/components';
import { __, sprintf, _n } from '@wordpress/i18n';
import { useMemo, useState } from '@wordpress/element';

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

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

function ActionWithModal( { action, selectedItems, setActionWithModal } ) {
const eligibleItems = useMemo( () => {
return selectedItems.filter( ( item ) => action.isEligible( item ) );
}, [ action, selectedItems ] );
const { RenderModal, hideModalHeader } = action;
return (
<Modal
title={ ! hideModalHeader && action.label }
__experimentalHideHeader={ !! hideModalHeader }
onRequestClose={ () => {
setActionWithModal( undefined );
} }
overlayClassName="dataviews-action-modal"
>
<RenderModal
items={ eligibleItems }
closeModal={ () => setActionWithModal( undefined ) }
/>
</Modal>
);
}

function BulkActionItem( {
action,
selectedItems,
onMenuOpenChange,
setActionWithModal,
} ) {
const eligibleItems = useMemo( () => {
return selectedItems.filter( ( item ) => action.isEligible( item ) );
}, [ action, selectedItems ] );
return (
<DropdownMenuItem
key={ action.id }
disabled={ eligibleItems.length === 0 }
onSelect={ async ( event ) => {
event.preventDefault();
if ( !! action.RenderModal ) {
onMenuOpenChange( false );
setActionWithModal( action );
} else {
await action.callback( eligibleItems );
}
} }
suffix={
eligibleItems.length > 0 ? eligibleItems.length : undefined
}
>
{ action.label }
</DropdownMenuItem>
);
}

function ActionsMenuGroup( {
actions,
selectedItems,
onMenuOpenChange,
setActionWithModal,
} ) {
const bulkActions = actions.filter( ( action ) => action.supportsBulk );
if ( bulkActions.length === 0 ) {
return null;
}
return (
<>
<DropdownMenuGroup>
{ bulkActions.map( ( action ) => (
<BulkActionItem
key={ action.id }
action={ action }
selectedItems={ selectedItems }
onMenuOpenChange={ onMenuOpenChange }
setActionWithModal={ setActionWithModal }
/>
) ) }
</DropdownMenuGroup>
<DropdownMenuSeparator />
</>
);
}

export default function BulkActions( {
data,
actions,
selection,
onSelectionChange,
getItemId,
} ) {
const areAllSelected = selection && selection.length === data.length;
const [ isMenuOpen, onMenuOpenChange ] = useState( false );
const [ actionWithModal, setActionWithModal ] = useState();
const selectedItems = useMemo( () => {
return data.filter( ( item ) =>
selection.includes( getItemId( item ) )
);
}, [ selection, data, getItemId ] );
return (
<>
<DropdownMenu
open={ isMenuOpen }
onOpenChange={ onMenuOpenChange }
label={ __( 'Filters' ) }
trigger={
<Button>
{ selection.length
? sprintf(
/* translators: %d: Number of items. */
_n(
'Edit %d item',
'Edit %d items',
selection.length
),
selection.length
)
: __( 'Bulk edit' ) }
</Button>
}
>
<ActionsMenuGroup
actions={ actions }
data={ data }
selection={ selection }
getItemId={ getItemId }
onMenuOpenChange={ onMenuOpenChange }
setActionWithModal={ setActionWithModal }
selectedItems={ selectedItems }
/>
<DropdownMenuGroup>
<DropdownMenuItem
disabled={ areAllSelected }
onSelect={ ( event ) => {
event.preventDefault();
onSelectionChange( data );
} }
suffix={ data.length }
>
{ __( 'Select all' ) }
</DropdownMenuItem>
<DropdownMenuItem
disabled={ selection.length === 0 }
onSelect={ ( event ) => {
event.preventDefault();
onSelectionChange( [] );
} }
>
{ __( 'Deselect' ) }
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenu>
{ actionWithModal && (
<ActionWithModal
action={ actionWithModal }
selectedItems={ selectedItems }
setActionWithModal={ setActionWithModal }
/>
) }
</>
);
}
14 changes: 13 additions & 1 deletion packages/dataviews/src/dataviews.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import Pagination from './pagination';
import ViewActions from './view-actions';
import Filters from './filters';
import Search from './search';
import { VIEW_LAYOUTS } from './constants';
import { VIEW_LAYOUTS, LAYOUT_TABLE } from './constants';
import BulkActions from './bulk-actions';

export default function DataViews( {
view,
Expand All @@ -30,6 +31,7 @@ export default function DataViews( {
supportedLayouts,
onSelectionChange,
deferredRendering,
labels,
} ) {
const [ selection, setSelection ] = useState( [] );

Expand All @@ -55,6 +57,15 @@ export default function DataViews( {
className="dataviews__filters-view-actions"
>
<HStack justify="start" wrap>
{ view.type === LAYOUT_TABLE && (
<BulkActions
actions={ actions }
data={ data }
onSelectionChange={ onSetSelection }
selection={ selection }
getItemId={ getItemId }
/>
) }
{ search && (
<Search
label={ searchLabel }
Expand Down Expand Up @@ -87,6 +98,7 @@ export default function DataViews( {
onSelectionChange={ onSetSelection }
selection={ selection }
deferredRendering={ deferredRendering }
labels={ labels }
/>
<Pagination
view={ view }
Expand Down
6 changes: 3 additions & 3 deletions packages/dataviews/src/item-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ function ActionWithModal( { action, item, ActionTrigger } ) {
overlayClassName="dataviews-action-modal"
>
<RenderModal
item={ item }
items={ [ item ] }
closeModal={ () => setIsModalOpen( false ) }
/>
</Modal>
Expand All @@ -93,7 +93,7 @@ function ActionsDropdownMenuGroup( { actions, item } ) {
<DropdownMenuItemTrigger
key={ action.id }
action={ action }
onClick={ () => action.callback( item ) }
onClick={ () => action.callback( [ item ] ) }
/>
);
} ) }
Expand Down Expand Up @@ -154,7 +154,7 @@ export default function ItemActions( { item, actions, isCompact } ) {
<ButtonTrigger
key={ action.id }
action={ action }
onClick={ () => action.callback( item ) }
onClick={ () => action.callback( [ item ] ) }
/>
);
} ) }
Expand Down
12 changes: 12 additions & 0 deletions packages/dataviews/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,15 @@
.dataviews-loading {
padding: 0 $grid-unit-40;
}

.dataviews-table-selection-checkbox label {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
Loading

0 comments on commit 35605cc

Please sign in to comment.