diff --git a/front/src/modules/command-menu/components/CommandMenu.tsx b/front/src/modules/command-menu/components/CommandMenu.tsx
index 66ffca3a0..80e6def7e 100644
--- a/front/src/modules/command-menu/components/CommandMenu.tsx
+++ b/front/src/modules/command-menu/components/CommandMenu.tsx
@@ -17,7 +17,7 @@ import { getLogoUrlFromDomainName } from '~/utils';
import { useCommandMenu } from '../hooks/useCommandMenu';
import { commandMenuCommandsState } from '../states/commandMenuCommandsState';
import { isCommandMenuOpenedState } from '../states/isCommandMenuOpenedState';
-import { CommandType } from '../types/Command';
+import { Command, CommandType } from '../types/Command';
import { CommandMenuItem } from './CommandMenuItem';
import {
@@ -65,6 +65,7 @@ export const CommandMenu = () => {
limit: 3,
},
});
+
const companies = companyData?.searchResults ?? [];
const { data: activityData } = useSearchActivityQuery({
@@ -78,18 +79,38 @@ export const CommandMenu = () => {
limit: 3,
},
});
+
const activities = activityData?.searchResults ?? [];
- const matchingNavigateCommand = commandMenuCommands.find(
+ const checkInShortcuts = (cmd: Command, search: string) => {
+ if (cmd.shortcuts && cmd.shortcuts.length > 0) {
+ return cmd.shortcuts
+ .join('')
+ .toLowerCase()
+ .includes(search.toLowerCase());
+ }
+ return false;
+ };
+
+ const checkInLabels = (cmd: Command, search: string) => {
+ if (cmd.label) {
+ return cmd.label.toLowerCase().includes(search.toLowerCase());
+ }
+ return false;
+ };
+
+ const matchingNavigateCommand = commandMenuCommands.filter(
(cmd) =>
- cmd.shortcuts?.join('') === search?.toUpperCase() &&
- cmd.type === CommandType.Navigate,
+ (search.length > 0
+ ? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
+ : true) && cmd.type === CommandType.Navigate,
);
- const matchingCreateCommand = commandMenuCommands.find(
+ const matchingCreateCommand = commandMenuCommands.filter(
(cmd) =>
- cmd.shortcuts?.join('') === search?.toUpperCase() &&
- cmd.type === CommandType.Create,
+ (search.length > 0
+ ? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
+ : true) && cmd.type === CommandType.Create,
);
return (
@@ -100,122 +121,96 @@ export const CommandMenu = () => {
closeCommandMenu();
}
}}
- label="Global Command Menu"
shouldFilter={false}
+ label="Global Command Menu"
>
- No results found.
- {!matchingCreateCommand && (
+ {matchingCreateCommand.length < 1 &&
+ matchingNavigateCommand.length < 1 &&
+ people.length < 1 &&
+ companies.length < 1 &&
+ activities.length < 1 && No results found.}
+ {matchingCreateCommand.length > 0 && (
- {commandMenuCommands
- .filter((cmd) => cmd.type === CommandType.Create)
- .map((cmd) => (
-
- ))}
+ {matchingCreateCommand.map((cmd) => (
+
+ ))}
)}
- {matchingCreateCommand && (
-
-
-
- )}
- {matchingNavigateCommand && (
+ {matchingNavigateCommand.length > 0 && (
-
+ {matchingNavigateCommand.map((cmd) => (
+
+ ))}
)}
- {!!people.length && (
+ {people.length > 0 && (
{people.map((person) => (
(
)}
/>
))}
)}
- {!!companies.length && (
+ {companies.length > 0 && (
{companies.map((company) => (
(
)}
/>
))}
)}
- {!!activities.length && (
+ {activities.length > 0 && (
{activities.map((activity) => (
openActivityRightDrawer(activity.id)}
- label={activity.title ?? ''}
- key={activity.id}
Icon={IconNotes}
+ key={activity.id}
+ label={activity.title ?? ''}
+ onClick={() => openActivityRightDrawer(activity.id)}
/>
))}
)}
- {!matchingNavigateCommand && (
-
- {commandMenuCommands
- .filter(
- (cmd) =>
- (cmd.shortcuts?.join('').includes(search?.toUpperCase()) ||
- cmd.label?.toUpperCase().includes(search?.toUpperCase())) &&
- cmd.type === CommandType.Navigate,
- )
- .map((cmd) => (
-
- ))}
-
- )}
);
diff --git a/front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx b/front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx
index a91c47ffd..501bbd782 100644
--- a/front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx
+++ b/front/src/modules/command-menu/components/__stories__/CommandMenu.stories.tsx
@@ -1,15 +1,160 @@
import { MemoryRouter } from 'react-router-dom';
+import { expect } from '@storybook/jest';
import type { Meta, StoryObj } from '@storybook/react';
import { fireEvent, userEvent, within } from '@storybook/testing-library';
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
import { sleep } from '~/testing/sleep';
-import { CommandMenu } from '../CommandMenu';
+import { WrapperCommandMenu } from './WrapperCommandMenu';
-const meta: Meta = {
- title: 'Modules/CommandMenu/CommandMenu',
- component: CommandMenu,
+enum CommandType {
+ Navigate = 'Navigate',
+ Create = 'Create',
+}
+
+const meta: Meta = {
+ title: 'Modules/CommandMenu/WrapperCommandMenu',
+ component: () => (
+
+ ),
decorators: [
(Story) => (
@@ -21,36 +166,94 @@ const meta: Meta = {
};
export default meta;
-type Story = StoryObj;
+type Story = StoryObj;
-export const Default: Story = {};
-
-export const CmdK: Story = {
+export const DefaultWithoutSearch: Story = {
play: async ({ canvasElement }) => {
fireEvent.keyDown(canvasElement, {
key: 'k',
code: 'KeyK',
metaKey: true,
});
-
await sleep(50);
-
const canvas = within(document.body);
-
const searchInput = await canvas.findByPlaceholderText('Search');
+ await sleep(10);
+ await userEvent.type(searchInput, '');
+ expect(await canvas.findByText('Create Task')).toBeInTheDocument();
+ expect(await canvas.findByText('Go to People')).toBeInTheDocument();
+ expect(await canvas.findByText('Go to Companies')).toBeInTheDocument();
+ expect(await canvas.findByText('Go to Opportunities')).toBeInTheDocument();
+ expect(await canvas.findByText('Go to Settings')).toBeInTheDocument();
+ expect(await canvas.findByText('Go to Tasks')).toBeInTheDocument();
+ },
+};
- await userEvent.type(searchInput, '{arrowdown}');
- await userEvent.type(searchInput, '{arrowup}');
- await userEvent.type(searchInput, '{arrowdown}');
- await userEvent.type(searchInput, '{arrowdown}');
- await userEvent.type(searchInput, '{enter}');
-
- await sleep(50);
-
+export const MatchingPersonCompanyActivityCreateNavigate: Story = {
+ play: async ({ canvasElement }) => {
fireEvent.keyDown(canvasElement, {
key: 'k',
code: 'KeyK',
metaKey: true,
});
+ await sleep(50);
+ const canvas = within(document.body);
+ const searchInput = await canvas.findByPlaceholderText('Search');
+ await sleep(10);
+ await userEvent.type(searchInput, 'a');
+ expect(await canvas.findByText('Isabella Scott')).toBeInTheDocument();
+ expect(await canvas.findByText('Airbnb')).toBeInTheDocument();
+ expect(await canvas.findByText('Buyout Proposal')).toBeInTheDocument();
+ expect(await canvas.findByText('Create Task')).toBeInTheDocument();
+ expect(await canvas.findByText('Go to Tasks')).toBeInTheDocument();
+ },
+};
+
+export const OnlyMatchingCreateAndNavigate: Story = {
+ play: async ({ canvasElement }) => {
+ fireEvent.keyDown(canvasElement, {
+ key: 'k',
+ code: 'KeyK',
+ metaKey: true,
+ });
+ await sleep(50);
+ const canvas = within(document.body);
+ const searchInput = await canvas.findByPlaceholderText('Search');
+ await sleep(10);
+ await userEvent.type(searchInput, 'tas');
+ expect(await canvas.findByText('Create Task')).toBeInTheDocument();
+ expect(await canvas.findByText('Go to Tasks')).toBeInTheDocument();
+ },
+};
+
+export const AtleastMatchingOnePerson: Story = {
+ play: async ({ canvasElement }) => {
+ fireEvent.keyDown(canvasElement, {
+ key: 'k',
+ code: 'KeyK',
+ metaKey: true,
+ });
+ await sleep(50);
+ const canvas = within(document.body);
+ const searchInput = await canvas.findByPlaceholderText('Search');
+ await sleep(10);
+ await userEvent.type(searchInput, 'sy');
+ expect(await canvas.findByText('Sylvie Palmer')).toBeInTheDocument();
+ },
+};
+
+export const NotMatchingAnything: Story = {
+ play: async ({ canvasElement }) => {
+ fireEvent.keyDown(canvasElement, {
+ key: 'k',
+ code: 'KeyK',
+ metaKey: true,
+ });
+ await sleep(50);
+ const canvas = within(document.body);
+ const searchInput = await canvas.findByPlaceholderText('Search');
+ await sleep(10);
+ await userEvent.type(searchInput, 'asdasdasd');
+ expect(await canvas.findByText('No results found.')).toBeInTheDocument();
},
};
diff --git a/front/src/modules/command-menu/components/__stories__/WrapperCommandMenu.tsx b/front/src/modules/command-menu/components/__stories__/WrapperCommandMenu.tsx
new file mode 100644
index 000000000..ccdea421c
--- /dev/null
+++ b/front/src/modules/command-menu/components/__stories__/WrapperCommandMenu.tsx
@@ -0,0 +1,247 @@
+import { useState } from 'react';
+import { useRecoilValue } from 'recoil';
+
+import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
+import { IconNotes } from '@/ui/icon';
+import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
+import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
+import { Avatar } from '@/users/components/Avatar';
+import { getLogoUrlFromDomainName } from '~/utils';
+
+import { useCommandMenu } from '../../hooks/useCommandMenu';
+import { isCommandMenuOpenedState } from '../../states/isCommandMenuOpenedState';
+import { Command, CommandType } from '../../types/Command';
+import { CommandMenuItem } from '../CommandMenuItem';
+import {
+ StyledDialog,
+ StyledEmpty,
+ StyledGroup,
+ StyledInput,
+ StyledList,
+} from '../CommandMenuStyles';
+
+export const WrapperCommandMenu = ({
+ values,
+ people,
+ companies,
+ activities,
+}: {
+ values: Command[];
+ people: Array<{
+ __typename?: 'Person';
+ id: string;
+ phone?: string | null;
+ email?: string | null;
+ city?: string | null;
+ firstName?: string | null;
+ lastName?: string | null;
+ displayName: string;
+ avatarUrl?: string | null;
+ createdAt: string;
+ }>;
+ companies: Array<{
+ __typename?: 'Company';
+ address: string;
+ createdAt: string;
+ domainName: string;
+ employees?: number | null;
+ linkedinUrl?: string | null;
+ xUrl?: string | null;
+ annualRecurringRevenue?: number | null;
+ idealCustomerProfile: boolean;
+ id: string;
+ name: string;
+ _activityCount: number;
+ accountOwner?: {
+ __typename?: 'User';
+ id: string;
+ email: string;
+ displayName: string;
+ avatarUrl?: string | null;
+ } | null;
+ }>;
+ activities: Array<{
+ __typename?: 'Activity';
+ id: string;
+ title?: string | null;
+ body?: string | null;
+ }>;
+}) => {
+ const { openCommandMenu, closeCommandMenu } = useCommandMenu();
+ const openActivityRightDrawer = useOpenActivityRightDrawer();
+ const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
+ const [search, setSearch] = useState('');
+ const commandMenuCommands = values;
+
+ const peopleData = people.filter((i) =>
+ search.length > 0
+ ? (i.firstName
+ ? i.firstName?.toLowerCase().includes(search.toLowerCase())
+ : false) ||
+ (i.lastName
+ ? i.lastName?.toLowerCase().includes(search.toLowerCase())
+ : false)
+ : false,
+ );
+
+ const companyData = companies.filter((i) =>
+ search.length > 0
+ ? i.name
+ ? i.name?.toLowerCase().includes(search.toLowerCase())
+ : false
+ : false,
+ );
+
+ const activityData = activities.filter((i) =>
+ search.length > 0
+ ? (i.title
+ ? i.title?.toLowerCase().includes(search.toLowerCase())
+ : false) ||
+ (i.body ? i.body?.toLowerCase().includes(search.toLowerCase()) : false)
+ : false,
+ );
+
+ useScopedHotkeys(
+ 'ctrl+k,meta+k',
+ () => {
+ openCommandMenu();
+ },
+ AppHotkeyScope.CommandMenu,
+ [openCommandMenu],
+ );
+
+ const checkInShortcuts = (cmd: Command, search: string) => {
+ if (cmd.shortcuts && cmd.shortcuts.length > 0) {
+ return cmd.shortcuts
+ .join('')
+ .toLowerCase()
+ .includes(search.toLowerCase());
+ }
+ return false;
+ };
+
+ const checkInLabels = (cmd: Command, search: string) => {
+ if (cmd.label) {
+ return cmd.label.toLowerCase().includes(search.toLowerCase());
+ }
+ return false;
+ };
+
+ const matchingNavigateCommand = commandMenuCommands.filter(
+ (cmd) =>
+ (search.length > 0
+ ? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
+ : true) && cmd.type === CommandType.Navigate,
+ );
+
+ const matchingCreateCommand = commandMenuCommands.filter(
+ (cmd) =>
+ (search.length > 0
+ ? checkInShortcuts(cmd, search) || checkInLabels(cmd, search)
+ : true) && cmd.type === CommandType.Create,
+ );
+
+ return (
+ {
+ if (!opened) {
+ closeCommandMenu();
+ }
+ }}
+ shouldFilter={false}
+ label="Global Command Menu"
+ >
+
+
+ {matchingCreateCommand.length < 1 &&
+ matchingNavigateCommand.length < 1 &&
+ peopleData.length < 1 &&
+ companyData.length < 1 &&
+ activityData.length < 1 && (
+ No results found.
+ )}
+ {matchingCreateCommand.length > 0 && (
+
+ {matchingCreateCommand.map((cmd) => (
+
+ ))}
+
+ )}
+ {matchingNavigateCommand.length > 0 && (
+
+ {matchingNavigateCommand.map((cmd) => (
+
+ ))}
+
+ )}
+ {peopleData.length > 0 && (
+
+ {peopleData.map((person) => (
+ (
+
+ )}
+ />
+ ))}
+
+ )}
+ {companyData.length > 0 && (
+
+ {companyData.map((company) => (
+ (
+
+ )}
+ />
+ ))}
+
+ )}
+ {activityData.length > 0 && (
+
+ {activityData.map((activity) => (
+ openActivityRightDrawer(activity.id)}
+ />
+ ))}
+
+ )}
+
+
+ );
+};