refactor(command-menu-item): rename Actions to CommandMenuItem (#18489)

actions are being renamed to command menu item, they will be migrated to
server and will be served as headless front components

---------

Co-authored-by: Raphaël Bosi <71827178+bosiraphael@users.noreply.github.com>
This commit is contained in:
nitin
2026-03-09 19:33:12 +05:30
committed by GitHub
parent 06bdb5ad6a
commit 36bcc71f3d
275 changed files with 4544 additions and 4436 deletions
@@ -0,0 +1,47 @@
import { CommandMenuItemComponent } from '@/command-menu-item/display/components/CommandMenuItemComponent';
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
import { styled } from '@linaria/react';
import { motion } from 'framer-motion';
import { useContext } from 'react';
import { ThemeContext } from 'twenty-ui/theme-constants';
const StyledActionContainer = styled(motion.div)`
align-items: center;
display: flex;
justify-content: center;
`;
export const PageHeaderCommandMenuButtons = () => {
const { theme } = useContext(ThemeContext);
const { commandMenuItems } = useContext(CommandMenuContext);
const pinnedActions = commandMenuItems
.filter((entry) => entry.isPinned)
.toReversed();
const actionsWithPositionForAnimation = pinnedActions.map(
(action, index) => ({
action,
position: pinnedActions.length - index - 1,
}),
);
return (
<>
{actionsWithPositionForAnimation.map(({ action, position }) => (
<StyledActionContainer
key={position}
layout
initial={{ width: 0, opacity: 0 }}
animate={{ width: 'unset', opacity: 1 }}
exit={{ width: 0, opacity: 0 }}
transition={{
duration: theme.animation.duration.instant,
ease: 'easeInOut',
}}
>
<CommandMenuItemComponent action={action} />
</StyledActionContainer>
))}
</>
);
};
@@ -0,0 +1,39 @@
import { PageHeaderCommandMenuButtons } from '@/command-menu-item/components/PageHeaderCommandMenuButtons';
import { RecordIndexCommandMenuDropdown } from '@/command-menu-item/components/RecordIndexCommandMenuDropdown';
import { CommandMenuContextProvider } from '@/command-menu-item/contexts/CommandMenuContextProvider';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useIsMobile } from 'twenty-ui/utilities';
export const RecordIndexCommandMenu = () => {
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
contextStoreCurrentObjectMetadataItemIdComponentState,
MAIN_CONTEXT_STORE_INSTANCE_ID,
);
const isMobile = useIsMobile();
return (
<>
{contextStoreCurrentObjectMetadataItemId && (
<>
<CommandMenuContextProvider
isInSidePanel={false}
displayType="button"
containerType="index-page-header"
>
{!isMobile && <PageHeaderCommandMenuButtons />}
</CommandMenuContextProvider>
<CommandMenuContextProvider
isInSidePanel={false}
displayType="dropdownItem"
containerType="index-page-dropdown"
>
<RecordIndexCommandMenuDropdown />
</CommandMenuContextProvider>
</>
)}
</>
);
};
@@ -0,0 +1,118 @@
import { CommandMenuItemComponent } from '@/command-menu-item/display/components/CommandMenuItemComponent';
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
import { COMMAND_MENU_DROPDOWN_CLICK_OUTSIDE_ID } from '@/command-menu-item/constants/CommandMenuDropdownClickOutsideId';
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
import { recordIndexCommandMenuDropdownPositionComponentState } from '@/command-menu-item/states/recordIndexCommandMenuDropdownPositionComponentState';
import { getCommandMenuDropdownIdFromCommandMenuId } from '@/command-menu-item/utils/getCommandMenuDropdownIdFromCommandMenuId';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { SelectableList } from '@/ui/layout/selectable-list/components/SelectableList';
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
import { selectedItemIdComponentState } from '@/ui/layout/selectable-list/states/selectedItemIdComponentState';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { useContext } from 'react';
import { IconLayoutSidebarRightExpand } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
const StyledDropdownMenuContainer = styled.div`
align-items: center;
display: flex;
flex-direction: column;
height: 100%;
justify-content: center;
width: 100%;
`;
export const RecordIndexCommandMenuDropdown = () => {
const { t } = useLingui();
const { commandMenuItems } = useContext(CommandMenuContext);
const recordIndexActions = commandMenuItems.filter(
(action) =>
action.type === CommandMenuItemType.Standard &&
action.scope === CommandMenuItemScope.RecordSelection,
);
const commandMenuId = useAvailableComponentInstanceIdOrThrow(
CommandMenuComponentInstanceContext,
);
const dropdownId = getCommandMenuDropdownIdFromCommandMenuId(commandMenuId);
const { closeDropdown } = useCloseDropdown();
const recordIndexCommandMenuDropdownPosition = useAtomComponentStateValue(
recordIndexCommandMenuDropdownPositionComponentState,
dropdownId,
);
const { openSidePanelMenu } = useSidePanelMenu();
const selectedItemIdArray = [
...recordIndexActions.map((action) => action.key),
'more-actions',
];
const selectedItemId = useAtomComponentStateValue(
selectedItemIdComponentState,
dropdownId,
);
return (
<Dropdown
dropdownId={dropdownId}
data-select-disable
dropdownPlacement="bottom-start"
dropdownStrategy="absolute"
dropdownOffset={{
x: recordIndexCommandMenuDropdownPosition.x ?? 0,
y: recordIndexCommandMenuDropdownPosition.y ?? 0,
}}
dropdownComponents={
<DropdownContent>
<StyledDropdownMenuContainer
data-click-outside-id={COMMAND_MENU_DROPDOWN_CLICK_OUTSIDE_ID}
>
<DropdownMenuItemsContainer>
<SelectableList
focusId={dropdownId}
selectableItemIdArray={selectedItemIdArray}
selectableListInstanceId={dropdownId}
>
{recordIndexActions.map((action) => (
<CommandMenuItemComponent action={action} key={action.key} />
))}
<SelectableListItem
itemId="more-actions"
key="more-actions"
onEnter={() => {
closeDropdown(dropdownId);
openSidePanelMenu();
}}
>
<MenuItem
LeftIcon={IconLayoutSidebarRightExpand}
onClick={() => {
closeDropdown(dropdownId);
openSidePanelMenu();
}}
focused={selectedItemId === 'more-actions'}
text={t`More actions`}
/>
</SelectableListItem>
</SelectableList>
</DropdownMenuItemsContainer>
</StyledDropdownMenuContainer>
</DropdownContent>
}
/>
);
};
@@ -0,0 +1,24 @@
import { RecordPageSidePanelCommandMenuDropdown } from '@/command-menu-item/components/RecordPageSidePanelCommandMenuDropdown';
import { CommandMenuContextProvider } from '@/command-menu-item/contexts/CommandMenuContextProvider';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
export const RecordPageSidePanelCommandMenu = () => {
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
contextStoreCurrentObjectMetadataItemIdComponentState,
);
return (
<>
{contextStoreCurrentObjectMetadataItemId && (
<CommandMenuContextProvider
isInSidePanel={true}
displayType="dropdownItem"
containerType="command-menu-show-page-dropdown"
>
<RecordPageSidePanelCommandMenuDropdown />
</CommandMenuContextProvider>
)}
</>
);
};
@@ -0,0 +1,39 @@
import { CommandMenuItemComponent } from '@/command-menu-item/display/components/CommandMenuItemComponent';
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
import { getSidePanelCommandMenuDropdownIdFromCommandMenuId } from '@/command-menu-item/utils/getSidePanelCommandMenuDropdownIdFromCommandMenuId';
import { OptionsDropdownMenu } from '@/ui/layout/dropdown/components/OptionsDropdownMenu';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useContext } from 'react';
export const RecordPageSidePanelCommandMenuDropdown = () => {
const { commandMenuItems } = useContext(CommandMenuContext);
const commandMenuId = useAvailableComponentInstanceIdOrThrow(
CommandMenuComponentInstanceContext,
);
const dropdownId =
getSidePanelCommandMenuDropdownIdFromCommandMenuId(commandMenuId);
const recordSelectionActions = commandMenuItems.filter(
(action) => action.scope === CommandMenuItemScope.RecordSelection,
);
const selectableItemIdArray = recordSelectionActions.map(
(action) => action.key,
);
return (
<OptionsDropdownMenu
dropdownId={dropdownId}
selectableListId={commandMenuId}
selectableItemIdArray={selectableItemIdArray}
>
{recordSelectionActions.map((action) => (
<CommandMenuItemComponent action={action} key={action.key} />
))}
</OptionsDropdownMenu>
);
};
@@ -0,0 +1,39 @@
import { PageHeaderCommandMenuButtons } from '@/command-menu-item/components/PageHeaderCommandMenuButtons';
import { CommandMenuContextProvider } from '@/command-menu-item/contexts/CommandMenuContextProvider';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { contextStoreCurrentObjectMetadataItemIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataItemIdComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useIsMobile } from 'twenty-ui/utilities';
export const RecordShowCommandMenu = () => {
const contextStoreCurrentObjectMetadataItemId = useAtomComponentStateValue(
contextStoreCurrentObjectMetadataItemIdComponentState,
MAIN_CONTEXT_STORE_INSTANCE_ID,
);
const contextStoreTargetedRecordsRule = useAtomComponentStateValue(
contextStoreTargetedRecordsRuleComponentState,
MAIN_CONTEXT_STORE_INSTANCE_ID,
);
const hasSelectedRecord =
contextStoreTargetedRecordsRule.mode === 'selection' &&
contextStoreTargetedRecordsRule.selectedRecordIds.length === 1;
const isMobile = useIsMobile();
return (
<>
{hasSelectedRecord && contextStoreCurrentObjectMetadataItemId && (
<CommandMenuContextProvider
isInSidePanel={false}
displayType="button"
containerType="show-page-header"
>
{!isMobile && <PageHeaderCommandMenuButtons />}
</CommandMenuContextProvider>
)}
</>
);
};
@@ -0,0 +1,151 @@
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
import { getSidePanelCommandMenuDropdownIdFromCommandMenuId } from '@/command-menu-item/utils/getSidePanelCommandMenuDropdownIdFromCommandMenuId';
import { SIDE_PANEL_FOCUS_ID } from '@/side-panel/constants/SidePanelFocusId';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { sidePanelNavigationStackState } from '@/side-panel/states/sidePanelNavigationStackState';
import { SidePanelPageComponentInstanceContext } from '@/side-panel/states/contexts/SidePanelPageComponentInstanceContext';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { contextStoreRecordShowParentViewComponentState } from '@/context-store/states/contextStoreRecordShowParentViewComponentState';
import { CoreObjectNameSingular, AppPath } from 'twenty-shared/types';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { type ObjectRecord } from '@/object-record/types/ObjectRecord';
import { useCloseDropdown } from '@/ui/layout/dropdown/hooks/useCloseDropdown';
import { getShowPageTabListComponentId } from '@/ui/layout/show-page/utils/getShowPageTabListComponentId';
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
import { useAtomFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilyStateValue';
import { useSetAtomComponentState } from '@/ui/utilities/state/jotai/hooks/useSetAtomComponentState';
import { t } from '@lingui/core/macro';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { IconBrowserMaximize } from 'twenty-ui/display';
import { Button } from 'twenty-ui/input';
import { getOsControlSymbol } from 'twenty-ui/utilities';
import { useNavigateApp } from '~/hooks/useNavigateApp';
type RecordShowSidePanelOpenRecordButtonProps = {
objectNameSingular: string;
recordId: string;
};
export const RecordShowSidePanelOpenRecordButton = ({
objectNameSingular,
recordId,
}: RecordShowSidePanelOpenRecordButtonProps) => {
const record = useAtomFamilyStateValue(recordStoreFamilyState, recordId) as
| ObjectRecord
| null
| undefined;
const { closeSidePanelMenu } = useSidePanelMenu();
const sidePanelPageComponentInstance = useComponentInstanceStateContext(
SidePanelPageComponentInstanceContext,
);
const tabListComponentId = getShowPageTabListComponentId({
pageId: sidePanelPageComponentInstance?.instanceId,
targetObjectId: recordId,
});
const activeTabId = useAtomComponentStateValue(
activeTabIdComponentState,
tabListComponentId,
);
const tabListComponentIdInRecordPage = getShowPageTabListComponentId({
targetObjectId: recordId,
});
const setActiveTabId = useSetAtomComponentState(
activeTabIdComponentState,
tabListComponentIdInRecordPage,
);
const parentViewState = useAtomComponentStateCallbackState(
contextStoreRecordShowParentViewComponentState,
MAIN_CONTEXT_STORE_INSTANCE_ID,
);
const store = useStore();
const navigate = useNavigateApp();
const commandMenuId = useAvailableComponentInstanceIdOrThrow(
CommandMenuComponentInstanceContext,
);
const { closeDropdown } = useCloseDropdown();
const handleOpenRecord = useCallback(() => {
const tabIdToOpen =
activeTabId === 'home'
? objectNameSingular === CoreObjectNameSingular.Note ||
objectNameSingular === CoreObjectNameSingular.Task
? 'richText'
: 'timeline'
: activeTabId;
setActiveTabId(tabIdToOpen);
const parentView = store.get(parentViewState);
if (
isDefined(parentView) &&
parentView.parentViewObjectNameSingular !== objectNameSingular
) {
store.set(parentViewState, undefined);
}
store.set(sidePanelNavigationStackState.atom, []);
navigate(AppPath.RecordShowPage, {
objectNameSingular,
objectRecordId: recordId,
});
closeDropdown(
getSidePanelCommandMenuDropdownIdFromCommandMenuId(commandMenuId),
);
closeSidePanelMenu();
}, [
commandMenuId,
activeTabId,
closeSidePanelMenu,
closeDropdown,
navigate,
objectNameSingular,
parentViewState,
recordId,
setActiveTabId,
store,
]);
useHotkeysOnFocusedElement({
keys: ['ctrl+Enter,meta+Enter'],
callback: handleOpenRecord,
focusId: SIDE_PANEL_FOCUS_ID,
dependencies: [handleOpenRecord],
});
if (!isDefined(record)) {
return null;
}
return (
<Button
title={t`Open`}
variant="primary"
accent="blue"
size="small"
Icon={IconBrowserMaximize}
hotkeys={[getOsControlSymbol(), '⏎']}
onClick={handleOpenRecord}
/>
);
};
@@ -0,0 +1,102 @@
import { type Meta, type StoryObj } from '@storybook/react-vite';
import { Provider as JotaiProvider } from 'jotai';
import * as test from 'storybook/test';
import { expect, userEvent, waitFor, within } from 'storybook/test';
import { RecordIndexCommandMenuDropdown } from '@/command-menu-item/components/RecordIndexCommandMenuDropdown';
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
import { recordIndexCommandMenuDropdownPositionComponentState } from '@/command-menu-item/states/recordIndexCommandMenuDropdownPositionComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { RouterDecorator } from 'twenty-ui/testing';
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
const deleteMock = test.fn();
const addToFavoritesMock = test.fn();
const exportMock = test.fn();
const meta: Meta<typeof RecordIndexCommandMenuDropdown> = {
title: 'Modules/CommandMenu/RecordIndexCommandMenuDropdown',
component: RecordIndexCommandMenuDropdown,
decorators: [
(Story) => {
jotaiStore.set(
isDropdownOpenComponentState.atomFamily({
instanceId: 'command-menu-dropdown-story-command-menu',
}),
true,
);
jotaiStore.set(
recordIndexCommandMenuDropdownPositionComponentState.atomFamily({
instanceId: 'command-menu-dropdown-story',
}),
{ x: 10, y: 10 },
);
return (
<JotaiProvider store={jotaiStore}>
<CommandMenuComponentInstanceContext.Provider
value={{ instanceId: 'story-command-menu' }}
>
<CommandMenuContext.Provider
value={{
isInSidePanel: true,
displayType: 'dropdownItem',
containerType: 'index-page-dropdown',
commandMenuItems: createMockCommandMenuItems({
deleteMock,
addToFavoritesMock,
exportMock,
}),
}}
>
<Story />
</CommandMenuContext.Provider>
</CommandMenuComponentInstanceContext.Provider>
</JotaiProvider>
);
},
ContextStoreDecorator,
RouterDecorator,
],
};
export default meta;
type Story = StoryObj<typeof RecordIndexCommandMenuDropdown>;
export const Default: Story = {
args: {
commandMenuId: 'story',
},
};
export const WithInteractions: Story = {
args: {
commandMenuId: 'story',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement.ownerDocument.body);
const deleteButton = await canvas.findByText('Delete');
await userEvent.click(deleteButton);
const addToFavoritesButton = await canvas.findByText('Add to favorites');
await userEvent.click(addToFavoritesButton);
const exportButton = await canvas.findByText('Export');
await userEvent.click(exportButton);
const moreActionsButton = await canvas.findByText('More actions');
await waitFor(() => {
expect(deleteMock).toHaveBeenCalled();
expect(addToFavoritesMock).toHaveBeenCalled();
expect(exportMock).toHaveBeenCalled();
expect(moreActionsButton).toBeInTheDocument();
});
},
};
@@ -0,0 +1,112 @@
import { type Meta, type StoryObj } from '@storybook/react-vite';
import { Provider as JotaiProvider } from 'jotai';
import * as test from 'storybook/test';
import { expect, userEvent, waitFor, within } from 'storybook/test';
import { RecordPageSidePanelCommandMenuDropdown } from '@/command-menu-item/components/RecordPageSidePanelCommandMenuDropdown';
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
import { CommandMenuComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuComponentInstanceContext';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
import { jotaiStore } from '@/ui/utilities/state/jotai/jotaiStore';
import { ContextStoreDecorator } from '~/testing/decorators/ContextStoreDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { JestContextStoreSetter } from '~/testing/jest/JestContextStoreSetter';
const deleteMock = test.fn();
const addToFavoritesMock = test.fn();
const exportMock = test.fn();
const meta: Meta<typeof RecordPageSidePanelCommandMenuDropdown> = {
title: 'Modules/CommandMenu/RecordPageSidePanelCommandMenuDropdown',
component: RecordPageSidePanelCommandMenuDropdown,
decorators: [
(Story) => (
<JotaiProvider store={jotaiStore}>
<ContextStoreComponentInstanceContext.Provider
value={{ instanceId: MAIN_CONTEXT_STORE_INSTANCE_ID }}
>
<JestContextStoreSetter
contextStoreTargetedRecordsRule={{
mode: 'selection',
selectedRecordIds: ['1'],
}}
contextStoreNumberOfSelectedRecords={1}
>
<CommandMenuComponentInstanceContext.Provider
value={{ instanceId: 'story-command-menu' }}
>
<CommandMenuContext.Provider
value={{
isInSidePanel: true,
displayType: 'dropdownItem',
containerType: 'command-menu-show-page-dropdown',
commandMenuItems: createMockCommandMenuItems({
deleteMock,
addToFavoritesMock,
exportMock,
}),
}}
>
<Story />
</CommandMenuContext.Provider>
</CommandMenuComponentInstanceContext.Provider>
</JestContextStoreSetter>
</ContextStoreComponentInstanceContext.Provider>
</JotaiProvider>
),
ComponentDecorator,
ContextStoreDecorator,
ObjectMetadataItemsDecorator,
SnackBarDecorator,
RouterDecorator,
],
args: {
commandMenuId: 'story-command-menu',
},
};
export default meta;
type Story = StoryObj<typeof RecordPageSidePanelCommandMenuDropdown>;
export const Default: Story = {
args: {
commandMenuId: 'story-command-menu',
},
};
export const WithButtonClicks: Story = {
args: {
commandMenuId: 'story-command-menu',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement.ownerDocument.body);
let actionButton = await canvas.findByText('Options');
await userEvent.click(actionButton);
const deleteButton = await canvas.findByText('Delete');
await userEvent.click(deleteButton);
actionButton = await canvas.findByText('Options');
await userEvent.click(actionButton);
const addToFavoritesButton = await canvas.findByText('Add to favorites');
await userEvent.click(addToFavoritesButton);
actionButton = await canvas.findByText('Options');
await userEvent.click(actionButton);
const exportButton = await canvas.findByText('Export');
await userEvent.click(exportButton);
await waitFor(() => {
expect(deleteMock).toHaveBeenCalled();
expect(addToFavoritesMock).toHaveBeenCalled();
expect(exportMock).toHaveBeenCalled();
});
},
};