Skip to content

Commit

Permalink
DataViews: Type the ItemActions component
Browse files Browse the repository at this point in the history
  • Loading branch information
youknowriad committed May 14, 2024
1 parent f5b456a commit a2b2784
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* External dependencies
*/
import type { MouseEventHandler } from 'react';

/**
* WordPress dependencies
*/
Expand All @@ -15,6 +20,7 @@ import { moreVertical } from '@wordpress/icons';
* Internal dependencies
*/
import { unlock } from './lock-unlock';
import type { Action, ActionModal as ActionModalType, Item } from './types';

const {
DropdownMenuV2: DropdownMenu,
Expand All @@ -24,7 +30,48 @@ const {
kebabCase,
} = unlock( componentsPrivateApis );

function ButtonTrigger( { action, onClick } ) {
interface ButtonTriggerProps {
action: Action;
onClick: MouseEventHandler;
}

interface DropdownMenuItemTriggerProps {
action: Action;
onClick: MouseEventHandler;
}

interface ActionModalProps {
action: ActionModalType;
items: Item[];
closeModal?: () => void;
onActionStart?: ( items: Item[] ) => void;
onActionPerformed?: ( items: Item[] ) => void;
}

interface ActionWithModalProps extends ActionModalProps {
ActionTrigger: (
props: ButtonTriggerProps | DropdownMenuItemTriggerProps
) => JSX.Element;
isBusy?: boolean;
}

interface ActionsDropdownMenuGroupProps {
actions: Action[];
item: Item;
}

interface ItemActionsProps {
item: Item;
actions: Action[];
isCompact: boolean;
}

interface CompactItemActionsProps {
item: Item;
actions: Action[];
}

function ButtonTrigger( { action, onClick }: ButtonTriggerProps ) {
return (
<Button
label={ action.label }
Expand All @@ -36,11 +83,14 @@ function ButtonTrigger( { action, onClick } ) {
);
}

function DropdownMenuItemTrigger( { action, onClick } ) {
function DropdownMenuItemTrigger( {
action,
onClick,
}: DropdownMenuItemTriggerProps ) {
return (
<DropdownMenuItem
onClick={ onClick }
hideOnClick={ ! action.RenderModal }
hideOnClick={ 'RenderModal' in action }
>
<DropdownMenuItemLabel>{ action.label }</DropdownMenuItemLabel>
</DropdownMenuItem>
Expand All @@ -53,12 +103,12 @@ export function ActionModal( {
closeModal,
onActionStart,
onActionPerformed,
} ) {
}: ActionModalProps ) {
return (
<Modal
title={ action.modalHeader || action.label }
__experimentalHideHeader={ !! action.hideModalHeader }
onRequestClose={ closeModal }
onRequestClose={ closeModal ?? ( () => {} ) }
overlayClassName={ `dataviews-action-modal dataviews-action-modal__${ kebabCase(
action.id
) }` }
Expand All @@ -80,7 +130,7 @@ export function ActionWithModal( {
onActionStart,
onActionPerformed,
isBusy,
} ) {
}: ActionWithModalProps ) {
const [ isModalOpen, setIsModalOpen ] = useState( false );
const actionTriggerProps = {
action,
Expand All @@ -106,11 +156,14 @@ export function ActionWithModal( {
);
}

export function ActionsDropdownMenuGroup( { actions, item } ) {
export function ActionsDropdownMenuGroup( {
actions,
item,
}: ActionsDropdownMenuGroupProps ) {
return (
<DropdownMenuGroup>
{ actions.map( ( action ) => {
if ( !! action.RenderModal ) {
if ( 'RenderModal' in action ) {
return (
<ActionWithModal
key={ action.id }
Expand All @@ -132,7 +185,11 @@ export function ActionsDropdownMenuGroup( { actions, item } ) {
);
}

export default function ItemActions( { item, actions, isCompact } ) {
export default function ItemActions( {
item,
actions,
isCompact,
}: ItemActionsProps ) {
const { primaryActions, eligibleActions } = useMemo( () => {
// If an action is eligible for all items, doesn't need
// to provide the `isEligible` function.
Expand Down Expand Up @@ -162,7 +219,7 @@ export default function ItemActions( { item, actions, isCompact } ) {
>
{ !! primaryActions.length &&
primaryActions.map( ( action ) => {
if ( !! action.RenderModal ) {
if ( 'RenderModal' in action ) {
return (
<ActionWithModal
key={ action.id }
Expand All @@ -185,7 +242,7 @@ export default function ItemActions( { item, actions, isCompact } ) {
);
}

function CompactItemActions( { item, actions } ) {
function CompactItemActions( { item, actions }: CompactItemActionsProps ) {
return (
<DropdownMenu
trigger={
Expand Down
78 changes: 78 additions & 0 deletions packages/dataviews/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/**
* WordPress dependencies
*/
import type { IconType } from '@wordpress/components';

/**
* External dependencies
*/
Expand Down Expand Up @@ -164,3 +169,76 @@ export interface ViewList extends ViewBase {
}

export type View = ViewList | ViewBase;

interface ActionBase {
/**
* The unique identifier of the action.
*/
id: string;

/**
* The label of the action.
*/
label: string;

/**
* The icon of the action. (Either a string or an SVG element)
*/
icon?: IconType;

/**
* Whether the action is disabled.
*/
disabled?: boolean;

/**
* Whether the action is destructive.
*/
isDestructive?: boolean;

/**
* Whether the action is a primary action.
*/
isPrimary?: boolean;

/**
* Whether the item passed as an argument supports the current action.
*/
isEligible?: ( item: Item ) => boolean;
}

export interface ActionModal extends ActionBase {
/**
* Modal to render when the action is triggered.
*/
RenderModal: ( {
items,
closeModal,
onActionStart,
onActionPerformed,
}: {
items: Item[];
closeModal?: () => void;
onActionStart?: ( items: Item[] ) => void;
onActionPerformed?: ( items: Item[] ) => void;
} ) => JSX.Element;

/**
* Whether to hide the modal header.
*/
hideModalHeader?: boolean;

/**
* The header of the modal.
*/
modalHeader?: string;
}

export interface ActionButton extends ActionBase {
/**
* The callback to execute when the action is triggered.
*/
callback: ( items: Item[] ) => void;
}

export type Action = ActionModal | ActionButton;

0 comments on commit a2b2784

Please sign in to comment.