File Manager > Extending Functionality
Customize File Bulk Actions
Learn how to add, replace, or remove bulk actions in the File Manager.
This feature is available since Webiny v5.38.0.
- how to add a custom bulk action to the File Manager
- how to discover existing bulk action names
- how to change the position, remove, or replace an existing bulk action
Overview
In File Manager, pre-built actions empower users to delete, or move multiple files effortlessly. These actions become visible when you select one or more files from the list.
To work with bulk actions, you need to use the FileManagerViewConfig
component. For this article, we will use the BulkAction
in the Browser
namespace.
import React from "react";import { Admin } from "@webiny/app-serverless-cms";import { Cognito } from "@webiny/app-admin-users-cognito";import { FileManagerViewConfig } from "@webiny/app-file-manager";import "./App.scss";
// You can destructure child components to make the code more readable and easier to work with.const { Browser } = FileManagerViewConfig;
export const App = () => { return ( <Admin> <Cognito /> <FileManagerViewConfig> <Browser.BulkAction name={"demo-bulk-action”} element={<span>Demo Bulk Action</span>} /> </FileManagerViewConfig> </Admin> );};
Browser
is the crucial component of the Admin app’s File Manager. This is where users can easily browse through files, apply filters, perform searches, upload new files and organize them into folders.
If users select one or more files from the list, the bulk action bar will appear.
Add a Bulk Action
To add a new bulk action, use the FileManagerViewConfig
component and mount it within your Admin app. This component will serve as the foundation for your bulk actions.
To ensure UI consistency, use the useButtons
hook to access four button components:
ButtonDefault
ButtonPrimary
ButtonSecondary
IconButton
Choose the one that best suits your design needs. In the following examples, we will use the IconButton
component.
Simple Bulk Action
Here is an example of creating a bulk action that copies data from the selected files to the clipboard in JSON format.
import React from "react";import { Admin } from "@webiny/app-serverless-cms";import { Cognito } from "@webiny/app-admin-users-cognito";import { useSnackbar } from "@webiny/app-admin";import { FileManagerViewConfig } from "@webiny/app-file-manager";import { ReactComponent as CopyIcon } from "@material-design-icons/svg/outlined/content_copy.svg";import "./App.scss";
// You can destructure child components to make the code more readable and easier to work with.const { BulkAction } = FileManagerViewConfig.Browser;const { useWorker, useButtons } = BulkAction;
const ActionCopyJson = () => { // useButtons() exposes the button components also used internally: use these to keep the UI consistent. const { IconButton } = useButtons(); // useWorker() exposes the currently selected items within the context. const { items } = useWorker(); // showSnackbar allows to provide a feedback to users. const { showSnackbar } = useSnackbar();
const copyJson = () => { navigator.clipboard.writeText(JSON.stringify(items, null, 2)); showSnackbar("JSON data copied to clipboard."); };
return ( <IconButton icon={<CopyIcon />} onAction={copyJson} label={`Copy as JSON`} tooltipPlacement={"bottom"} /> );};
export const App = () => { return ( <Admin> <Cognito /> <FileManagerViewConfig> <BulkAction name={"copy-json"} element={<ActionCopyJson />} /> </FileManagerViewConfig> </Admin> );};
The items
property obtained from useWorker()
is a crucial part of managing bulk actions: these are the currently selected items within the context of the File Manager.
This is the whole process of registering a new bulk action element.
Asynchronous Bulk Action
Sometimes, you may need to execute a bulk action that involves multiple asynchronous tasks, such as sending selected files to an external service. In such cases, we can use the useWorker
and useDialogs
hooks to create a seamless user experience:
useWorker
provides the currently selected items and theprocessInSeries
method to perform our callback.useDialog
provides methods to show confirmation and result dialogs.
Here’s an example of creating a bulk action that sends the selected files to an external service using fetch
and shows the result of each iteration.
import React from "react";import { Admin } from "@webiny/app-serverless-cms";import { Cognito } from "@webiny/app-admin-users-cognito";import { FileManagerViewConfig } from "@webiny/app-file-manager";import { ReactComponent as SendIcon } from "@material-design-icons/svg/outlined/send.svg";import "./App.scss";
// You can destructure child components to make the code more readable and easier to work with.const { BulkAction } = FileManagerViewConfig.Browser;const { useWorker, useButtons, useDialog } = BulkAction;
const ActionSendToExternal = () => { // useButtons() exposes the button components also used internally: use these to keep the UI consistent. const { IconButton } = useButtons(); // useWorker provides the selected items, method to process them and the result of the operation. const worker = useWorker(); // useDialog provides methods to show confirmation and result dialogs. const { showConfirmationDialog, showResultsDialog } = useDialog();
const openSendToExternalServiceDialog = () => showConfirmationDialog({ title: "Send to External Service", message: `You are about to send the selected files. Are you sure you want to continue?`, loadingLabel: `Processing`, execute: async () => { await worker.processInSeries(async ({ item, report }) => { try { const response = await fetch( "https://any.url.com", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(item.id) } );
// Add the result from the operation, marking it as a success report.success({ title: item.name, message: `File successfully sent, response status: ${response.status}` }); } catch (e) { // Add the result from the operation, marking it as an error report.error({ title: item.name, message: e.message }); } }, 5 // Batch size, default value: 10);
// Reset the selected items worker.resetItems();
// Show a dialog with the report from all operations showResultsDialog({ results: worker.results, title: "Send to External Service", message: "Operation completed, here below you find the complete report:" }); } });
return ( <IconButton icon={<SendIcon />} onAction={openSendToExternalServiceDialog} label={`Send to external service`} tooltipPlacement={"bottom"} /> );};
export const App = () => { return ( <Admin> <Cognito /> <FileManagerViewConfig> <BulkAction name={"send-to-external"} element={<ActionSendToExternal />} /> </FileManagerViewConfig> </Admin> );};
For the IconButton
component, the onAction
prop can accept any method. In the current scenario, we are using the showConfirmationDialog
method to open a dialog.
showConfirmationDialog
is designed to receive a callback function, which will be executed when the users confirm their action. You can pass any callback function to this method or use the processInSeries
method from the useWorker
hook.
processInSeries
requires a callback as its first parameter. This callback is provided with the current item of the list being iterated and report
: use this information to collect the result of the operation and indicate whether it was successful or not. The second parameter is the chunk
size, which determines how many items are processed in each batch. You can adjust this value to control the size of each batch.
showResultsDialog
is used after the completion of the sending process to present the user with a summary of the actions performed, including information about successful items sent and any errors encountered during the process. This allows the user to review a detailed report of the entire operation.
Discover Bulk Actions
This section demonstrates how you can discover the names of existing bulk actions. This is important for further sections on positioning, removing, and replacing bulk actions.
The easiest way to discover existing bulk actions is to use your browser’s React Dev Tools plugins, select at least one file and look for the Buttons
element inside BulkActions
. From there, you can either look for actions
prop or look at the child elements and their keys:
Position a Bulk Action
To position your custom bulk action before or after an existing action, you can use the before
and after
props on the <Browser.BulkAction>
element:
<FileManagerViewConfig>
<Browser.BulkAction name={"copy-json"} element={<ActionCopyJson />} before={"delete"} />
</FileManagerViewConfig>
Remove a Bulk Action
Sometimes you might want to remove an existing bulk action. All you need to do is reference the action by name and pass a remove
prop to the <Browser.BulkAction>
element:
<FileManagerViewConfig>
<Browser.BulkAction name={"delete"} remove />
</FileManagerViewConfig>
Replace a Bulk Action
To replace an existing bulk action with a new action element, you need to reference an existing action by name and pass a new component via the element
prop:
<FileManagerViewConfig>
<Browser.BulkAction name={"delete"} element={<span>A new action!</span>} />
</FileManagerViewConfig>