Command menu search bar (#4337)

* Improve performance on findMany queries

* Fix

* Fix command menu not emptying the search on toggle

* Fix tests
This commit is contained in:
Charles Bochet
2024-03-06 14:20:05 +01:00
committed by GitHub
parent e7857d7fa3
commit b08b361dc0
4 changed files with 59 additions and 113 deletions

View File

@ -1,10 +1,11 @@
import { useMemo, useRef, useState } from 'react'; import { useMemo, useRef } from 'react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil'; import { useRecoilState, useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer'; import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
import { Activity } from '@/activities/types/Activity'; import { Activity } from '@/activities/types/Activity';
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { Company } from '@/companies/types/Company'; import { Company } from '@/companies/types/Company';
import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu'; import { useKeyboardShortcutMenu } from '@/keyboard-shortcut-menu/hooks/useKeyboardShortcutMenu';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
@ -105,23 +106,24 @@ export const CommandMenu = () => {
const openActivityRightDrawer = useOpenActivityRightDrawer(); const openActivityRightDrawer = useOpenActivityRightDrawer();
const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState); const isCommandMenuOpened = useRecoilValue(isCommandMenuOpenedState);
const [search, setSearch] = useState(''); const [commandMenuSearch, setCommandMenuSearch] = useRecoilState(
commandMenuSearchState,
);
const commandMenuCommands = useRecoilValue(commandMenuCommandsState); const commandMenuCommands = useRecoilValue(commandMenuCommandsState);
const { closeKeyboardShortcutMenu } = useKeyboardShortcutMenu(); const { closeKeyboardShortcutMenu } = useKeyboardShortcutMenu();
const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearch(event.target.value); setCommandMenuSearch(event.target.value);
}; };
useScopedHotkeys( useScopedHotkeys(
'ctrl+k,meta+k', 'ctrl+k,meta+k',
() => { () => {
closeKeyboardShortcutMenu(); closeKeyboardShortcutMenu();
setSearch('');
toggleCommandMenu(); toggleCommandMenu();
}, },
AppHotkeyScope.CommandMenu, AppHotkeyScope.CommandMenu,
[toggleCommandMenu, setSearch], [toggleCommandMenu],
); );
useScopedHotkeys( useScopedHotkeys(
@ -136,12 +138,12 @@ export const CommandMenu = () => {
const { records: people } = useFindManyRecords<Person>({ const { records: people } = useFindManyRecords<Person>({
skip: !isCommandMenuOpened, skip: !isCommandMenuOpened,
objectNameSingular: CoreObjectNameSingular.Person, objectNameSingular: CoreObjectNameSingular.Person,
filter: search filter: commandMenuSearch
? makeOrFilterVariables([ ? makeOrFilterVariables([
{ name: { firstName: { ilike: `%${search}%` } } }, { name: { firstName: { ilike: `%${commandMenuSearch}%` } } },
{ name: { lastName: { ilike: `%${search}%` } } }, { name: { lastName: { ilike: `%${commandMenuSearch}%` } } },
{ email: { ilike: `%${search}%` } }, { email: { ilike: `%${commandMenuSearch}%` } },
{ phone: { ilike: `%${search}%` } }, { phone: { ilike: `%${commandMenuSearch}%` } },
]) ])
: undefined, : undefined,
limit: 3, limit: 3,
@ -150,9 +152,9 @@ export const CommandMenu = () => {
const { records: companies } = useFindManyRecords<Company>({ const { records: companies } = useFindManyRecords<Company>({
skip: !isCommandMenuOpened, skip: !isCommandMenuOpened,
objectNameSingular: CoreObjectNameSingular.Company, objectNameSingular: CoreObjectNameSingular.Company,
filter: search filter: commandMenuSearch
? { ? {
name: { ilike: `%${search}%` }, name: { ilike: `%${commandMenuSearch}%` },
} }
: undefined, : undefined,
limit: 3, limit: 3,
@ -161,10 +163,10 @@ export const CommandMenu = () => {
const { records: activities } = useFindManyRecords<Activity>({ const { records: activities } = useFindManyRecords<Activity>({
skip: !isCommandMenuOpened, skip: !isCommandMenuOpened,
objectNameSingular: CoreObjectNameSingular.Activity, objectNameSingular: CoreObjectNameSingular.Activity,
filter: search filter: commandMenuSearch
? makeOrFilterVariables([ ? makeOrFilterVariables([
{ title: { ilike: `%${search}%` } }, { title: { ilike: `%${commandMenuSearch}%` } },
{ body: { ilike: `%${search}%` } }, { body: { ilike: `%${commandMenuSearch}%` } },
]) ])
: undefined, : undefined,
limit: 3, limit: 3,
@ -224,15 +226,17 @@ export const CommandMenu = () => {
const matchingNavigateCommand = commandMenuCommands.filter( const matchingNavigateCommand = commandMenuCommands.filter(
(cmd) => (cmd) =>
(search.length > 0 (commandMenuSearch.length > 0
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search) ? checkInShortcuts(cmd, commandMenuSearch) ||
checkInLabels(cmd, commandMenuSearch)
: true) && cmd.type === CommandType.Navigate, : true) && cmd.type === CommandType.Navigate,
); );
const matchingCreateCommand = commandMenuCommands.filter( const matchingCreateCommand = commandMenuCommands.filter(
(cmd) => (cmd) =>
(search.length > 0 (commandMenuSearch.length > 0
? checkInShortcuts(cmd, search) || checkInLabels(cmd, search) ? checkInShortcuts(cmd, commandMenuSearch) ||
checkInLabels(cmd, commandMenuSearch)
: true) && cmd.type === CommandType.Create, : true) && cmd.type === CommandType.Create,
); );
@ -254,7 +258,7 @@ export const CommandMenu = () => {
<StyledDialog ref={commandMenuRef}> <StyledDialog ref={commandMenuRef}>
<StyledInput <StyledInput
autoFocus autoFocus
value={search} value={commandMenuSearch}
placeholder="Search" placeholder="Search"
onChange={handleSearchChange} onChange={handleSearchChange}
/> />

View File

@ -2,6 +2,7 @@ import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useRecoilCallback, useSetRecoilState } from 'recoil'; import { useRecoilCallback, useSetRecoilState } from 'recoil';
import { commandMenuSearchState } from '@/command-menu/states/commandMenuSearchState';
import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList'; import { useSelectableList } from '@/ui/layout/selectable-list/hooks/useSelectableList';
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope'; import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
@ -42,17 +43,22 @@ export const useCommandMenu = () => {
[goBackToPreviousHotkeyScope, resetSelectedItem, setIsCommandMenuOpened], [goBackToPreviousHotkeyScope, resetSelectedItem, setIsCommandMenuOpened],
); );
const toggleCommandMenu = useRecoilCallback(({ snapshot }) => async () => { const toggleCommandMenu = useRecoilCallback(
const isCommandMenuOpened = snapshot ({ snapshot, set }) =>
.getLoadable(isCommandMenuOpenedState) async () => {
.getValue(); const isCommandMenuOpened = snapshot
.getLoadable(isCommandMenuOpenedState)
.getValue();
if (isCommandMenuOpened) { set(commandMenuSearchState, '');
closeCommandMenu();
} else { if (isCommandMenuOpened) {
openCommandMenu(); closeCommandMenu();
} } else {
}); openCommandMenu();
}
},
);
const addToCommandMenu = useCallback( const addToCommandMenu = useCallback(
(addCommand: Command[]) => { (addCommand: Command[]) => {

View File

@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const commandMenuSearchState = atom<string>({
key: 'command-menu/commandMenuSearchState',
default: '',
});

View File

@ -20,43 +20,20 @@ const getOneToManyRelation = () => {
return { return {
field: objectMetadataItem.fields.find((field) => field.name === 'company')!, field: objectMetadataItem.fields.find((field) => field.name === 'company')!,
res: `company res: `company
{ {
__typename __typename
id id
xLink xLink
{ {
label label
url url
} }
accountOwner
{
__typename
id
}
linkedinLink linkedinLink
{ {
label label
url url
} }
attachments
{
edges {
node {
__typename
id
}
}
}
domainName domainName
opportunities
{
edges {
node {
__typename
id
}
}
}
annualRecurringRevenue annualRecurringRevenue
{ {
amountMicros amountMicros
@ -65,39 +42,12 @@ opportunities
createdAt createdAt
address address
updatedAt updatedAt
activityTargets
{
edges {
node {
__typename
id
}
}
}
favorites
{
edges {
node {
__typename
id
}
}
}
people
{
edges {
node {
__typename
id
}
}
}
name name
accountOwnerId accountOwnerId
employees employees
id id
idealCustomerProfile idealCustomerProfile
}`, }`,
}; };
}; };
@ -133,27 +83,17 @@ const getOneToManyFromRelationField = () => {
return { return {
field, field,
res: `opportunities res: `opportunities
{ {
edges { edges {
node { node {
__typename __typename
id id
personId personId
pointOfContactId pointOfContactId
updatedAt updatedAt
company
{
__typename
id
}
companyId companyId
pipelineStepId pipelineStepId
probability probability
pipelineStep
{
__typename
id
}
closeDate closeDate
amount amount
{ {
@ -162,19 +102,9 @@ closeDate
} }
id id
createdAt createdAt
pointOfContact }
{
__typename
id
}
person
{
__typename
id
}
} }
} }`,
}`,
}; };
}; };