Skip to content

Commit

Permalink
Add: Bulk actions API and trash action.
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgefilipecosta committed Nov 23, 2023
1 parent aa91684 commit 12925b1
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 7 deletions.
66 changes: 66 additions & 0 deletions packages/edit-site/src/components/bulk-actions/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* WordPress dependencies
*/
import { trash } from '@wordpress/icons';
import { useMemo } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { store as noticesStore } from '@wordpress/notices';
import { useDispatch } from '@wordpress/data';
import { store as coreStore } from '@wordpress/core-data';

export function useBulkTrashPostAction() {
const { createSuccessNotice, createErrorNotice } =
useDispatch( noticesStore );
const { deleteEntityRecord } = useDispatch( coreStore );
return useMemo(
() => ( {
id: 'move-to-trash',
label: __( 'Move to Trash' ),
isPrimary: true,
icon: trash,
isEligible( data, selection ) {
console.log( {
p: data.filter( ( post ) => selection.includes( post.id ) ),
} );
return ! data
.filter( ( post ) => selection.includes( post.id ) )
.some( ( post ) => post.status === 'trash' );
},
async callback( data, selection ) {
const postsToDelete = data.filter( ( post ) =>
selection.includes( post.id )
);
try {
await Promise.all(
postsToDelete.map( async ( post ) => {
deleteEntityRecord(
'postType',
post.type,
post.id,
{},
{ throwOnError: true }
);
} )
);
createSuccessNotice(
__( 'The selected posts were moved to the trash.' ),
{
type: 'snackbar',
id: 'edit-site-page-trashed',
}
);
} catch ( error ) {
const errorMessage =
error.message && error.code !== 'unknown_error'
? error.message
: __(
'An error occurred while moving the posts to the trash.'
);

createErrorNotice( errorMessage, { type: 'snackbar' } );
}
},
} ),
[ createErrorNotice, createSuccessNotice, deleteEntityRecord ]
);
}
94 changes: 94 additions & 0 deletions packages/edit-site/src/components/dataviews/bulk-actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* WordPress dependencies
*/
import {
ToolbarButton,
Toolbar,
ToolbarGroup,
ToolbarItem,
Popover,
} from '@wordpress/components';
import { useMemo } from '@wordpress/element';
import { __, _n, sprintf } from '@wordpress/i18n';

function PrimaryActionTrigger( { action, onClick } ) {
return (
<ToolbarButton
label={ action.label }
icon={ action.icon }
isDestructive={ action.isDestructive }
size="compact"
onClick={ onClick }
/>
);
}

const EMPTY_ARRAY = [];

export default function BulkActions( {
data,
selection,
bulkActions = EMPTY_ARRAY,
setSelection,
} ) {
const primaryActions = useMemo(
() =>
bulkActions.filter( ( action ) => {
return action.isPrimary && action.isEligible( data, selection );
} ),
[ bulkActions, data, selection ]
);
if (
( selection && selection.length === 0 ) ||
primaryActions.length === 0
) {
return null;
}
return (
<Popover
placement="top-middle"
className="dataviews-bulk-actions-popover"
>
<Toolbar label="Bulk actions">
<div className="dataviews-bulk-actions-toolbar-wrapper">
<ToolbarGroup>
<ToolbarButton onClick={ () => {} } disabled={ true }>
{
// translators: %s: Total number of selected items.
sprintf(
// translators: %s: Total number of selected items.
_n(
'%s item selected',
'%s items selected',
selection.length
),
selection.length
)
}
</ToolbarButton>
<ToolbarButton
onClick={ () => {
setSelection( EMPTY_ARRAY );
} }
>
{ __( 'Deselect' ) }
</ToolbarButton>
</ToolbarGroup>
<ToolbarGroup>
{ primaryActions.map( ( action ) => {
return (
<PrimaryActionTrigger
key={ action.id }
action={ action }
onClick={ () =>
action.callback( data, selection )
}
/>
);
} ) }
</ToolbarGroup>
</div>
</Toolbar>
</Popover>
);
}
26 changes: 21 additions & 5 deletions packages/edit-site/src/components/dataviews/dataviews.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import {
__experimentalVStack as VStack,
__experimentalHStack as HStack,
Popover,
} from '@wordpress/components';
import { useMemo } from '@wordpress/element';

Expand All @@ -17,6 +18,7 @@ import Filters from './filters';
import Search from './search';
import { ViewGrid } from './view-grid';
import { ViewSideBySide } from './view-side-by-side';
import BulkActions from './bulk-actions';

