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:
@@ -0,0 +1,32 @@
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { useCloseCommandMenu } from '@/command-menu-item/hooks/useCloseCommandMenu';
|
||||
import { useContext } from 'react';
|
||||
|
||||
export const Command = ({
|
||||
onClick,
|
||||
closeSidePanelOnShowPageOptionsExecution = false,
|
||||
closeSidePanelOnCommandMenuListExecution = true,
|
||||
}: {
|
||||
onClick: () => void;
|
||||
closeSidePanelOnShowPageOptionsExecution?: boolean;
|
||||
closeSidePanelOnCommandMenuListExecution?: boolean;
|
||||
}) => {
|
||||
const commandMenuItemConfig = useContext(CommandConfigContext);
|
||||
|
||||
const { closeCommandMenu } = useCloseCommandMenu({
|
||||
closeSidePanelOnShowPageOptionsExecution,
|
||||
closeSidePanelOnCommandMenuListExecution,
|
||||
});
|
||||
|
||||
if (!commandMenuItemConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
closeCommandMenu();
|
||||
onClick();
|
||||
};
|
||||
|
||||
return <CommandMenuItemDisplay onClick={handleClick} />;
|
||||
};
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
import { type CommandMenuItemDisplayProps } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState';
|
||||
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
|
||||
import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { MenuItem } from 'twenty-ui/navigation';
|
||||
|
||||
export const CommandDropdownItem = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
}: {
|
||||
action: CommandMenuItemDisplayProps;
|
||||
onClick?: () => void;
|
||||
to?: string;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleClick = () => {
|
||||
onClick?.();
|
||||
if (isDefined(to)) {
|
||||
navigate(to);
|
||||
}
|
||||
};
|
||||
|
||||
const selectableListInstanceId = useAvailableComponentInstanceIdOrThrow(
|
||||
SelectableListComponentInstanceContext,
|
||||
);
|
||||
|
||||
const isSelectedItemId = useAtomComponentFamilyStateValue(
|
||||
isSelectedItemIdComponentFamilyState,
|
||||
action.key,
|
||||
selectableListInstanceId,
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectableListItem itemId={action.key} onEnter={handleClick}>
|
||||
<MenuItem
|
||||
focused={isSelectedItemId}
|
||||
key={action.key}
|
||||
LeftIcon={action.Icon}
|
||||
onClick={handleClick}
|
||||
text={getCommandMenuItemLabel(action.label)}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
};
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { useCloseCommandMenu } from '@/command-menu-item/hooks/useCloseCommandMenu';
|
||||
import { type PathParam } from 'react-router-dom';
|
||||
import { type AppPath } from 'twenty-shared/types';
|
||||
import { getAppPath } from 'twenty-shared/utils';
|
||||
|
||||
export const CommandLink = <T extends AppPath>({
|
||||
to,
|
||||
params,
|
||||
queryParams,
|
||||
}: {
|
||||
to: T;
|
||||
params?: { [key in PathParam<T>]: string | null };
|
||||
queryParams?: Record<string, any>;
|
||||
}) => {
|
||||
const { closeCommandMenu } = useCloseCommandMenu();
|
||||
|
||||
const path = getAppPath(to, params, queryParams);
|
||||
|
||||
return <CommandMenuItemDisplay onClick={closeCommandMenu} to={path} />;
|
||||
};
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
import { type CommandMenuItemDisplayProps } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { CommandMenuItem } from '@/command-menu/components/CommandMenuItem';
|
||||
import { SelectableListItem } from '@/ui/layout/selectable-list/components/SelectableListItem';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Loader } from 'twenty-ui/feedback';
|
||||
|
||||
export const CommandListItem = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
disabled = false,
|
||||
}: {
|
||||
action: CommandMenuItemDisplayProps;
|
||||
onClick?: () => void;
|
||||
to?: string;
|
||||
disabled?: boolean;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleClick = () => {
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
onClick?.();
|
||||
if (isDefined(to)) {
|
||||
navigate(to);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<SelectableListItem itemId={action.key} onEnter={handleClick}>
|
||||
<CommandMenuItem
|
||||
id={action.key}
|
||||
Icon={action.Icon}
|
||||
label={getCommandMenuItemLabel(action.label)}
|
||||
description={getCommandMenuItemLabel(action.description ?? '')}
|
||||
to={to}
|
||||
onClick={disabled ? undefined : onClick}
|
||||
hotKeys={action.hotKeys}
|
||||
disabled={disabled}
|
||||
RightComponent={disabled ? <Loader /> : undefined}
|
||||
/>
|
||||
</SelectableListItem>
|
||||
);
|
||||
};
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { CommandMenuButton } from '@/command-menu/components/CommandMenuButton';
|
||||
import { type CommandMenuItemDisplayProps } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
|
||||
export const CommandMenuItemButton = ({
|
||||
action,
|
||||
onClick,
|
||||
to,
|
||||
}: {
|
||||
action: CommandMenuItemDisplayProps;
|
||||
onClick?: (event?: React.MouseEvent<HTMLElement>) => void;
|
||||
to?: string;
|
||||
}) => {
|
||||
return <CommandMenuButton command={action} to={to} onClick={onClick} />;
|
||||
};
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
|
||||
export const CommandMenuItemComponent = ({
|
||||
action,
|
||||
}: {
|
||||
action: CommandMenuItemConfig;
|
||||
}) => {
|
||||
return (
|
||||
<CommandConfigContext.Provider value={action}>
|
||||
{action.component}
|
||||
</CommandConfigContext.Provider>
|
||||
);
|
||||
};
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
import { CommandMenuItemButton } from '@/command-menu-item/display/components/CommandMenuItemButton';
|
||||
import { CommandDropdownItem } from '@/command-menu-item/display/components/CommandDropdownItem';
|
||||
import { CommandListItem } from '@/command-menu-item/display/components/CommandListItem';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { type MessageDescriptor } from '@lingui/core';
|
||||
import { useContext } from 'react';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
import { type MenuItemAccent } from 'twenty-ui/navigation';
|
||||
|
||||
export type CommandMenuItemDisplayProps = {
|
||||
key: string;
|
||||
label: MessageDescriptor | string;
|
||||
shortLabel?: MessageDescriptor | string;
|
||||
description?: MessageDescriptor | string;
|
||||
Icon: IconComponent;
|
||||
isPrimaryCTA?: boolean;
|
||||
accent?: MenuItemAccent;
|
||||
hotKeys?: string[];
|
||||
};
|
||||
|
||||
export const CommandMenuItemDisplay = ({
|
||||
onClick,
|
||||
to,
|
||||
disabled,
|
||||
}: {
|
||||
onClick?: (event?: React.MouseEvent<HTMLElement>) => void;
|
||||
to?: string;
|
||||
disabled?: boolean;
|
||||
}) => {
|
||||
const action = useContext(CommandConfigContext);
|
||||
const { displayType } = useContext(CommandMenuContext);
|
||||
|
||||
if (!action) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (displayType === 'button') {
|
||||
return <CommandMenuItemButton action={action} onClick={onClick} to={to} />;
|
||||
}
|
||||
|
||||
if (displayType === 'listItem') {
|
||||
return (
|
||||
<CommandListItem
|
||||
action={action}
|
||||
onClick={onClick}
|
||||
to={to}
|
||||
disabled={disabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (displayType === 'dropdownItem') {
|
||||
return <CommandDropdownItem action={action} onClick={onClick} to={to} />;
|
||||
}
|
||||
|
||||
return assertUnreachable(displayType, 'Unsupported display type');
|
||||
};
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { useNavigateSidePanel } from '@/side-panel/hooks/useNavigateSidePanel';
|
||||
import { sidePanelSearchState } from '@/side-panel/states/sidePanelSearchState';
|
||||
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
|
||||
import { type MessageDescriptor } from '@lingui/core';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useContext } from 'react';
|
||||
import { type SidePanelPages } from 'twenty-shared/types';
|
||||
import { type IconComponent } from 'twenty-ui/display';
|
||||
|
||||
export const CommandMenuItemOpenSidePanelPage = ({
|
||||
page,
|
||||
pageTitle,
|
||||
pageIcon,
|
||||
onClick,
|
||||
shouldResetSearchState = false,
|
||||
}: {
|
||||
page: SidePanelPages;
|
||||
pageTitle: MessageDescriptor;
|
||||
pageIcon: IconComponent;
|
||||
onClick?: () => void;
|
||||
shouldResetSearchState?: boolean;
|
||||
}) => {
|
||||
const actionConfig = useContext(CommandConfigContext);
|
||||
|
||||
const { navigateSidePanel } = useNavigateSidePanel();
|
||||
|
||||
const setSidePanelSearch = useSetAtomState(sidePanelSearchState);
|
||||
|
||||
if (!actionConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
onClick?.();
|
||||
|
||||
navigateSidePanel({
|
||||
page,
|
||||
pageTitle: t(pageTitle),
|
||||
pageIcon,
|
||||
});
|
||||
|
||||
if (shouldResetSearchState) {
|
||||
setSidePanelSearch('');
|
||||
}
|
||||
};
|
||||
|
||||
return <CommandMenuItemDisplay onClick={handleClick} />;
|
||||
};
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import { type ReactNode, useContext } from 'react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { useCloseCommandMenu } from '@/command-menu-item/hooks/useCloseCommandMenu';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||
import { isModalOpenedComponentState } from '@/ui/layout/modal/states/isModalOpenedComponentState';
|
||||
import { useAtomComponentStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateValue';
|
||||
import { type ButtonAccent } from 'twenty-ui/input';
|
||||
|
||||
export type CommandModalProps = {
|
||||
title: string;
|
||||
subtitle: ReactNode;
|
||||
onConfirmClick: () => void | Promise<void>;
|
||||
confirmButtonText?: string;
|
||||
confirmButtonAccent?: ButtonAccent;
|
||||
isLoading?: boolean;
|
||||
closeSidePanelOnShowPageOptionsExecution?: boolean;
|
||||
closeSidePanelOnCommandMenuListExecution?: boolean;
|
||||
};
|
||||
|
||||
export const CommandModal = ({
|
||||
title,
|
||||
subtitle,
|
||||
onConfirmClick,
|
||||
confirmButtonText = t`Confirm`,
|
||||
confirmButtonAccent = 'danger',
|
||||
isLoading = false,
|
||||
closeSidePanelOnShowPageOptionsExecution,
|
||||
closeSidePanelOnCommandMenuListExecution,
|
||||
}: CommandModalProps) => {
|
||||
const { openModal } = useModal();
|
||||
|
||||
const { closeCommandMenu } = useCloseCommandMenu({
|
||||
closeSidePanelOnShowPageOptionsExecution,
|
||||
closeSidePanelOnCommandMenuListExecution,
|
||||
});
|
||||
|
||||
const handleConfirmClick = async () => {
|
||||
await onConfirmClick();
|
||||
closeCommandMenu();
|
||||
};
|
||||
|
||||
const commandMenuItemConfig = useContext(CommandConfigContext);
|
||||
const { containerType } = useContext(CommandMenuContext);
|
||||
|
||||
const modalId = `${commandMenuItemConfig?.key}-command-menu-item-modal-${containerType}`;
|
||||
|
||||
const isModalOpened = useAtomComponentStateValue(
|
||||
isModalOpenedComponentState,
|
||||
modalId,
|
||||
);
|
||||
|
||||
if (!commandMenuItemConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => openModal(modalId);
|
||||
|
||||
return (
|
||||
<>
|
||||
<CommandMenuItemDisplay onClick={handleClick} />
|
||||
{isModalOpened && (
|
||||
<ConfirmationModal
|
||||
modalInstanceId={modalId}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
onConfirmClick={handleConfirmClick}
|
||||
confirmButtonText={confirmButtonText}
|
||||
confirmButtonAccent={confirmButtonAccent}
|
||||
loading={isLoading}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { useCloseCommandMenu } from '@/command-menu-item/hooks/useCloseCommandMenu';
|
||||
import { isHeadlessFrontComponentMountedFamilySelector } from '@/front-components/selectors/isHeadlessFrontComponentMountedFamilySelector';
|
||||
import { useAtomFamilySelectorValue } from '@/ui/utilities/state/jotai/hooks/useAtomFamilySelectorValue';
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { CommandMenuItemDisplay } from './CommandMenuItemDisplay';
|
||||
|
||||
export const HeadlessFrontComponentCommandMenuItem = ({
|
||||
frontComponentId,
|
||||
onClick,
|
||||
}: {
|
||||
frontComponentId: string;
|
||||
onClick: () => void;
|
||||
}) => {
|
||||
const commandMenuItemConfig = useContext(CommandConfigContext);
|
||||
|
||||
const { closeCommandMenu } = useCloseCommandMenu({
|
||||
closeSidePanelOnShowPageOptionsExecution: false,
|
||||
closeSidePanelOnCommandMenuListExecution: false,
|
||||
});
|
||||
|
||||
const isMounted = useAtomFamilySelectorValue(
|
||||
isHeadlessFrontComponentMountedFamilySelector,
|
||||
frontComponentId,
|
||||
);
|
||||
|
||||
if (!commandMenuItemConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClick = () => {
|
||||
if (isMounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
closeCommandMenu();
|
||||
onClick();
|
||||
};
|
||||
|
||||
return <CommandMenuItemDisplay onClick={handleClick} disabled={isMounted} />;
|
||||
};
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
import { CommandMenuItemButton } from '@/command-menu-item/display/components/CommandMenuItemButton';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, fn, userEvent, within } from 'storybook/test';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
|
||||
const meta: Meta<typeof CommandMenuItemButton> = {
|
||||
title: 'Modules/CommandMenuItem/Display/CommandMenuItemButton',
|
||||
component: CommandMenuItemButton,
|
||||
decorators: [ComponentDecorator, RouterDecorator],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof CommandMenuItemButton>;
|
||||
|
||||
const deleteMock = fn();
|
||||
const addToFavoritesMock = fn();
|
||||
|
||||
const mockActions = createMockCommandMenuItems({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
});
|
||||
|
||||
const addToFavoritesCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
const goToPeopleCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
);
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
action: addToFavoritesCommandMenuItem,
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getCommandMenuItemLabel(
|
||||
addToFavoritesCommandMenuItem?.shortLabel ?? '',
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
export const WithLink: Story = {
|
||||
args: {
|
||||
action: goToPeopleCommandMenuItem,
|
||||
to: '/objects/people',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const menuItem = await canvas.findByText(
|
||||
getCommandMenuItemLabel(goToPeopleCommandMenuItem?.shortLabel ?? ''),
|
||||
);
|
||||
expect(menuItem).toBeVisible();
|
||||
expect(canvas.getByRole('link')).toHaveAttribute('href', '/objects/people');
|
||||
},
|
||||
};
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, within } from 'storybook/test';
|
||||
|
||||
import { CommandMenuItemComponent } from '@/command-menu-item/display/components/CommandMenuItemComponent';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
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 { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { ComponentDecorator } from 'twenty-ui/testing';
|
||||
|
||||
const mockActions = createMockCommandMenuItems({});
|
||||
|
||||
const addToFavoritesCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
if (!addToFavoritesCommandMenuItem) {
|
||||
throw new Error('Add to favorites action not found');
|
||||
}
|
||||
|
||||
const meta: Meta<typeof CommandMenuItemComponent> = {
|
||||
title: 'Modules/CommandMenuItem/Display/CommandMenuItemComponent',
|
||||
component: CommandMenuItemComponent,
|
||||
decorators: [
|
||||
ComponentDecorator,
|
||||
(Story) => (
|
||||
<CommandMenuComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story' }}
|
||||
>
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: false,
|
||||
containerType: 'index-page-header',
|
||||
displayType: 'button',
|
||||
commandMenuItems: [addToFavoritesCommandMenuItem],
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</CommandMenuContext.Provider>
|
||||
</CommandMenuComponentInstanceContext.Provider>
|
||||
),
|
||||
],
|
||||
args: {
|
||||
action: addToFavoritesCommandMenuItem,
|
||||
},
|
||||
parameters: {
|
||||
container: {
|
||||
width: 'auto',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CommandMenuItemComponent>;
|
||||
|
||||
export const Default: Story = {
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
expect(
|
||||
await canvas.findByText(
|
||||
getCommandMenuItemLabel(
|
||||
addToFavoritesCommandMenuItem?.shortLabel ?? '',
|
||||
),
|
||||
),
|
||||
).toBeVisible();
|
||||
},
|
||||
};
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
import { CommandMenuItemDisplay } from '@/command-menu-item/display/components/CommandMenuItemDisplay';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
|
||||
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, fn, userEvent, within } from 'storybook/test';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
|
||||
type Story = StoryObj<typeof CommandMenuItemDisplay>;
|
||||
|
||||
const deleteMock = fn();
|
||||
const addToFavoritesMock = fn();
|
||||
|
||||
const mockActions = createMockCommandMenuItems({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
});
|
||||
|
||||
const addToFavoritesCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
if (!addToFavoritesCommandMenuItem) {
|
||||
throw new Error('addToFavoritesCommandMenuItem not found');
|
||||
}
|
||||
|
||||
const meta: Meta<typeof CommandMenuItemDisplay> = {
|
||||
title: 'Modules/CommandMenuItem/Display/CommandMenuItemDisplay',
|
||||
component: CommandMenuItemDisplay,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<CommandConfigContext.Provider value={addToFavoritesCommandMenuItem}>
|
||||
<Story />
|
||||
</CommandConfigContext.Provider>
|
||||
),
|
||||
ComponentDecorator,
|
||||
RouterDecorator,
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const AsButton: Story = {
|
||||
args: {
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: false,
|
||||
containerType: 'command-menu-list',
|
||||
displayType: 'button',
|
||||
commandMenuItems: [],
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</CommandMenuContext.Provider>
|
||||
),
|
||||
],
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getCommandMenuItemLabel(
|
||||
addToFavoritesCommandMenuItem?.shortLabel ?? '',
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
export const AsListItem: Story = {
|
||||
args: {
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<SelectableListComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story' }}
|
||||
>
|
||||
<Story />
|
||||
</SelectableListComponentInstanceContext.Provider>
|
||||
),
|
||||
(Story) => (
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: false,
|
||||
containerType: 'command-menu-list',
|
||||
displayType: 'listItem',
|
||||
commandMenuItems: [],
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</CommandMenuContext.Provider>
|
||||
),
|
||||
],
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getCommandMenuItemLabel(addToFavoritesCommandMenuItem?.label ?? ''),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
export const AsDropdownItem: Story = {
|
||||
args: {
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<SelectableListComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story' }}
|
||||
>
|
||||
<Story />
|
||||
</SelectableListComponentInstanceContext.Provider>
|
||||
),
|
||||
(Story) => (
|
||||
<CommandMenuContext.Provider
|
||||
value={{
|
||||
isInSidePanel: false,
|
||||
containerType: 'command-menu-list',
|
||||
displayType: 'dropdownItem',
|
||||
commandMenuItems: [],
|
||||
}}
|
||||
>
|
||||
<Story />
|
||||
</CommandMenuContext.Provider>
|
||||
),
|
||||
],
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getCommandMenuItemLabel(addToFavoritesCommandMenuItem?.label ?? ''),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import { CommandDropdownItem } from '@/command-menu-item/display/components/CommandDropdownItem';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, fn, userEvent, within } from 'storybook/test';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
|
||||
const meta: Meta<typeof CommandDropdownItem> = {
|
||||
title: 'Modules/CommandMenuItem/Display/CommandDropdownItem',
|
||||
component: CommandDropdownItem,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<SelectableListComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story' }}
|
||||
>
|
||||
<Story />
|
||||
</SelectableListComponentInstanceContext.Provider>
|
||||
),
|
||||
ComponentDecorator,
|
||||
RouterDecorator,
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof CommandDropdownItem>;
|
||||
|
||||
const deleteMock = fn();
|
||||
const addToFavoritesMock = fn();
|
||||
|
||||
const mockActions = createMockCommandMenuItems({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
});
|
||||
|
||||
const addToFavoritesCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
const goToPeopleCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
);
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
action: addToFavoritesCommandMenuItem,
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getCommandMenuItemLabel(addToFavoritesCommandMenuItem?.label ?? ''),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
export const WithLink: Story = {
|
||||
args: {
|
||||
action: goToPeopleCommandMenuItem,
|
||||
to: '/objects/people',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const dropdownItem = await canvas.findByText(
|
||||
getCommandMenuItemLabel(goToPeopleCommandMenuItem?.label ?? ''),
|
||||
);
|
||||
expect(dropdownItem).toBeVisible();
|
||||
},
|
||||
};
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import { CommandListItem } from '@/command-menu-item/display/components/CommandListItem';
|
||||
import { NoSelectionRecordCommandKeys } from '@/command-menu-item/record/no-selection/types/NoSelectionRecordCommandKeys';
|
||||
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
|
||||
import { createMockCommandMenuItems } from '@/command-menu-item/mock/command-menu-items.mock';
|
||||
import { getCommandMenuItemLabel } from '@/command-menu-item/utils/getCommandMenuItemLabel';
|
||||
import { SelectableListComponentInstanceContext } from '@/ui/layout/selectable-list/states/contexts/SelectableListComponentInstanceContext';
|
||||
import { type Meta, type StoryObj } from '@storybook/react-vite';
|
||||
import { expect, fn, userEvent, within } from 'storybook/test';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||
|
||||
type Story = StoryObj<typeof CommandListItem>;
|
||||
|
||||
const deleteMock = fn();
|
||||
const addToFavoritesMock = fn();
|
||||
|
||||
const mockActions = createMockCommandMenuItems({
|
||||
deleteMock,
|
||||
addToFavoritesMock,
|
||||
});
|
||||
|
||||
const addToFavoritesCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === SingleRecordCommandKeys.ADD_TO_FAVORITES,
|
||||
);
|
||||
|
||||
const goToPeopleCommandMenuItem = mockActions.find(
|
||||
(action) => action.key === NoSelectionRecordCommandKeys.GO_TO_PEOPLE,
|
||||
);
|
||||
|
||||
const meta: Meta<typeof CommandListItem> = {
|
||||
title: 'Modules/CommandMenuItem/Display/CommandListItem',
|
||||
component: CommandListItem,
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<SelectableListComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'story' }}
|
||||
>
|
||||
<Story />
|
||||
</SelectableListComponentInstanceContext.Provider>
|
||||
),
|
||||
ComponentDecorator,
|
||||
RouterDecorator,
|
||||
],
|
||||
};
|
||||
|
||||
export default meta;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
action: addToFavoritesCommandMenuItem,
|
||||
onClick: addToFavoritesMock,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
await userEvent.click(
|
||||
await canvas.findByText(
|
||||
getCommandMenuItemLabel(addToFavoritesCommandMenuItem?.label ?? ''),
|
||||
),
|
||||
);
|
||||
expect(addToFavoritesMock).toHaveBeenCalled();
|
||||
},
|
||||
};
|
||||
|
||||
export const WithLink: Story = {
|
||||
args: {
|
||||
action: goToPeopleCommandMenuItem,
|
||||
to: '/objects/people',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
const listItem = await canvas.findByText(
|
||||
getCommandMenuItemLabel(goToPeopleCommandMenuItem?.label ?? ''),
|
||||
);
|
||||
expect(listItem).toBeVisible();
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user