Fixed single record select hotkeys (#9433)
There is a problem of hotkey scope not being passed to the relation picker used for single record select fields. Fixed it where we open a single record select.
This commit is contained in:
@ -5,6 +5,7 @@ import { ObjectMetadataItemsLoadEffect } from '@/object-metadata/components/Obje
|
||||
import { PreComputedChipGeneratorsProvider } from '@/object-metadata/components/PreComputedChipGeneratorsProvider';
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { UserOrMetadataLoader } from '~/loading/components/UserOrMetadataLoader';
|
||||
|
||||
export const ObjectMetadataItemsProvider = ({
|
||||
@ -20,7 +21,7 @@ export const ObjectMetadataItemsProvider = ({
|
||||
{shouldDisplayChildren ? (
|
||||
<PreComputedChipGeneratorsProvider>
|
||||
<RecordPickerComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'record-picker' }}
|
||||
value={{ instanceId: RelationPickerHotkeyScope.RelationPicker }}
|
||||
>
|
||||
{children}
|
||||
</RecordPickerComponentInstanceContext.Provider>
|
||||
|
||||
@ -6,12 +6,14 @@ import { viewableRecordIdState } from '@/object-record/record-right-drawer/state
|
||||
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
|
||||
import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect';
|
||||
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
|
||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
|
||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const RecordBoardColumnNewOpportunity = ({
|
||||
columnId,
|
||||
position,
|
||||
@ -62,7 +64,7 @@ export const RecordBoardColumnNewOpportunity = ({
|
||||
{newRecord.isCreating && newRecord.position === position && (
|
||||
<OverlayContainer>
|
||||
<RecordPickerComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'relation-picker' }}
|
||||
value={{ instanceId: RelationPickerHotkeyScope.RelationPicker }}
|
||||
>
|
||||
<SingleRecordSelect
|
||||
onCancel={() => handleCreateSuccess(position, columnId, false)}
|
||||
|
||||
@ -21,7 +21,7 @@ export const useAddNewCard = () => {
|
||||
const { createOneRecord, selectFieldMetadataItem, objectMetadataItem } =
|
||||
useContext(RecordBoardContext);
|
||||
const { resetSearchFilter } = useRecordSelectSearch({
|
||||
recordPickerInstanceId: 'record-picker',
|
||||
recordPickerInstanceId: RelationPickerHotkeyScope.RelationPicker,
|
||||
});
|
||||
|
||||
const {
|
||||
|
||||
@ -78,7 +78,7 @@ export const RecordDetailRelationSection = ({
|
||||
|
||||
const relationRecordIds = relationRecords.map(({ id }) => id);
|
||||
|
||||
const dropdownId = `record-field-card-relation-picker-${fieldDefinition.label}-${recordId}`;
|
||||
const dropdownId = `record-field-card-relation-picker-${fieldDefinition.fieldMetadataId}-${recordId}`;
|
||||
|
||||
const { closeDropdown, isDropdownOpen, dropdownPlacement } =
|
||||
useDropdown(dropdownId);
|
||||
@ -138,6 +138,7 @@ export const RecordDetailRelationSection = ({
|
||||
},
|
||||
view: indexView?.id,
|
||||
};
|
||||
|
||||
const filterLinkHref = `/objects/${
|
||||
relationObjectMetadataItem.namePlural
|
||||
}?${qs.stringify(filterQueryParams)}`;
|
||||
|
||||
@ -9,6 +9,7 @@ import { SearchPickerInitialValueEffect } from '@/object-record/relation-picker/
|
||||
import { SingleRecordSelect } from '@/object-record/relation-picker/components/SingleRecordSelect';
|
||||
import { useAddNewRecordAndOpenRightDrawer } from '@/object-record/relation-picker/hooks/useAddNewRecordAndOpenRightDrawer';
|
||||
import { RecordForSelect } from '@/object-record/relation-picker/types/RecordForSelect';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
|
||||
export type RelationPickerProps = {
|
||||
selectedRecordId?: string;
|
||||
@ -29,7 +30,7 @@ export const RelationPicker = ({
|
||||
initialSearchFilter,
|
||||
fieldDefinition,
|
||||
}: RelationPickerProps) => {
|
||||
const recordPickerInstanceId = 'relation-picker';
|
||||
const recordPickerInstanceId = RelationPickerHotkeyScope.RelationPicker;
|
||||
|
||||
const handleRecordSelected = (
|
||||
selectedRecord: RecordForSelect | null | undefined,
|
||||
|
||||
@ -54,7 +54,7 @@ export const WithActivate: Story = {
|
||||
|
||||
await expect(handleActivateMockFunction).toHaveBeenCalledTimes(0);
|
||||
|
||||
const activateMenuItem = await canvas.getByText('Activate');
|
||||
const activateMenuItem = await canvas.findByText('Activate');
|
||||
|
||||
await userEvent.click(activateMenuItem);
|
||||
|
||||
@ -75,7 +75,7 @@ export const WithDelete: Story = {
|
||||
|
||||
await expect(handleDeleteMockFunction).toHaveBeenCalledTimes(0);
|
||||
|
||||
const deleteMenuItem = await canvas.getByText('Delete');
|
||||
const deleteMenuItem = await canvas.findByText('Delete');
|
||||
|
||||
await userEvent.click(deleteMenuItem);
|
||||
|
||||
|
||||
@ -51,13 +51,13 @@ export const WithOpenMonthSelect: Story = {
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
].forEach((monthLabel) =>
|
||||
expect(canvas.getByText(monthLabel)).toBeInTheDocument(),
|
||||
].forEach(async (monthLabel) =>
|
||||
expect(await canvas.findByText(monthLabel)).toBeInTheDocument(),
|
||||
);
|
||||
|
||||
await userEvent.click(canvas.getByText('February'));
|
||||
await userEvent.click(await canvas.findByText('February'));
|
||||
|
||||
expect(canvas.getByText('February')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('February')).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
@ -69,12 +69,12 @@ export const WithOpenYearSelect: Story = {
|
||||
|
||||
await userEvent.click(yearSelect);
|
||||
|
||||
['2024', '2025', '2026'].forEach((yearLabel) =>
|
||||
expect(canvas.getByText(yearLabel)).toBeInTheDocument(),
|
||||
['2024', '2025', '2026'].forEach(async (yearLabel) =>
|
||||
expect(await canvas.findByText(yearLabel)).toBeInTheDocument(),
|
||||
);
|
||||
|
||||
await userEvent.click(canvas.getByText('2024'));
|
||||
await userEvent.click(await canvas.findByText('2024'));
|
||||
|
||||
expect(canvas.getByText('2024')).toBeInTheDocument();
|
||||
expect(await canvas.findByText('2024')).toBeInTheDocument();
|
||||
},
|
||||
};
|
||||
|
||||
@ -17,11 +17,14 @@ import { useDropdown } from '../hooks/useDropdown';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { DropdownUnmountEffect } from '@/ui/layout/dropdown/components/DropdownUnmountEffect';
|
||||
import { DropdownComponentInstanceContext } from '@/ui/layout/dropdown/contexts/DropdownComponeInstanceContext';
|
||||
import { dropdownHotkeyComponentState } from '@/ui/layout/dropdown/states/dropdownHotkeyComponentState';
|
||||
import { dropdownMaxHeightComponentStateV2 } from '@/ui/layout/dropdown/states/dropdownMaxHeightComponentStateV2';
|
||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||
import styled from '@emotion/styled';
|
||||
import { flushSync } from 'react-dom';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
import { sleep } from '~/utils/sleep';
|
||||
import { DropdownOnToggleEffect } from './DropdownOnToggleEffect';
|
||||
|
||||
const StyledDropdownFallbackAnchor = styled.div`
|
||||
@ -104,13 +107,25 @@ export const Dropdown = ({
|
||||
strategy: dropdownStrategy,
|
||||
});
|
||||
|
||||
const handleClickableComponentClick = (event: MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
const handleClickableComponentClick = useRecoilCallback(
|
||||
({ set }) =>
|
||||
async (event: MouseEvent) => {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
toggleDropdown();
|
||||
onClickOutside?.();
|
||||
};
|
||||
// TODO: refactor this when we have finished dropdown refactor with state and V1 + V2
|
||||
set(
|
||||
dropdownHotkeyComponentState({ scopeId: dropdownId }),
|
||||
dropdownHotkeyScope,
|
||||
);
|
||||
|
||||
await sleep(100);
|
||||
|
||||
toggleDropdown();
|
||||
onClickOutside?.();
|
||||
},
|
||||
[dropdownId, dropdownHotkeyScope, onClickOutside, toggleDropdown],
|
||||
);
|
||||
|
||||
return (
|
||||
<DropdownComponentInstanceContext.Provider
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
|
||||
import { useDropdownStates } from '@/ui/layout/dropdown/hooks/internal/useDropdownStates';
|
||||
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
|
||||
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
|
||||
import { dropdownHotkeyComponentState } from '@/ui/layout/dropdown/states/dropdownHotkeyComponentState';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { useCallback } from 'react';
|
||||
import { isDefined } from '~/utils/isDefined';
|
||||
|
||||
export const useDropdown = (dropdownId?: string) => {
|
||||
const {
|
||||
scopeId,
|
||||
dropdownHotkeyScopeState,
|
||||
dropdownWidthState,
|
||||
isDropdownOpenState,
|
||||
dropdownPlacementState,
|
||||
@ -30,8 +31,6 @@ export const useDropdown = (dropdownId?: string) => {
|
||||
goBackToPreviousHotkeyScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const [dropdownHotkeyScope] = useRecoilState(dropdownHotkeyScopeState);
|
||||
|
||||
const [dropdownWidth, setDropdownWidth] = useRecoilState(dropdownWidthState);
|
||||
|
||||
const [dropdownPlacement, setDropdownPlacement] = useRecoilState(
|
||||
@ -54,18 +53,37 @@ export const useDropdown = (dropdownId?: string) => {
|
||||
goBackToPreviousDropdownFocusId,
|
||||
]);
|
||||
|
||||
const openDropdown = () => {
|
||||
if (!isDropdownOpen) {
|
||||
setIsDropdownOpen(true);
|
||||
setActiveDropdownFocusIdAndMemorizePrevious(dropdownId ?? scopeId);
|
||||
if (isDefined(dropdownHotkeyScope)) {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
dropdownHotkeyScope.scope,
|
||||
dropdownHotkeyScope.customScopes,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
const openDropdown = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
() => {
|
||||
if (!isDropdownOpen) {
|
||||
setIsDropdownOpen(true);
|
||||
setActiveDropdownFocusIdAndMemorizePrevious(dropdownId ?? scopeId);
|
||||
|
||||
const dropdownHotkeyScope = getSnapshotValue(
|
||||
snapshot,
|
||||
dropdownHotkeyComponentState({
|
||||
scopeId: dropdownId ?? scopeId,
|
||||
}),
|
||||
);
|
||||
|
||||
if (isDefined(dropdownHotkeyScope)) {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
dropdownHotkeyScope.scope,
|
||||
dropdownHotkeyScope.customScopes,
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
dropdownId,
|
||||
isDropdownOpen,
|
||||
scopeId,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
setActiveDropdownFocusIdAndMemorizePrevious,
|
||||
setIsDropdownOpen,
|
||||
],
|
||||
);
|
||||
|
||||
const toggleDropdown = () => {
|
||||
if (isDropdownOpen) {
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { Decorator } from '@storybook/react';
|
||||
|
||||
import { RecordPickerComponentInstanceContext } from '@/object-record/relation-picker/states/contexts/RecordPickerComponentInstanceContext';
|
||||
import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope';
|
||||
|
||||
export const RecordPickerDecorator: Decorator = (Story) => (
|
||||
<RecordPickerComponentInstanceContext.Provider
|
||||
value={{ instanceId: 'record-picker' }}
|
||||
value={{ instanceId: RelationPickerHotkeyScope.RelationPicker }}
|
||||
>
|
||||
<Story />
|
||||
</RecordPickerComponentInstanceContext.Provider>
|
||||
|
||||
Reference in New Issue
Block a user