// To do: convert to view type registry.
export const viewTypeSupportsMap = {
Expand Down Expand Up @@ -45,6 +47,9 @@ export default function DataViews( {
isLoading = false,
paginationInfo,
supportedLayouts,
selection,
setSelection,
bulkActions,
} ) {
const ViewComponent = viewTypeMap[ view.type ];
const _fields = useMemo( () => {
Expand Down Expand Up @@ -89,12 +94,23 @@ export default function DataViews( {
data={ data }
getItemId={ getItemId }
isLoading={ isLoading }
selection={ selection }
setSelection={ setSelection }
/>
<Pagination
view={ view }
onChangeView={ onChangeView }
paginationInfo={ paginationInfo }
/>

<div>
<Pagination
view={ view }
onChangeView={ onChangeView }
paginationInfo={ paginationInfo }
/>
<BulkActions
data={ data }
bulkActions={ bulkActions }
selection={ selection }
setSelection={ setSelection }
/>
</div>
</VStack>
</div>
);
Expand Down
11 changes: 11 additions & 0 deletions packages/edit-site/src/components/dataviews/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,14 @@
.dataviews-action-modal {
z-index: z-index(".dataviews-action-modal");
}

.dataviews-bulk-actions-popover .components-popover__content {
min-width: max-content;
}

.dataviews-bulk-actions-toolbar-wrapper {
display: flex;
flex-grow: 1;
width: 100%;
}

71 changes: 69 additions & 2 deletions packages/edit-site/src/components/dataviews/view-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
Button,
Icon,
privateApis as componentsPrivateApis,
CheckboxControl,
} from '@wordpress/components';
import { useMemo, Children, Fragment } from '@wordpress/element';

Expand Down Expand Up @@ -252,9 +253,12 @@ function ViewList( {
getItemId,
isLoading = false,
paginationInfo,
selection,
setSelection,
} ) {
const areAllSelected = selection && selection.length === data.length;
const columns = useMemo( () => {
const _columns = fields.map( ( field ) => {
const fieldsColumns = fields.map( ( field ) => {
const { render, getValue, ...column } = field;
column.cell = ( props ) =>
render( { item: props.row.original, view } );
Expand All @@ -263,6 +267,61 @@ function ViewList( {
}
return column;
} );
const _columns =
selection !== undefined
? [
{
header: (
<CheckboxControl
__nextHasNoMarginBottom
checked={ areAllSelected }
onChange={ () => {
if ( areAllSelected ) {
setSelection( [] );
} else {
setSelection(
data.map( ( { id } ) => id )
);
}
} }
/>
),
id: 'selection',
cell: ( props ) => {
//console.log({ props });
const item = props.row.original;
const isSelected = selection.includes(
item.id
);
//console.log({ item, isSelected });
return (
<CheckboxControl
__nextHasNoMarginBottom
checked={ isSelected }
onChange={ () => {
if ( ! isSelected ) {
const newSelection = [
...selection,
item.id,
];
setSelection( newSelection );
} else {
setSelection(
selection.filter(
( id ) => id !== item.id
)
);
}
} }
/>
);
},
enableHiding: false,
width: 40,
},
...fieldsColumns,
]
: fieldsColumns;
if ( actions?.length ) {
_columns.push( {
header: __( 'Actions' ),
Expand All @@ -280,7 +339,15 @@ function ViewList( {
}

return _columns;
}, [ fields, actions, view ] );
}, [
areAllSelected,
fields,
actions,
view,
selection,
setSelection,
data,
] );

const columnVisibility = useMemo( () => {
if ( ! view.hiddenFields?.length ) {
Expand Down
26 changes: 26 additions & 0 deletions packages/edit-site/src/components/page-pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
viewPostAction,
useEditPostAction,
} from '../actions';
import { useBulkTrashPostAction } from '../bulk-actions';
import SideEditor from './side-editor';
import Media from '../media';
import { unlock } from '../../lock-unlock';
Expand Down Expand Up @@ -157,6 +158,20 @@ export default function PagePages() {
totalPages,
} = useEntityRecords( 'postType', postType, queryArgs );

useEffect( () => {
if (
selection.some(
( id ) => ! pages?.some( ( page ) => page.id === id )
)
) {
setSelection(
selection.filter( ( id ) =>
pages?.some( ( page ) => page.id === id )
)
);
}
}, [ pages, selection ] );

const { records: authors, isResolving: isLoadingAuthors } =
useEntityRecords( 'root', 'user' );

Expand Down Expand Up @@ -271,6 +286,9 @@ export default function PagePages() {
const permanentlyDeletePostAction = usePermanentlyDeletePostAction();
const restorePostAction = useRestorePostAction();
const editPostAction = useEditPostAction();

const bulkTrashPostAction = useBulkTrashPostAction();

const actions = useMemo(
() => [
viewPostAction,
Expand All @@ -287,6 +305,11 @@ export default function PagePages() {
editPostAction,
]
);

const bulkActions = useMemo(
() => [ bulkTrashPostAction ],
[ bulkTrashPostAction ]
);
const onChangeView = useCallback(
( viewUpdater ) => {
let updatedView =
Expand Down Expand Up @@ -315,11 +338,14 @@ export default function PagePages() {
paginationInfo={ paginationInfo }
fields={ fields }
actions={ actions }
bulkActions={ bulkActions }
data={ pages || EMPTY_ARRAY }
getItemId={ ( item ) => item.id }
isLoading={ isLoadingPages || isLoadingAuthors }
view={ view }
onChangeView={ onChangeView }
selection={ selection }
setSelection={ setSelection }
/>
</Page>
{ viewTypeSupportsMap[ view.type ].preview && (
Expand Down

0 comments on commit 12925b1

Please sign in to comment.