7499 refactor right drawer to have contextual actions (#7954)

Closes #7499
- Modifies context store states to be component states
- Introduces the concept of `mainContextStore` which will dictate the
available actions inside the command K
- Adds contextual actions inside the right drawer
- Creates a new type of modal variant
This commit is contained in:
Raphaël Bosi
2024-10-22 18:35:45 +02:00
committed by GitHub
parent 6c93587efb
commit 6843a642b5
56 changed files with 718 additions and 418 deletions

View File

@ -1,5 +1,5 @@
import { useActionMenu } from '@/action-menu/hooks/useActionMenu';
import { actionMenuDropdownPositionComponentState } from '@/action-menu/states/actionMenuDropdownPositionComponentState';
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext';
import { useRecordBoardStates } from '@/object-record/record-board/hooks/internal/useRecordBoardStates';
import { RecordBoardCardContext } from '@/object-record/record-board/record-board-card/contexts/RecordBoardCardContext';
@ -188,7 +188,7 @@ export const RecordBoardCard = ({
const setActionMenuDropdownPosition = useSetRecoilState(
extractComponentState(
actionMenuDropdownPositionComponentState,
recordIndexActionMenuDropdownPositionComponentState,
`action-menu-dropdown-${recordBoardId}`,
),
);

View File

@ -2,7 +2,7 @@ import { useCallback, useEffect } from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { contextStoreTargetedRecordsRuleState } from '@/context-store/states/contextStoreTargetedRecordsRuleState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { getObjectSlug } from '@/object-metadata/utils/getObjectSlug';
import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoard';
@ -11,6 +11,7 @@ import { recordIndexIsCompactModeActiveState } from '@/object-record/record-inde
import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState';
import { computeRecordBoardColumnDefinitionsFromObjectMetadata } from '@/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { FieldMetadataType } from '~/generated-metadata/graphql';
import { isDefined } from '~/utils/isDefined';
@ -120,8 +121,8 @@ export const RecordIndexBoardDataLoaderEffect = ({
const selectedRecordIds = useRecoilValue(selectedRecordIdsSelector());
const setContextStoreTargetedRecords = useSetRecoilState(
contextStoreTargetedRecordsRuleState,
const setContextStoreTargetedRecords = useSetRecoilComponentStateV2(
contextStoreTargetedRecordsRuleComponentState,
);
useEffect(() => {

View File

@ -22,8 +22,9 @@ import { RecordFieldValueSelectorContextProvider } from '@/object-record/record-
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { SpreadsheetImportProvider } from '@/spreadsheet-import/provider/components/SpreadsheetImportProvider';
import { ActionMenu } from '@/action-menu/components/ActionMenu';
import { contextStoreTargetedRecordsRuleState } from '@/context-store/states/contextStoreTargetedRecordsRuleState';
import { RecordIndexActionMenu } from '@/action-menu/components/RecordIndexActionMenu';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { ViewBar } from '@/views/components/ViewBar';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { ViewField } from '@/views/types/ViewField';
@ -102,8 +103,8 @@ export const RecordIndexContainer = () => {
[columnDefinitions, setTableColumns],
);
const setContextStoreTargetedRecordsRule = useSetRecoilState(
contextStoreTargetedRecordsRuleState,
const setContextStoreTargetedRecordsRule = useSetRecoilComponentStateV2(
contextStoreTargetedRecordsRuleComponentState,
);
return (
@ -186,7 +187,7 @@ export const RecordIndexContainer = () => {
/>
</StyledContainerWithPadding>
)}
<ActionMenu actionMenuId={recordIndexId} />
<RecordIndexActionMenu actionMenuId={recordIndexId} />
</RecordFieldValueSelectorContextProvider>
</ViewComponentInstanceContext.Provider>
</StyledContainer>

View File

@ -1,22 +1,23 @@
import { contextStoreNumberOfSelectedRecordsState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsState';
import { contextStoreTargetedRecordsRuleState } from '@/context-store/states/contextStoreTargetedRecordsRuleState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
import { useFindManyParams } from '@/object-record/record-index/hooks/useLoadRecordIndexTable';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useContext, useEffect } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect =
() => {
const setContextStoreNumberOfSelectedRecords = useSetRecoilState(
contextStoreNumberOfSelectedRecordsState,
const setContextStoreNumberOfSelectedRecords = useSetRecoilComponentStateV2(
contextStoreNumberOfSelectedRecordsComponentState,
);
const contextStoreTargetedRecordsRule = useRecoilValue(
contextStoreTargetedRecordsRuleState,
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
);
const { objectNamePlural } = useContext(RecordIndexRootPropsContext);

View File

@ -1,13 +1,13 @@
import { contextStoreCurrentObjectMetadataIdState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdState';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useContext, useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
export const RecordIndexContainerContextStoreObjectMetadataEffect = () => {
const setContextStoreCurrentObjectMetadataItem = useSetRecoilState(
contextStoreCurrentObjectMetadataIdState,
const setContextStoreCurrentObjectMetadataItem = useSetRecoilComponentStateV2(
contextStoreCurrentObjectMetadataIdComponentState,
);
const { objectNamePlural } = useContext(RecordIndexRootPropsContext);

View File

@ -1,13 +1,14 @@
import { useContext, useEffect } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { useRecoilValue } from 'recoil';
import { contextStoreTargetedRecordsRuleState } from '@/context-store/states/contextStoreTargetedRecordsRuleState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useColumnDefinitionsFromFieldMetadata';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
import { useHandleToggleColumnFilter } from '@/object-record/record-index/hooks/useHandleToggleColumnFilter';
import { useHandleToggleColumnSort } from '@/object-record/record-index/hooks/useHandleToggleColumnSort';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useSetRecordCountInCurrentView } from '@/views/hooks/useSetRecordCountInCurrentView';
export const RecordIndexTableContainerEffect = () => {
@ -73,8 +74,8 @@ export const RecordIndexTableContainerEffect = () => {
);
}, [setRecordCountInCurrentView, setOnEntityCountChange]);
const setContextStoreTargetedRecords = useSetRecoilState(
contextStoreTargetedRecordsRuleState,
const setContextStoreTargetedRecords = useSetRecoilComponentStateV2(
contextStoreTargetedRecordsRuleComponentState,
);
const hasUserSelectedAllRows = useRecoilValue(hasUserSelectedAllRowsState);
const selectedRowIds = useRecoilValue(selectedRowIdsSelector());

View File

@ -130,6 +130,7 @@ const mocks: MockedResponse[] = [
const WrapperWithResponse = getJestMetadataAndApolloMocksAndContextStoreWrapper(
{
apolloMocks: mocks,
componentInstanceId: 'recordIndexId',
contextStoreTargetedRecordsRule: {
mode: 'selection',
selectedRecordIds: [],
@ -155,6 +156,7 @@ const graphqlEmptyResponse = [
const WrapperWithEmptyResponse =
getJestMetadataAndApolloMocksAndContextStoreWrapper({
apolloMocks: graphqlEmptyResponse,
componentInstanceId: 'recordIndexId',
contextStoreTargetedRecordsRule: {
mode: 'selection',
selectedRecordIds: [],
@ -207,7 +209,6 @@ describe('useRecordData', () => {
objectMetadataItem,
callback,
pageSize: 30,
delayMs: 0,
}),
{ wrapper: WrapperWithResponse },

View File

@ -7,7 +7,7 @@ import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefin
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isDefined } from '~/utils/isDefined';
import { contextStoreTargetedRecordsRuleState } from '@/context-store/states/contextStoreTargetedRecordsRuleState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords';
@ -15,6 +15,7 @@ import { useRecordBoardStates } from '@/object-record/record-board/hooks/interna
import { useFindManyParams } from '@/object-record/record-index/hooks/useLoadRecordIndexTable';
import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/record-index/options/constants/ExportTableDataDefaultPageSize';
import { useRecordIndexOptionsForBoard } from '@/object-record/record-index/options/hooks/useRecordIndexOptionsForBoard';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { ViewType } from '@/views/types/ViewType';
export const sleep = (ms: number) =>
@ -75,8 +76,8 @@ export const useRecordData = ({
);
const columns = useRecoilValue(visibleTableColumnsSelector());
const contextStoreTargetedRecordsRule = useRecoilValue(
contextStoreTargetedRecordsRuleState,
const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleComponentState,
);
const queryFilter = computeContextStoreFilters(

View File

@ -1,7 +1,10 @@
import { InformationBannerDeletedRecord } from '@/information-banner/components/deleted-record/InformationBannerDeletedRecord';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ShowPageContainer } from '@/ui/layout/page/components/ShowPageContainer';
import { SetMainContextStoreComponentInstanceIdEffect } from '@/context-store/components/SetMainContextStoreComponentInstanceIdEffect';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { InformationBannerDeletedRecord } from '@/information-banner/components/deleted-record/InformationBannerDeletedRecord';
import { RecordShowContainerContextStoreEffect } from '@/object-record/record-show/components/RecordShowContainerContextStoreEffect';
import { useRecordShowContainerData } from '@/object-record/record-show/hooks/useRecordShowContainerData';
import { useRecordShowContainerTabs } from '@/object-record/record-show/hooks/useRecordShowContainerTabs';
import { ShowPageSubContainer } from '@/ui/layout/show-page/components/ShowPageSubContainer';
@ -38,7 +41,16 @@ export const RecordShowContainer = ({
);
return (
<>
<ContextStoreComponentInstanceContext.Provider
value={{
instanceId: 'record-show',
}}
>
<RecordShowContainerContextStoreEffect
recordId={objectRecordId}
objectNameSingular={objectNameSingular}
/>
{!isInRightDrawer && <SetMainContextStoreComponentInstanceIdEffect />}
{recordFromStore && recordFromStore.deletedAt && (
<InformationBannerDeletedRecord
recordId={objectRecordId}
@ -57,6 +69,6 @@ export const RecordShowContainer = ({
isNewRightDrawerItemLoading={isNewRightDrawerItemLoading}
/>
</ShowPageContainer>
</>
</ContextStoreComponentInstanceContext.Provider>
);
};

View File

@ -0,0 +1,56 @@
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useEffect } from 'react';
export const RecordShowContainerContextStoreEffect = ({
recordId,
objectNameSingular,
}: {
recordId: string;
objectNameSingular: string;
}) => {
const setContextStoreTargetedRecordsRule = useSetRecoilComponentStateV2(
contextStoreTargetedRecordsRuleComponentState,
);
const setContextStoreCurrentObjectMetadataId = useSetRecoilComponentStateV2(
contextStoreCurrentObjectMetadataIdComponentState,
);
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: objectNameSingular,
});
const setContextStoreNumberOfSelectedRecords = useSetRecoilComponentStateV2(
contextStoreNumberOfSelectedRecordsComponentState,
);
useEffect(() => {
setContextStoreTargetedRecordsRule({
mode: 'selection',
selectedRecordIds: [recordId],
});
setContextStoreCurrentObjectMetadataId(objectMetadataItem?.id);
setContextStoreNumberOfSelectedRecords(1);
return () => {
setContextStoreTargetedRecordsRule({
mode: 'selection',
selectedRecordIds: [],
});
setContextStoreCurrentObjectMetadataId(null);
setContextStoreNumberOfSelectedRecords(0);
};
}, [
recordId,
setContextStoreTargetedRecordsRule,
setContextStoreCurrentObjectMetadataId,
objectMetadataItem?.id,
setContextStoreNumberOfSelectedRecords,
]);
return null;
};

View File

@ -1,6 +1,6 @@
import { useRecoilCallback } from 'recoil';
import { actionMenuDropdownPositionComponentState } from '@/action-menu/states/actionMenuDropdownPositionComponentState';
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
import { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
@ -23,7 +23,7 @@ export const useTriggerActionMenuDropdown = ({
set(
extractComponentState(
actionMenuDropdownPositionComponentState,
recordIndexActionMenuDropdownPositionComponentState,
`action-menu-dropdown-${recordTableId}`,
),
{