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,6 +1,7 @@
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries'; import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
import { contextStoreNumberOfSelectedRecordsState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsState'; import { ActionMenuType } from '@/action-menu/types/ActionMenuType';
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 { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
import { useFavorites } from '@/favorites/hooks/useFavorites'; import { useFavorites } from '@/favorites/hooks/useFavorites';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
@ -9,16 +10,19 @@ import { useDeleteManyRecords } from '@/object-record/hooks/useDeleteManyRecords
import { useFetchAllRecordIds } from '@/object-record/hooks/useFetchAllRecordIds'; import { useFetchAllRecordIds } from '@/object-record/hooks/useFetchAllRecordIds';
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable'; import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal'; import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { IconTrash, isDefined } from 'twenty-ui'; import { IconTrash, isDefined } from 'twenty-ui';
export const DeleteRecordsActionEffect = ({ export const DeleteRecordsActionEffect = ({
position, position,
objectMetadataItem, objectMetadataItem,
actionMenuType,
}: { }: {
position: number; position: number;
objectMetadataItem: ObjectMetadataItem; objectMetadataItem: ObjectMetadataItem;
actionMenuType: ActionMenuType;
}) => { }) => {
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries(); const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
@ -35,12 +39,12 @@ export const DeleteRecordsActionEffect = ({
const { favorites, deleteFavorite } = useFavorites(); const { favorites, deleteFavorite } = useFavorites();
const contextStoreNumberOfSelectedRecords = useRecoilValue( const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsState, contextStoreNumberOfSelectedRecordsComponentState,
); );
const contextStoreTargetedRecordsRule = useRecoilValue( const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleState, contextStoreTargetedRecordsRuleComponentState,
); );
const graphqlFilter = computeContextStoreFilters( const graphqlFilter = computeContextStoreFilters(
@ -53,6 +57,8 @@ export const DeleteRecordsActionEffect = ({
filter: graphqlFilter, filter: graphqlFilter,
}); });
const { closeRightDrawer } = useRightDrawer();
const handleDeleteClick = useCallback(async () => { const handleDeleteClick = useCallback(async () => {
const recordIdsToDelete = await fetchAllRecordIds(); const recordIdsToDelete = await fetchAllRecordIds();
@ -112,10 +118,19 @@ export const DeleteRecordsActionEffect = ({
}? ${ }? ${
contextStoreNumberOfSelectedRecords === 1 ? 'It' : 'They' contextStoreNumberOfSelectedRecords === 1 ? 'It' : 'They'
} can be recovered from the Options menu.`} } can be recovered from the Options menu.`}
onConfirmClick={() => handleDeleteClick()} onConfirmClick={() => {
handleDeleteClick();
if (actionMenuType === 'recordShow') {
closeRightDrawer();
}
}}
deleteButtonText={`Delete ${ deleteButtonText={`Delete ${
contextStoreNumberOfSelectedRecords > 1 ? 'Records' : 'Record' contextStoreNumberOfSelectedRecords > 1 ? 'Records' : 'Record'
}`} }`}
modalVariant={
actionMenuType === 'recordShow' ? 'tertiary' : 'primary'
}
/> />
), ),
}); });
@ -127,8 +142,10 @@ export const DeleteRecordsActionEffect = ({
removeActionMenuEntry('delete'); removeActionMenuEntry('delete');
}; };
}, [ }, [
actionMenuType,
addActionMenuEntry, addActionMenuEntry,
canDelete, canDelete,
closeRightDrawer,
contextStoreNumberOfSelectedRecords, contextStoreNumberOfSelectedRecords,
handleDeleteClick, handleDeleteClick,
isDeleteRecordsModalOpen, isDeleteRecordsModalOpen,

View File

@ -1,8 +1,9 @@
import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries'; import { useActionMenuEntries } from '@/action-menu/hooks/useActionMenuEntries';
import { contextStoreTargetedRecordsRuleState } from '@/context-store/states/contextStoreTargetedRecordsRuleState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useFavorites } from '@/favorites/hooks/useFavorites'; import { useFavorites } from '@/favorites/hooks/useFavorites';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { IconHeart, IconHeartOff, isDefined } from 'twenty-ui'; import { IconHeart, IconHeartOff, isDefined } from 'twenty-ui';
@ -16,8 +17,8 @@ export const ManageFavoritesActionEffect = ({
}) => { }) => {
const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries(); const { addActionMenuEntry, removeActionMenuEntry } = useActionMenuEntries();
const contextStoreTargetedRecordsRule = useRecoilValue( const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleState, contextStoreTargetedRecordsRuleComponentState,
); );
const { favorites, createFavorite, deleteFavorite } = useFavorites(); const { favorites, createFavorite, deleteFavorite } = useFavorites();

View File

@ -1,13 +1,16 @@
import { DeleteRecordsActionEffect } from '@/action-menu/actions/record-actions/components/DeleteRecordsActionEffect'; import { DeleteRecordsActionEffect } from '@/action-menu/actions/record-actions/components/DeleteRecordsActionEffect';
import { ExportRecordsActionEffect } from '@/action-menu/actions/record-actions/components/ExportRecordsActionEffect'; import { ExportRecordsActionEffect } from '@/action-menu/actions/record-actions/components/ExportRecordsActionEffect';
import { ActionMenuType } from '@/action-menu/types/ActionMenuType';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
const actionEffects = [ExportRecordsActionEffect, DeleteRecordsActionEffect]; const actionEffects = [ExportRecordsActionEffect, DeleteRecordsActionEffect];
export const MultipleRecordsActionMenuEntriesSetter = ({ export const MultipleRecordsActionMenuEntriesSetter = ({
objectMetadataItem, objectMetadataItem,
actionMenuType,
}: { }: {
objectMetadataItem: ObjectMetadataItem; objectMetadataItem: ObjectMetadataItem;
actionMenuType: ActionMenuType;
}) => { }) => {
return ( return (
<> <>
@ -16,6 +19,7 @@ export const MultipleRecordsActionMenuEntriesSetter = ({
key={index} key={index}
position={index} position={index}
objectMetadataItem={objectMetadataItem} objectMetadataItem={objectMetadataItem}
actionMenuType={actionMenuType}
/> />
))} ))}
</> </>

View File

@ -1,17 +1,22 @@
import { MultipleRecordsActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/MultipleRecordsActionMenuEntriesSetter'; import { MultipleRecordsActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/MultipleRecordsActionMenuEntriesSetter';
import { SingleRecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/SingleRecordActionMenuEntriesSetter'; import { SingleRecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/SingleRecordActionMenuEntriesSetter';
import { contextStoreCurrentObjectMetadataIdState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdState'; import { ActionMenuType } from '@/action-menu/types/ActionMenuType';
import { contextStoreNumberOfSelectedRecordsState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsState'; import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById'; import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { useRecoilValue } from 'recoil'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const RecordActionMenuEntriesSetter = () => { export const RecordActionMenuEntriesSetter = ({
const contextStoreNumberOfSelectedRecords = useRecoilValue( actionMenuType,
contextStoreNumberOfSelectedRecordsState, }: {
actionMenuType: ActionMenuType;
}) => {
const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsComponentState,
); );
const contextStoreCurrentObjectMetadataId = useRecoilValue( const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataIdState, contextStoreCurrentObjectMetadataIdComponentState,
); );
const { objectMetadataItem } = useObjectMetadataItemById({ const { objectMetadataItem } = useObjectMetadataItemById({
@ -32,6 +37,7 @@ export const RecordActionMenuEntriesSetter = () => {
return ( return (
<SingleRecordActionMenuEntriesSetter <SingleRecordActionMenuEntriesSetter
objectMetadataItem={objectMetadataItem} objectMetadataItem={objectMetadataItem}
actionMenuType={actionMenuType}
/> />
); );
} }
@ -39,6 +45,7 @@ export const RecordActionMenuEntriesSetter = () => {
return ( return (
<MultipleRecordsActionMenuEntriesSetter <MultipleRecordsActionMenuEntriesSetter
objectMetadataItem={objectMetadataItem} objectMetadataItem={objectMetadataItem}
actionMenuType={actionMenuType}
/> />
); );
}; };

View File

@ -1,12 +1,15 @@
import { DeleteRecordsActionEffect } from '@/action-menu/actions/record-actions/components/DeleteRecordsActionEffect'; import { DeleteRecordsActionEffect } from '@/action-menu/actions/record-actions/components/DeleteRecordsActionEffect';
import { ExportRecordsActionEffect } from '@/action-menu/actions/record-actions/components/ExportRecordsActionEffect'; import { ExportRecordsActionEffect } from '@/action-menu/actions/record-actions/components/ExportRecordsActionEffect';
import { ManageFavoritesActionEffect } from '@/action-menu/actions/record-actions/components/ManageFavoritesActionEffect'; import { ManageFavoritesActionEffect } from '@/action-menu/actions/record-actions/components/ManageFavoritesActionEffect';
import { ActionMenuType } from '@/action-menu/types/ActionMenuType';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
export const SingleRecordActionMenuEntriesSetter = ({ export const SingleRecordActionMenuEntriesSetter = ({
objectMetadataItem, objectMetadataItem,
actionMenuType,
}: { }: {
objectMetadataItem: ObjectMetadataItem; objectMetadataItem: ObjectMetadataItem;
actionMenuType: ActionMenuType;
}) => { }) => {
const actionEffects = [ const actionEffects = [
ManageFavoritesActionEffect, ManageFavoritesActionEffect,
@ -20,6 +23,7 @@ export const SingleRecordActionMenuEntriesSetter = ({
key={index} key={index}
position={index} position={index}
objectMetadataItem={objectMetadataItem} objectMetadataItem={objectMetadataItem}
actionMenuType={actionMenuType}
/> />
))} ))}
</> </>

View File

@ -1,30 +0,0 @@
import { RecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter';
import { ActionMenuBar } from '@/action-menu/components/ActionMenuBar';
import { ActionMenuConfirmationModals } from '@/action-menu/components/ActionMenuConfirmationModals';
import { ActionMenuDropdown } from '@/action-menu/components/ActionMenuDropdown';
import { ActionMenuEffect } from '@/action-menu/components/ActionMenuEffect';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { contextStoreCurrentObjectMetadataIdState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdState';
import { useRecoilValue } from 'recoil';
export const ActionMenu = ({ actionMenuId }: { actionMenuId: string }) => {
const contextStoreCurrentObjectMetadataId = useRecoilValue(
contextStoreCurrentObjectMetadataIdState,
);
return (
<>
{contextStoreCurrentObjectMetadataId && (
<ActionMenuComponentInstanceContext.Provider
value={{ instanceId: actionMenuId }}
>
<ActionMenuBar />
<ActionMenuDropdown />
<ActionMenuConfirmationModals />
<ActionMenuEffect />
<RecordActionMenuEntriesSetter />
</ActionMenuComponentInstanceContext.Provider>
)}
</>
);
};

View File

@ -0,0 +1,35 @@
import { RecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter';
import { ActionMenuConfirmationModals } from '@/action-menu/components/ActionMenuConfirmationModals';
import { RecordIndexActionMenuBar } from '@/action-menu/components/RecordIndexActionMenuBar';
import { RecordIndexActionMenuDropdown } from '@/action-menu/components/RecordIndexActionMenuDropdown';
import { RecordIndexActionMenuEffect } from '@/action-menu/components/RecordIndexActionMenuEffect';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const RecordIndexActionMenu = ({
actionMenuId,
}: {
actionMenuId: string;
}) => {
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataIdComponentState,
);
return (
<>
{contextStoreCurrentObjectMetadataId && (
<ActionMenuComponentInstanceContext.Provider
value={{ instanceId: actionMenuId }}
>
<RecordIndexActionMenuBar />
<RecordIndexActionMenuDropdown />
<ActionMenuConfirmationModals />
<RecordIndexActionMenuEffect />
<RecordActionMenuEntriesSetter actionMenuType="recordIndex" />
</ActionMenuComponentInstanceContext.Provider>
)}
</>
);
};

View File

@ -1,14 +1,13 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { ActionMenuBarEntry } from '@/action-menu/components/ActionMenuBarEntry'; import { RecordIndexActionMenuBarEntry } from '@/action-menu/components/RecordIndexActionMenuBarEntry';
import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector'; import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope'; import { ActionBarHotkeyScope } from '@/action-menu/types/ActionBarHotKeyScope';
import { contextStoreNumberOfSelectedRecordsState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { BottomBar } from '@/ui/layout/bottom-bar/components/BottomBar'; import { BottomBar } from '@/ui/layout/bottom-bar/components/BottomBar';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useRecoilValue } from 'recoil';
const StyledLabel = styled.div` const StyledLabel = styled.div`
color: ${({ theme }) => theme.font.color.tertiary}; color: ${({ theme }) => theme.font.color.tertiary};
@ -18,9 +17,9 @@ const StyledLabel = styled.div`
padding-right: ${({ theme }) => theme.spacing(2)}; padding-right: ${({ theme }) => theme.spacing(2)};
`; `;
export const ActionMenuBar = () => { export const RecordIndexActionMenuBar = () => {
const contextStoreNumberOfSelectedRecords = useRecoilValue( const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsState, contextStoreNumberOfSelectedRecordsComponentState,
); );
const actionMenuId = useAvailableComponentInstanceIdOrThrow( const actionMenuId = useAvailableComponentInstanceIdOrThrow(
@ -44,7 +43,7 @@ export const ActionMenuBar = () => {
> >
<StyledLabel>{contextStoreNumberOfSelectedRecords} selected:</StyledLabel> <StyledLabel>{contextStoreNumberOfSelectedRecords} selected:</StyledLabel>
{actionMenuEntries.map((entry, index) => ( {actionMenuEntries.map((entry, index) => (
<ActionMenuBarEntry key={index} entry={entry} /> <RecordIndexActionMenuBarEntry key={index} entry={entry} />
))} ))}
</BottomBar> </BottomBar>
); );

View File

@ -4,7 +4,7 @@ import styled from '@emotion/styled';
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry'; import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent'; import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
type ActionMenuBarEntryProps = { type RecordIndexActionMenuBarEntryProps = {
entry: ActionMenuEntry; entry: ActionMenuEntry;
}; };
@ -35,7 +35,9 @@ const StyledButtonLabel = styled.div`
margin-left: ${({ theme }) => theme.spacing(1)}; margin-left: ${({ theme }) => theme.spacing(1)};
`; `;
export const ActionMenuBarEntry = ({ entry }: ActionMenuBarEntryProps) => { export const RecordIndexActionMenuBarEntry = ({
entry,
}: RecordIndexActionMenuBarEntryProps) => {
const theme = useTheme(); const theme = useTheme();
return ( return (
<StyledButton <StyledButton

View File

@ -3,9 +3,9 @@ import { useRecoilValue } from 'recoil';
import { PositionType } from '../types/PositionType'; import { PositionType } from '../types/PositionType';
import { actionMenuDropdownPositionComponentState } from '@/action-menu/states/actionMenuDropdownPositionComponentState';
import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector'; import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDropdownHotKeyScope'; import { ActionMenuDropdownHotkeyScope } from '@/action-menu/types/ActionMenuDropdownHotKeyScope';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
@ -34,7 +34,7 @@ const StyledContainerActionMenuDropdown = styled.div<StyledContainerProps>`
width: auto; width: auto;
`; `;
export const ActionMenuDropdown = () => { export const RecordIndexActionMenuDropdown = () => {
const actionMenuEntries = useRecoilComponentValueV2( const actionMenuEntries = useRecoilComponentValueV2(
actionMenuEntriesComponentSelector, actionMenuEntriesComponentSelector,
); );
@ -45,7 +45,7 @@ export const ActionMenuDropdown = () => {
const actionMenuDropdownPosition = useRecoilValue( const actionMenuDropdownPosition = useRecoilValue(
extractComponentState( extractComponentState(
actionMenuDropdownPositionComponentState, recordIndexActionMenuDropdownPositionComponentState,
`action-menu-dropdown-${actionMenuId}`, `action-menu-dropdown-${actionMenuId}`,
), ),
); );

View File

@ -1,15 +1,16 @@
import { useActionMenu } from '@/action-menu/hooks/useActionMenu'; import { useActionMenu } from '@/action-menu/hooks/useActionMenu';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { contextStoreNumberOfSelectedRecordsState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow'; import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
export const ActionMenuEffect = () => { export const RecordIndexActionMenuEffect = () => {
const contextStoreNumberOfSelectedRecords = useRecoilValue( const contextStoreNumberOfSelectedRecords = useRecoilComponentValueV2(
contextStoreNumberOfSelectedRecordsState, contextStoreNumberOfSelectedRecordsComponentState,
); );
const actionMenuId = useAvailableComponentInstanceIdOrThrow( const actionMenuId = useAvailableComponentInstanceIdOrThrow(

View File

@ -0,0 +1,31 @@
import { RecordActionMenuEntriesSetter } from '@/action-menu/actions/record-actions/components/RecordActionMenuEntriesSetter';
import { ActionMenuConfirmationModals } from '@/action-menu/components/ActionMenuConfirmationModals';
import { RecordShowActionMenuBar } from '@/action-menu/components/RecordShowActionMenuBar';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const RecordShowActionMenu = ({
actionMenuId,
}: {
actionMenuId: string;
}) => {
const contextStoreCurrentObjectMetadataId = useRecoilComponentValueV2(
contextStoreCurrentObjectMetadataIdComponentState,
);
return (
<>
{contextStoreCurrentObjectMetadataId && (
<ActionMenuComponentInstanceContext.Provider
value={{ instanceId: actionMenuId }}
>
<RecordShowActionMenuBar />
<ActionMenuConfirmationModals />
<RecordActionMenuEntriesSetter actionMenuType="recordShow" />
</ActionMenuComponentInstanceContext.Provider>
)}
</>
);
};

View File

@ -0,0 +1,16 @@
import { RecordShowActionMenuBarEntry } from '@/action-menu/components/RecordShowActionMenuBarEntry';
import { actionMenuEntriesComponentSelector } from '@/action-menu/states/actionMenuEntriesComponentSelector';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
export const RecordShowActionMenuBar = () => {
const actionMenuEntries = useRecoilComponentValueV2(
actionMenuEntriesComponentSelector,
);
return (
<>
{actionMenuEntries.map((actionMenuEntry) => (
<RecordShowActionMenuBarEntry entry={actionMenuEntry} />
))}
</>
);
};

View File

@ -0,0 +1,53 @@
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { ActionMenuEntry } from '@/action-menu/types/ActionMenuEntry';
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
type RecordShowActionMenuBarEntryProps = {
entry: ActionMenuEntry;
};
const StyledButton = styled.div<{ accent: MenuItemAccent }>`
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${(props) =>
props.accent === 'danger'
? props.theme.color.red
: props.theme.font.color.secondary};
cursor: pointer;
display: flex;
justify-content: center;
padding: ${({ theme }) => theme.spacing(2)};
transition: background 0.1s ease;
user-select: none;
&:hover {
background: ${({ theme, accent }) =>
accent === 'danger'
? theme.background.danger
: theme.background.tertiary};
}
`;
const StyledButtonLabel = styled.div`
font-weight: ${({ theme }) => theme.font.weight.medium};
margin-left: ${({ theme }) => theme.spacing(1)};
`;
// For now, this component is the same as RecordIndexActionMenuBarEntry but they
// will probably diverge in the future
export const RecordShowActionMenuBarEntry = ({
entry,
}: RecordShowActionMenuBarEntryProps) => {
const theme = useTheme();
return (
<StyledButton
accent={entry.accent ?? 'default'}
onClick={() => entry.onClick?.()}
>
{entry.Icon && <entry.Icon size={theme.icon.size.md} />}
<StyledButtonLabel>{entry.label}</StyledButtonLabel>
</StyledButton>
);
};

View File

@ -1,116 +0,0 @@
import { expect, jest } from '@storybook/jest';
import { Meta, StoryObj } from '@storybook/react';
import { RecoilRoot } from 'recoil';
import { ActionMenuBar } from '@/action-menu/components/ActionMenuBar';
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { contextStoreNumberOfSelectedRecordsState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsState';
import { contextStoreTargetedRecordsRuleState } from '@/context-store/states/contextStoreTargetedRecordsRuleState';
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
import { userEvent, waitFor, within } from '@storybook/test';
import { IconCheckbox, IconTrash } from 'twenty-ui';
const deleteMock = jest.fn();
const markAsDoneMock = jest.fn();
const meta: Meta<typeof ActionMenuBar> = {
title: 'Modules/ActionMenu/ActionMenuBar',
component: ActionMenuBar,
decorators: [
(Story) => (
<RecoilRoot
initializeState={({ set }) => {
set(contextStoreTargetedRecordsRuleState, {
mode: 'selection',
selectedRecordIds: ['1', '2', '3'],
});
set(contextStoreNumberOfSelectedRecordsState, 3);
set(
actionMenuEntriesComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
new Map([
[
'delete',
{
key: 'delete',
label: 'Delete',
position: 0,
Icon: IconTrash,
onClick: deleteMock,
},
],
[
'markAsDone',
{
key: 'markAsDone',
label: 'Mark as done',
position: 1,
Icon: IconCheckbox,
onClick: markAsDoneMock,
},
],
]),
);
set(
isBottomBarOpenedComponentState.atomFamily({
instanceId: 'action-bar-story-action-menu',
}),
true,
);
}}
>
<ActionMenuComponentInstanceContext.Provider
value={{ instanceId: 'story-action-menu' }}
>
<Story />
</ActionMenuComponentInstanceContext.Provider>
</RecoilRoot>
),
],
args: {
actionMenuId: 'story-action-menu',
},
};
export default meta;
type Story = StoryObj<typeof ActionMenuBar>;
export const Default: Story = {
args: {
actionMenuId: 'story-action-menu',
},
};
export const WithCustomSelection: Story = {
args: {
actionMenuId: 'story-action-menu',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const selectionText = await canvas.findByText('3 selected:');
expect(selectionText).toBeInTheDocument();
},
};
export const WithButtonClicks: Story = {
args: {
actionMenuId: 'story-action-menu',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const deleteButton = await canvas.findByText('Delete');
await userEvent.click(deleteButton);
const markAsDoneButton = await canvas.findByText('Mark as done');
await userEvent.click(markAsDoneButton);
await waitFor(() => {
expect(deleteMock).toHaveBeenCalled();
expect(markAsDoneMock).toHaveBeenCalled();
});
},
};

View File

@ -0,0 +1,131 @@
import { expect, jest } from '@storybook/jest';
import { Meta, StoryObj } from '@storybook/react';
import { RecoilRoot } from 'recoil';
import { RecordIndexActionMenuBar } from '@/action-menu/components/RecordIndexActionMenuBar';
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
import { userEvent, waitFor, within } from '@storybook/test';
import { IconCheckbox, IconTrash } from 'twenty-ui';
const deleteMock = jest.fn();
const markAsDoneMock = jest.fn();
const meta: Meta<typeof RecordIndexActionMenuBar> = {
title: 'Modules/ActionMenu/RecordIndexActionMenuBar',
component: RecordIndexActionMenuBar,
decorators: [
(Story) => (
<ContextStoreComponentInstanceContext.Provider
value={{ instanceId: 'story-action-menu' }}
>
<RecoilRoot
initializeState={({ set }) => {
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
{
mode: 'selection',
selectedRecordIds: ['1', '2', '3'],
},
);
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
3,
);
set(
actionMenuEntriesComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
new Map([
[
'delete',
{
key: 'delete',
label: 'Delete',
position: 0,
Icon: IconTrash,
onClick: deleteMock,
},
],
[
'markAsDone',
{
key: 'markAsDone',
label: 'Mark as done',
position: 1,
Icon: IconCheckbox,
onClick: markAsDoneMock,
},
],
]),
);
set(
isBottomBarOpenedComponentState.atomFamily({
instanceId: 'action-bar-story-action-menu',
}),
true,
);
}}
>
<ActionMenuComponentInstanceContext.Provider
value={{ instanceId: 'story-action-menu' }}
>
<Story />
</ActionMenuComponentInstanceContext.Provider>
</RecoilRoot>
</ContextStoreComponentInstanceContext.Provider>
),
],
args: {
actionMenuId: 'story-action-menu',
},
};
export default meta;
type Story = StoryObj<typeof RecordIndexActionMenuBar>;
export const Default: Story = {
args: {
actionMenuId: 'story-action-menu',
},
};
export const WithCustomSelection: Story = {
args: {
actionMenuId: 'story-action-menu',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const selectionText = await canvas.findByText('3 selected:');
expect(selectionText).toBeInTheDocument();
},
};
export const WithButtonClicks: Story = {
args: {
actionMenuId: 'story-action-menu',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const deleteButton = await canvas.findByText('Delete');
await userEvent.click(deleteButton);
const markAsDoneButton = await canvas.findByText('Mark as done');
await userEvent.click(markAsDoneButton);
await waitFor(() => {
expect(deleteMock).toHaveBeenCalled();
expect(markAsDoneMock).toHaveBeenCalled();
});
},
};

View File

@ -1,19 +1,19 @@
import { RecordIndexActionMenuBarEntry } from '@/action-menu/components/RecordIndexActionMenuBarEntry';
import { expect, jest } from '@storybook/jest'; import { expect, jest } from '@storybook/jest';
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library'; import { userEvent, within } from '@storybook/testing-library';
import { ComponentDecorator, IconCheckbox, IconTrash } from 'twenty-ui'; import { ComponentDecorator, IconCheckbox, IconTrash } from 'twenty-ui';
import { ActionMenuBarEntry } from '../ActionMenuBarEntry';
const meta: Meta<typeof ActionMenuBarEntry> = { const meta: Meta<typeof RecordIndexActionMenuBarEntry> = {
title: 'Modules/ActionMenu/ActionMenuBarEntry', title: 'Modules/ActionMenu/RecordIndexActionMenuBarEntry',
component: ActionMenuBarEntry, component: RecordIndexActionMenuBarEntry,
decorators: [ComponentDecorator], decorators: [ComponentDecorator],
}; };
export default meta; export default meta;
type Story = StoryObj<typeof ActionMenuBarEntry>; type Story = StoryObj<typeof RecordIndexActionMenuBarEntry>;
const deleteMock = jest.fn(); const deleteMock = jest.fn();
const markAsDoneMock = jest.fn(); const markAsDoneMock = jest.fn();

View File

@ -3,10 +3,10 @@ import { Meta, StoryObj } from '@storybook/react';
import { userEvent, within } from '@storybook/testing-library'; import { userEvent, within } from '@storybook/testing-library';
import { RecoilRoot } from 'recoil'; import { RecoilRoot } from 'recoil';
import { ActionMenuDropdown } from '@/action-menu/components/ActionMenuDropdown'; import { RecordIndexActionMenuDropdown } from '@/action-menu/components/RecordIndexActionMenuDropdown';
import { actionMenuDropdownPositionComponentState } from '@/action-menu/states/actionMenuDropdownPositionComponentState';
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState'; import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext'; import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { recordIndexActionMenuDropdownPositionComponentState } from '@/action-menu/states/recordIndexActionMenuDropdownPositionComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState'; import { extractComponentState } from '@/ui/utilities/state/component-state/utils/extractComponentState';
import { IconCheckbox, IconHeart, IconTrash } from 'twenty-ui'; import { IconCheckbox, IconHeart, IconTrash } from 'twenty-ui';
@ -15,16 +15,16 @@ const deleteMock = jest.fn();
const markAsDoneMock = jest.fn(); const markAsDoneMock = jest.fn();
const addToFavoritesMock = jest.fn(); const addToFavoritesMock = jest.fn();
const meta: Meta<typeof ActionMenuDropdown> = { const meta: Meta<typeof RecordIndexActionMenuDropdown> = {
title: 'Modules/ActionMenu/ActionMenuDropdown', title: 'Modules/ActionMenu/RecordIndexActionMenuDropdown',
component: ActionMenuDropdown, component: RecordIndexActionMenuDropdown,
decorators: [ decorators: [
(Story) => ( (Story) => (
<RecoilRoot <RecoilRoot
initializeState={({ set }) => { initializeState={({ set }) => {
set( set(
extractComponentState( extractComponentState(
actionMenuDropdownPositionComponentState, recordIndexActionMenuDropdownPositionComponentState,
'action-menu-dropdown-story', 'action-menu-dropdown-story',
), ),
{ x: 10, y: 10 }, { x: 10, y: 10 },
@ -87,7 +87,7 @@ const meta: Meta<typeof ActionMenuDropdown> = {
export default meta; export default meta;
type Story = StoryObj<typeof ActionMenuDropdown>; type Story = StoryObj<typeof RecordIndexActionMenuDropdown>;
export const Default: Story = { export const Default: Story = {
args: { args: {

View File

@ -0,0 +1,131 @@
import { expect, jest } from '@storybook/jest';
import { Meta, StoryObj } from '@storybook/react';
import { RecoilRoot } from 'recoil';
import { RecordShowActionMenuBar } from '@/action-menu/components/RecordShowActionMenuBar';
import { actionMenuEntriesComponentState } from '@/action-menu/states/actionMenuEntriesComponentState';
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { MenuItemAccent } from '@/ui/navigation/menu-item/types/MenuItemAccent';
import { userEvent, waitFor, within } from '@storybook/test';
import {
ComponentDecorator,
IconFileExport,
IconHeart,
IconTrash,
} from 'twenty-ui';
const deleteMock = jest.fn();
const addToFavoritesMock = jest.fn();
const exportMock = jest.fn();
const meta: Meta<typeof RecordShowActionMenuBar> = {
title: 'Modules/ActionMenu/RecordShowActionMenuBar',
component: RecordShowActionMenuBar,
decorators: [
(Story) => (
<RecoilRoot
initializeState={({ set }) => {
set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
{
mode: 'selection',
selectedRecordIds: ['1'],
},
);
set(
contextStoreNumberOfSelectedRecordsComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
1,
);
set(
actionMenuEntriesComponentState.atomFamily({
instanceId: 'story-action-menu',
}),
new Map([
[
'addToFavorites',
{
key: 'addToFavorites',
label: 'Add to favorites',
position: 0,
Icon: IconHeart,
onClick: addToFavoritesMock,
},
],
[
'export',
{
key: 'export',
label: 'Export',
position: 1,
Icon: IconFileExport,
onClick: exportMock,
},
],
[
'delete',
{
key: 'delete',
label: 'Delete',
position: 2,
Icon: IconTrash,
onClick: deleteMock,
accent: 'danger' as MenuItemAccent,
},
],
]),
);
}}
>
<ActionMenuComponentInstanceContext.Provider
value={{ instanceId: 'story-action-menu' }}
>
<Story />
</ActionMenuComponentInstanceContext.Provider>
</RecoilRoot>
),
ComponentDecorator,
],
args: {
actionMenuId: 'story-action-menu',
},
};
export default meta;
type Story = StoryObj<typeof RecordShowActionMenuBar>;
export const Default: Story = {
args: {
actionMenuId: 'story-action-menu',
},
};
export const WithButtonClicks: Story = {
args: {
actionMenuId: 'story-action-menu',
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const deleteButton = await canvas.findByText('Delete');
await userEvent.click(deleteButton);
const addToFavoritesButton = await canvas.findByText('Add to favorites');
await userEvent.click(addToFavoritesButton);
const exportButton = await canvas.findByText('Export');
await userEvent.click(exportButton);
await waitFor(() => {
expect(deleteMock).toHaveBeenCalled();
expect(addToFavoritesMock).toHaveBeenCalled();
expect(exportMock).toHaveBeenCalled();
});
},
};

View File

@ -1,9 +1,9 @@
import { PositionType } from '@/action-menu/types/PositionType'; import { PositionType } from '@/action-menu/types/PositionType';
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
export const actionMenuDropdownPositionComponentState = export const recordIndexActionMenuDropdownPositionComponentState =
createComponentState<PositionType>({ createComponentState<PositionType>({
key: 'actionMenuDropdownPositionComponentState', key: 'recordIndexActionMenuDropdownPositionComponentState',
defaultValue: { defaultValue: {
x: null, x: null,
y: null, y: null,

View File

@ -0,0 +1 @@
export type ActionMenuType = 'recordIndex' | 'recordShow';

View File

@ -0,0 +1,22 @@
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { mainContextStoreComponentInstanceIdState } from '@/context-store/states/mainContextStoreComponentInstanceId';
import { useContext, useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
export const SetMainContextStoreComponentInstanceIdEffect = () => {
const setMainContextStoreComponentInstanceId = useSetRecoilState(
mainContextStoreComponentInstanceIdState,
);
const context = useContext(ContextStoreComponentInstanceContext);
useEffect(() => {
setMainContextStoreComponentInstanceId(context?.instanceId ?? null);
return () => {
setMainContextStoreComponentInstanceId(null);
};
}, [context, setMainContextStoreComponentInstanceId]);
return null;
};

View File

@ -0,0 +1,9 @@
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const contextStoreCurrentObjectMetadataIdComponentState =
createComponentStateV2<string | null>({
key: 'contextStoreCurrentObjectMetadataIdComponentState',
defaultValue: null,
componentInstanceContext: ContextStoreComponentInstanceContext,
});

View File

@ -1,8 +0,0 @@
import { createState } from 'twenty-ui';
export const contextStoreCurrentObjectMetadataIdState = createState<
string | null
>({
key: 'contextStoreCurrentObjectMetadataIdState',
defaultValue: null,
});

View File

@ -0,0 +1,10 @@
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const contextStoreCurrentViewIdComponentState = createComponentStateV2<
string | null
>({
key: 'contextStoreCurrentViewIdComponentState',
defaultValue: null,
componentInstanceContext: ContextStoreComponentInstanceContext,
});

View File

@ -1,6 +0,0 @@
import { createState } from 'twenty-ui';
export const contextStoreCurrentViewIdState = createState<string | null>({
key: 'contextStoreCurrentViewIdState',
defaultValue: null,
});

View File

@ -0,0 +1,9 @@
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
export const contextStoreNumberOfSelectedRecordsComponentState =
createComponentStateV2<number>({
key: 'contextStoreNumberOfSelectedRecordsComponentState',
defaultValue: 0,
componentInstanceContext: ContextStoreComponentInstanceContext,
});

View File

@ -1,6 +0,0 @@
import { createState } from 'twenty-ui';
export const contextStoreNumberOfSelectedRecordsState = createState<number>({
key: 'contextStoreNumberOfSelectedRecordsState',
defaultValue: 0,
});

View File

@ -1,5 +1,6 @@
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { Filter } from '@/object-record/object-filter-dropdown/types/Filter'; import { Filter } from '@/object-record/object-filter-dropdown/types/Filter';
import { createState } from 'twenty-ui'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2';
type ContextStoreTargetedRecordsRuleSelectionMode = { type ContextStoreTargetedRecordsRuleSelectionMode = {
mode: 'selection'; mode: 'selection';
@ -16,11 +17,12 @@ export type ContextStoreTargetedRecordsRule =
| ContextStoreTargetedRecordsRuleSelectionMode | ContextStoreTargetedRecordsRuleSelectionMode
| ContextStoreTargetedRecordsRuleExclusionMode; | ContextStoreTargetedRecordsRuleExclusionMode;
export const contextStoreTargetedRecordsRuleState = export const contextStoreTargetedRecordsRuleComponentState =
createState<ContextStoreTargetedRecordsRule>({ createComponentStateV2<ContextStoreTargetedRecordsRule>({
key: 'contextStoreTargetedRecordsRuleState', key: 'contextStoreTargetedRecordsRuleComponentState',
defaultValue: { defaultValue: {
mode: 'selection', mode: 'selection',
selectedRecordIds: [], selectedRecordIds: [],
}, },
componentInstanceContext: ContextStoreComponentInstanceContext,
}); });

View File

@ -0,0 +1,4 @@
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
export const ContextStoreComponentInstanceContext =
createComponentInstanceContext();

View File

@ -0,0 +1,8 @@
import { createState } from 'twenty-ui';
export const mainContextStoreComponentInstanceIdState = createState<
string | null
>({
key: 'mainContextStoreComponentInstanceIdState',
defaultValue: null,
});

View File

@ -1,4 +1,4 @@
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleState'; import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters'; import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
import { ViewFilterOperand } from '@/views/types/ViewFilterOperand'; import { ViewFilterOperand } from '@/views/types/ViewFilterOperand';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';

View File

@ -1,4 +1,4 @@
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleState'; import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter'; import { RecordGqlOperationFilter } from '@/object-record/graphql/types/RecordGqlOperationFilter';
import { turnFiltersIntoQueryFilter } from '@/object-record/record-filter/utils/turnFiltersIntoQueryFilter'; import { turnFiltersIntoQueryFilter } from '@/object-record/record-filter/utils/turnFiltersIntoQueryFilter';

View File

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

View File

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

View File

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

View File

@ -1,22 +1,23 @@
import { contextStoreNumberOfSelectedRecordsState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsState'; import { contextStoreNumberOfSelectedRecordsComponentState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsComponentState';
import { contextStoreTargetedRecordsRuleState } from '@/context-store/states/contextStoreTargetedRecordsRuleState'; import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters'; import { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords'; import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext';
import { useFindManyParams } from '@/object-record/record-index/hooks/useLoadRecordIndexTable'; 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 { useContext, useEffect } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil';
export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect = export const RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect =
() => { () => {
const setContextStoreNumberOfSelectedRecords = useSetRecoilState( const setContextStoreNumberOfSelectedRecords = useSetRecoilComponentStateV2(
contextStoreNumberOfSelectedRecordsState, contextStoreNumberOfSelectedRecordsComponentState,
); );
const contextStoreTargetedRecordsRule = useRecoilValue( const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleState, contextStoreTargetedRecordsRuleComponentState,
); );
const { objectNamePlural } = useContext(RecordIndexRootPropsContext); 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 { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { RecordIndexRootPropsContext } from '@/object-record/record-index/contexts/RecordIndexRootPropsContext'; 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 { useContext, useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
export const RecordIndexContainerContextStoreObjectMetadataEffect = () => { export const RecordIndexContainerContextStoreObjectMetadataEffect = () => {
const setContextStoreCurrentObjectMetadataItem = useSetRecoilState( const setContextStoreCurrentObjectMetadataItem = useSetRecoilComponentStateV2(
contextStoreCurrentObjectMetadataIdState, contextStoreCurrentObjectMetadataIdComponentState,
); );
const { objectNamePlural } = useContext(RecordIndexRootPropsContext); const { objectNamePlural } = useContext(RecordIndexRootPropsContext);

View File

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

View File

@ -130,6 +130,7 @@ const mocks: MockedResponse[] = [
const WrapperWithResponse = getJestMetadataAndApolloMocksAndContextStoreWrapper( const WrapperWithResponse = getJestMetadataAndApolloMocksAndContextStoreWrapper(
{ {
apolloMocks: mocks, apolloMocks: mocks,
componentInstanceId: 'recordIndexId',
contextStoreTargetedRecordsRule: { contextStoreTargetedRecordsRule: {
mode: 'selection', mode: 'selection',
selectedRecordIds: [], selectedRecordIds: [],
@ -155,6 +156,7 @@ const graphqlEmptyResponse = [
const WrapperWithEmptyResponse = const WrapperWithEmptyResponse =
getJestMetadataAndApolloMocksAndContextStoreWrapper({ getJestMetadataAndApolloMocksAndContextStoreWrapper({
apolloMocks: graphqlEmptyResponse, apolloMocks: graphqlEmptyResponse,
componentInstanceId: 'recordIndexId',
contextStoreTargetedRecordsRule: { contextStoreTargetedRecordsRule: {
mode: 'selection', mode: 'selection',
selectedRecordIds: [], selectedRecordIds: [],
@ -207,7 +209,6 @@ describe('useRecordData', () => {
objectMetadataItem, objectMetadataItem,
callback, callback,
pageSize: 30, pageSize: 30,
delayMs: 0, delayMs: 0,
}), }),
{ wrapper: WrapperWithResponse }, { 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 { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { isDefined } from '~/utils/isDefined'; 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 { computeContextStoreFilters } from '@/context-store/utils/computeContextStoreFilters';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { useLazyFindManyRecords } from '@/object-record/hooks/useLazyFindManyRecords'; 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 { useFindManyParams } from '@/object-record/record-index/hooks/useLoadRecordIndexTable';
import { EXPORT_TABLE_DATA_DEFAULT_PAGE_SIZE } from '@/object-record/record-index/options/constants/ExportTableDataDefaultPageSize'; 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 { 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'; import { ViewType } from '@/views/types/ViewType';
export const sleep = (ms: number) => export const sleep = (ms: number) =>
@ -75,8 +76,8 @@ export const useRecordData = ({
); );
const columns = useRecoilValue(visibleTableColumnsSelector()); const columns = useRecoilValue(visibleTableColumnsSelector());
const contextStoreTargetedRecordsRule = useRecoilValue( const contextStoreTargetedRecordsRule = useRecoilComponentValueV2(
contextStoreTargetedRecordsRuleState, contextStoreTargetedRecordsRuleComponentState,
); );
const queryFilter = computeContextStoreFilters( 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 { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { ShowPageContainer } from '@/ui/layout/page/components/ShowPageContainer'; 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 { useRecordShowContainerData } from '@/object-record/record-show/hooks/useRecordShowContainerData';
import { useRecordShowContainerTabs } from '@/object-record/record-show/hooks/useRecordShowContainerTabs'; import { useRecordShowContainerTabs } from '@/object-record/record-show/hooks/useRecordShowContainerTabs';
import { ShowPageSubContainer } from '@/ui/layout/show-page/components/ShowPageSubContainer'; import { ShowPageSubContainer } from '@/ui/layout/show-page/components/ShowPageSubContainer';
@ -38,7 +41,16 @@ export const RecordShowContainer = ({
); );
return ( return (
<> <ContextStoreComponentInstanceContext.Provider
value={{
instanceId: 'record-show',
}}
>
<RecordShowContainerContextStoreEffect
recordId={objectRecordId}
objectNameSingular={objectNameSingular}
/>
{!isInRightDrawer && <SetMainContextStoreComponentInstanceIdEffect />}
{recordFromStore && recordFromStore.deletedAt && ( {recordFromStore && recordFromStore.deletedAt && (
<InformationBannerDeletedRecord <InformationBannerDeletedRecord
recordId={objectRecordId} recordId={objectRecordId}
@ -57,6 +69,6 @@ export const RecordShowContainer = ({
isNewRightDrawerItemLoading={isNewRightDrawerItemLoading} isNewRightDrawerItemLoading={isNewRightDrawerItemLoading}
/> />
</ShowPageContainer> </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 { 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 { isRowSelectedComponentFamilyState } from '@/object-record/record-table/record-table-row/states/isRowSelectedComponentFamilyState';
import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState'; import { isBottomBarOpenedComponentState } from '@/ui/layout/bottom-bar/states/isBottomBarOpenedComponentState';
import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState';
@ -23,7 +23,7 @@ export const useTriggerActionMenuDropdown = ({
set( set(
extractComponentState( extractComponentState(
actionMenuDropdownPositionComponentState, recordIndexActionMenuDropdownPositionComponentState,
`action-menu-dropdown-${recordTableId}`, `action-menu-dropdown-${recordTableId}`,
), ),
{ {

View File

@ -7,7 +7,7 @@ import { useDebouncedCallback } from 'use-debounce';
import { Button, ButtonAccent } from '@/ui/input/button/components/Button'; import { Button, ButtonAccent } from '@/ui/input/button/components/Button';
import { TextInput } from '@/ui/input/components/TextInput'; import { TextInput } from '@/ui/input/components/TextInput';
import { Modal } from '@/ui/layout/modal/components/Modal'; import { Modal, ModalVariants } from '@/ui/layout/modal/components/Modal';
import { import {
Section, Section,
SectionAlignment, SectionAlignment,
@ -26,6 +26,7 @@ export type ConfirmationModalProps = {
confirmationValue?: string; confirmationValue?: string;
confirmButtonAccent?: ButtonAccent; confirmButtonAccent?: ButtonAccent;
AdditionalButtons?: React.ReactNode; AdditionalButtons?: React.ReactNode;
modalVariant?: ModalVariants;
}; };
const StyledConfirmationModal = styled(Modal)` const StyledConfirmationModal = styled(Modal)`
@ -71,6 +72,7 @@ export const ConfirmationModal = ({
confirmationPlaceholder, confirmationPlaceholder,
confirmButtonAccent = 'danger', confirmButtonAccent = 'danger',
AdditionalButtons, AdditionalButtons,
modalVariant = 'primary',
}: ConfirmationModalProps) => { }: ConfirmationModalProps) => {
const [inputConfirmationValue, setInputConfirmationValue] = const [inputConfirmationValue, setInputConfirmationValue] =
useState<string>(''); useState<string>('');
@ -113,6 +115,7 @@ export const ConfirmationModal = ({
onEnter={handleEnter} onEnter={handleEnter}
isClosable={true} isClosable={true}
padding="large" padding="large"
modalVariant={modalVariant}
> >
<StyledCenteredTitle> <StyledCenteredTitle>
<H1Title title={title} fontColor={H1TitleFontColor.Primary} /> <H1Title title={title} fontColor={H1TitleFontColor.Primary} />

View File

@ -94,7 +94,9 @@ const StyledBackDrop = styled(motion.div)<{
background: ${({ theme, modalVariant }) => background: ${({ theme, modalVariant }) =>
modalVariant === 'primary' modalVariant === 'primary'
? theme.background.overlayPrimary ? theme.background.overlayPrimary
: theme.background.overlaySecondary}; : modalVariant === 'secondary'
? theme.background.overlaySecondary
: theme.background.overlayTertiary};
display: flex; display: flex;
height: 100%; height: 100%;
justify-content: center; justify-content: center;
@ -132,7 +134,7 @@ const ModalFooter = ({ children, className }: ModalFooterProps) => (
export type ModalSize = 'small' | 'medium' | 'large'; export type ModalSize = 'small' | 'medium' | 'large';
export type ModalPadding = 'none' | 'small' | 'medium' | 'large'; export type ModalPadding = 'none' | 'small' | 'medium' | 'large';
export type ModalVariants = 'primary' | 'secondary'; export type ModalVariants = 'primary' | 'secondary' | 'tertiary';
export type ModalProps = React.PropsWithChildren & { export type ModalProps = React.PropsWithChildren & {
size?: ModalSize; size?: ModalSize;

View File

@ -1,52 +0,0 @@
import { ReactNode } from 'react';
import styled from '@emotion/styled';
import { MOBILE_VIEWPORT } from 'twenty-ui';
import { RightDrawer } from '@/ui/layout/right-drawer/components/RightDrawer';
import { PagePanel } from './PagePanel';
type RightDrawerContainerProps = {
children: ReactNode;
};
const StyledMainContainer = styled.div`
background: ${({ theme }) => theme.background.noisy};
box-sizing: border-box;
display: flex;
flex: 1 1 auto;
flex-direction: row;
gap: ${({ theme }) => theme.spacing(2)};
min-height: 0;
padding-bottom: ${({ theme }) => theme.spacing(3)};
padding-right: ${({ theme }) => theme.spacing(3)};
padding-left: 0;
width: 100%;
@media (max-width: ${MOBILE_VIEWPORT}px) {
padding-left: ${({ theme }) => theme.spacing(3)};
padding-bottom: 0;
}
`;
type LeftContainerProps = {
isRightDrawerOpen?: boolean;
};
const StyledLeftContainer = styled.div<LeftContainerProps>`
display: flex;
flex-direction: column;
position: relative;
width: 100%;
`;
export const RightDrawerContainer = ({
children,
}: RightDrawerContainerProps) => (
<StyledMainContainer>
<StyledLeftContainer>
<PagePanel>{children}</PagePanel>
</StyledLeftContainer>
<RightDrawer />
</StyledMainContainer>
);

View File

@ -1,3 +1,4 @@
import { RecordShowActionMenu } from '@/action-menu/components/RecordShowActionMenu';
import { Calendar } from '@/activities/calendar/components/Calendar'; import { Calendar } from '@/activities/calendar/components/Calendar';
import { EmailThreads } from '@/activities/emails/components/EmailThreads'; import { EmailThreads } from '@/activities/emails/components/EmailThreads';
import { Attachments } from '@/activities/files/components/Attachments'; import { Attachments } from '@/activities/files/components/Attachments';
@ -6,13 +7,11 @@ import { ObjectTasks } from '@/activities/tasks/components/ObjectTasks';
import { TimelineActivities } from '@/activities/timeline-activities/components/TimelineActivities'; import { TimelineActivities } from '@/activities/timeline-activities/components/TimelineActivities';
import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useDeleteOneRecord } from '@/object-record/hooks/useDeleteOneRecord';
import { isNewViewableRecordLoadingState } from '@/object-record/record-right-drawer/states/isNewViewableRecordLoading'; import { isNewViewableRecordLoadingState } from '@/object-record/record-right-drawer/states/isNewViewableRecordLoading';
import { FieldsCard } from '@/object-record/record-show/components/FieldsCard'; import { FieldsCard } from '@/object-record/record-show/components/FieldsCard';
import { SummaryCard } from '@/object-record/record-show/components/SummaryCard'; import { SummaryCard } from '@/object-record/record-show/components/SummaryCard';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState'; import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { Button } from '@/ui/input/button/components/Button';
import { ShowPageActivityContainer } from '@/ui/layout/show-page/components/ShowPageActivityContainer'; import { ShowPageActivityContainer } from '@/ui/layout/show-page/components/ShowPageActivityContainer';
import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer'; import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer';
import { SingleTabProps, TabList } from '@/ui/layout/tab/components/TabList'; import { SingleTabProps, TabList } from '@/ui/layout/tab/components/TabList';
@ -25,9 +24,7 @@ import { WorkflowVersionVisualizerEffect } from '@/workflow/components/WorkflowV
import { WorkflowVisualizer } from '@/workflow/components/WorkflowVisualizer'; import { WorkflowVisualizer } from '@/workflow/components/WorkflowVisualizer';
import { WorkflowVisualizerEffect } from '@/workflow/components/WorkflowVisualizerEffect'; import { WorkflowVisualizerEffect } from '@/workflow/components/WorkflowVisualizerEffect';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecoilState, useRecoilValue } from 'recoil';
import { IconTrash } from 'twenty-ui';
const StyledShowPageRightContainer = styled.div<{ isMobile: boolean }>` const StyledShowPageRightContainer = styled.div<{ isMobile: boolean }>`
display: flex; display: flex;
@ -198,18 +195,6 @@ export const ShowPageSubContainer = ({
} }
}; };
const [isDeleting, setIsDeleting] = useState(false);
const { deleteOneRecord } = useDeleteOneRecord({
objectNameSingular: targetableObject.targetObjectNameSingular,
});
const handleDelete = async () => {
setIsDeleting(true);
await deleteOneRecord(targetableObject.id);
setIsDeleting(false);
};
const [recordFromStore] = useRecoilState<ObjectRecord | null>( const [recordFromStore] = useRecoilState<ObjectRecord | null>(
recordStoreFamilyState(targetableObject.id), recordStoreFamilyState(targetableObject.id),
); );
@ -236,12 +221,7 @@ export const ShowPageSubContainer = ({
</StyledContentContainer> </StyledContentContainer>
{isInRightDrawer && recordFromStore && !recordFromStore.deletedAt && ( {isInRightDrawer && recordFromStore && !recordFromStore.deletedAt && (
<StyledButtonContainer> <StyledButtonContainer>
<Button <RecordShowActionMenu actionMenuId={'right-drawer-action-menu'} />
Icon={IconTrash}
onClick={handleDelete}
disabled={isDeleting}
title={isDeleting ? 'Deleting...' : 'Delete'}
></Button>
</StyledButtonContainer> </StyledButtonContainer>
)} )}
</StyledShowPageRightContainer> </StyledShowPageRightContainer>

View File

@ -1,14 +1,14 @@
import { contextStoreCurrentViewIdState } from '@/context-store/states/contextStoreCurrentViewIdState'; import { contextStoreCurrentViewIdComponentState } from '@/context-store/states/contextStoreCurrentViewIdComponentState';
import { useLastVisitedObjectMetadataItem } from '@/navigation/hooks/useLastVisitedObjectMetadataItem'; import { useLastVisitedObjectMetadataItem } from '@/navigation/hooks/useLastVisitedObjectMetadataItem';
import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView'; import { useLastVisitedView } from '@/navigation/hooks/useLastVisitedView';
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams'; import { useViewFromQueryParams } from '@/views/hooks/internal/useViewFromQueryParams';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView'; import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState'; import { currentViewIdComponentState } from '@/views/states/currentViewIdComponentState';
import { isUndefined } from '@sniptt/guards'; import { isUndefined } from '@sniptt/guards';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { isDefined } from '~/utils/isDefined'; import { isDefined } from '~/utils/isDefined';
@ -39,8 +39,8 @@ export const QueryParamsViewIdEffect = () => {
objectMetadataItemId?.id, objectMetadataItemId?.id,
lastVisitedObjectMetadataItemId, lastVisitedObjectMetadataItemId,
); );
const setContextStoreCurrentViewId = useSetRecoilState( const setContextStoreCurrentViewId = useSetRecoilComponentStateV2(
contextStoreCurrentViewIdState, contextStoreCurrentViewIdComponentState,
); );
// // TODO: scope view bar per view id if possible // // TODO: scope view bar per view id if possible

View File

@ -1,6 +1,8 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { SetMainContextStoreComponentInstanceIdEffect } from '@/context-store/components/SetMainContextStoreComponentInstanceIdEffect';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural'; import { useObjectNameSingularFromPlural } from '@/object-metadata/hooks/useObjectNameSingularFromPlural';
import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId'; import { lastShowPageRecordIdState } from '@/object-record/record-field/states/lastShowPageRecordId';
@ -73,9 +75,16 @@ export const RecordIndexPage = () => {
<RecordIndexPageHeader /> <RecordIndexPageHeader />
<PageBody> <PageBody>
<StyledIndexContainer> <StyledIndexContainer>
<RecordIndexContainerContextStoreObjectMetadataEffect /> <ContextStoreComponentInstanceContext.Provider
<RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect /> value={{
<RecordIndexContainer /> instanceId: 'record-index',
}}
>
<RecordIndexContainerContextStoreObjectMetadataEffect />
<RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect />
<SetMainContextStoreComponentInstanceIdEffect />
<RecordIndexContainer />
</ContextStoreComponentInstanceContext.Provider>
</StyledIndexContainer> </StyledIndexContainer>
</PageBody> </PageBody>
</RecordIndexRootPropsContext.Provider> </RecordIndexRootPropsContext.Provider>

View File

@ -12,7 +12,6 @@ import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle';
import { RecordShowPageWorkflowHeader } from '@/workflow/components/RecordShowPageWorkflowHeader'; import { RecordShowPageWorkflowHeader } from '@/workflow/components/RecordShowPageWorkflowHeader';
import { RecordShowPageWorkflowVersionHeader } from '@/workflow/components/RecordShowPageWorkflowVersionHeader'; import { RecordShowPageWorkflowVersionHeader } from '@/workflow/components/RecordShowPageWorkflowVersionHeader';
import { RecordShowPageBaseHeader } from '~/pages/object-record/RecordShowPageBaseHeader'; import { RecordShowPageBaseHeader } from '~/pages/object-record/RecordShowPageBaseHeader';
import { RecordShowPageContextStoreEffect } from '~/pages/object-record/RecordShowPageContextStoreEffect';
import { RecordShowPageHeader } from '~/pages/object-record/RecordShowPageHeader'; import { RecordShowPageHeader } from '~/pages/object-record/RecordShowPageHeader';
export const RecordShowPage = () => { export const RecordShowPage = () => {
@ -40,7 +39,6 @@ export const RecordShowPage = () => {
return ( return (
<RecordFieldValueSelectorContextProvider> <RecordFieldValueSelectorContextProvider>
<RecordValueSetterEffect recordId={objectRecordId} /> <RecordValueSetterEffect recordId={objectRecordId} />
<RecordShowPageContextStoreEffect recordId={objectRecordId} />
<PageContainer> <PageContainer>
<PageTitle title={pageTitle} /> <PageTitle title={pageTitle} />
<RecordShowPageHeader <RecordShowPageHeader

View File

@ -1,57 +0,0 @@
import { contextStoreCurrentObjectMetadataIdState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdState';
import { contextStoreNumberOfSelectedRecordsState } from '@/context-store/states/contextStoreNumberOfSelectedRecordsState';
import { contextStoreTargetedRecordsRuleState } from '@/context-store/states/contextStoreTargetedRecordsRuleState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { useSetRecoilState } from 'recoil';
export const RecordShowPageContextStoreEffect = ({
recordId,
}: {
recordId: string;
}) => {
const setContextStoreTargetedRecordsRule = useSetRecoilState(
contextStoreTargetedRecordsRuleState,
);
const setContextStoreCurrentObjectMetadataId = useSetRecoilState(
contextStoreCurrentObjectMetadataIdState,
);
const { objectNameSingular } = useParams();
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular: objectNameSingular ?? '',
});
const setContextStoreNumberOfSelectedRecords = useSetRecoilState(
contextStoreNumberOfSelectedRecordsState,
);
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,12 +1,12 @@
import { ReactNode, useEffect, useState } from 'react'; import { ReactNode, useEffect, useState } from 'react';
import { useSetRecoilState } from 'recoil';
import { contextStoreCurrentObjectMetadataIdState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdState'; import { contextStoreCurrentObjectMetadataIdComponentState } from '@/context-store/states/contextStoreCurrentObjectMetadataIdComponentState';
import { import {
ContextStoreTargetedRecordsRule, ContextStoreTargetedRecordsRule,
contextStoreTargetedRecordsRuleState, contextStoreTargetedRecordsRuleComponentState,
} from '@/context-store/states/contextStoreTargetedRecordsRuleState'; } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
export const JestContextStoreSetter = ({ export const JestContextStoreSetter = ({
contextStoreTargetedRecordsRule = { contextStoreTargetedRecordsRule = {
@ -20,11 +20,12 @@ export const JestContextStoreSetter = ({
contextStoreCurrentObjectMetadataNameSingular?: string; contextStoreCurrentObjectMetadataNameSingular?: string;
children: ReactNode; children: ReactNode;
}) => { }) => {
const setContextStoreTargetedRecordsRule = useSetRecoilState( const setContextStoreTargetedRecordsRule = useSetRecoilComponentStateV2(
contextStoreTargetedRecordsRuleState, contextStoreTargetedRecordsRuleComponentState,
); );
const setContextStoreCurrentObjectMetadataId = useSetRecoilState(
contextStoreCurrentObjectMetadataIdState, const setContextStoreCurrentObjectMetadataId = useSetRecoilComponentStateV2(
contextStoreCurrentObjectMetadataIdComponentState,
); );
const { objectMetadataItem } = useObjectMetadataItem({ const { objectMetadataItem } = useObjectMetadataItem({

View File

@ -1,4 +1,5 @@
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleState'; import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { ContextStoreTargetedRecordsRule } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { MockedResponse } from '@apollo/client/testing'; import { MockedResponse } from '@apollo/client/testing';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { MutableSnapshot } from 'recoil'; import { MutableSnapshot } from 'recoil';
@ -10,6 +11,7 @@ export const getJestMetadataAndApolloMocksAndContextStoreWrapper = ({
onInitializeRecoilSnapshot, onInitializeRecoilSnapshot,
contextStoreTargetedRecordsRule, contextStoreTargetedRecordsRule,
contextStoreCurrentObjectMetadataNameSingular, contextStoreCurrentObjectMetadataNameSingular,
componentInstanceId,
}: { }: {
apolloMocks: apolloMocks:
| readonly MockedResponse<Record<string, any>, Record<string, any>>[] | readonly MockedResponse<Record<string, any>, Record<string, any>>[]
@ -17,6 +19,7 @@ export const getJestMetadataAndApolloMocksAndContextStoreWrapper = ({
onInitializeRecoilSnapshot?: (snapshot: MutableSnapshot) => void; onInitializeRecoilSnapshot?: (snapshot: MutableSnapshot) => void;
contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule; contextStoreTargetedRecordsRule?: ContextStoreTargetedRecordsRule;
contextStoreCurrentObjectMetadataNameSingular?: string; contextStoreCurrentObjectMetadataNameSingular?: string;
componentInstanceId: string;
}) => { }) => {
const Wrapper = getJestMetadataAndApolloMocksWrapper({ const Wrapper = getJestMetadataAndApolloMocksWrapper({
apolloMocks, apolloMocks,
@ -24,14 +27,20 @@ export const getJestMetadataAndApolloMocksAndContextStoreWrapper = ({
}); });
return ({ children }: { children: ReactNode }) => ( return ({ children }: { children: ReactNode }) => (
<Wrapper> <Wrapper>
<JestContextStoreSetter <ContextStoreComponentInstanceContext.Provider
contextStoreTargetedRecordsRule={contextStoreTargetedRecordsRule} value={{
contextStoreCurrentObjectMetadataNameSingular={ instanceId: componentInstanceId,
contextStoreCurrentObjectMetadataNameSingular }}
}
> >
{children} <JestContextStoreSetter
</JestContextStoreSetter> contextStoreTargetedRecordsRule={contextStoreTargetedRecordsRule}
contextStoreCurrentObjectMetadataNameSingular={
contextStoreCurrentObjectMetadataNameSingular
}
>
{children}
</JestContextStoreSetter>
</ContextStoreComponentInstanceContext.Provider>
</Wrapper> </Wrapper>
); );
}; };

View File

@ -23,8 +23,9 @@ export const BACKGROUND_DARK = {
lighter: RGBA(GRAY_SCALE.gray0, 0.03), lighter: RGBA(GRAY_SCALE.gray0, 0.03),
danger: RGBA(COLOR.red, 0.08), danger: RGBA(COLOR.red, 0.08),
}, },
overlayPrimary: RGBA(GRAY_SCALE.gray80, 0.8), overlayPrimary: RGBA(GRAY_SCALE.gray90, 0.8),
overlaySecondary: RGBA(GRAY_SCALE.gray80, 0.4), overlaySecondary: RGBA(GRAY_SCALE.gray90, 0.4),
overlayTertiary: RGBA(GRAY_SCALE.gray90, 0.4),
radialGradient: `radial-gradient(50% 62.62% at 50% 0%, #505050 0%, ${GRAY_SCALE.gray60} 100%)`, radialGradient: `radial-gradient(50% 62.62% at 50% 0%, #505050 0%, ${GRAY_SCALE.gray60} 100%)`,
radialGradientHover: `radial-gradient(76.32% 95.59% at 50% 0%, #505050 0%, ${GRAY_SCALE.gray60} 100%)`, radialGradientHover: `radial-gradient(76.32% 95.59% at 50% 0%, #505050 0%, ${GRAY_SCALE.gray60} 100%)`,
primaryInverted: GRAY_SCALE.gray20, primaryInverted: GRAY_SCALE.gray20,

View File

@ -23,8 +23,9 @@ export const BACKGROUND_LIGHT = {
lighter: RGBA(GRAY_SCALE.gray100, 0.02), lighter: RGBA(GRAY_SCALE.gray100, 0.02),
danger: RGBA(COLOR.red, 0.08), danger: RGBA(COLOR.red, 0.08),
}, },
overlayPrimary: RGBA(GRAY_SCALE.gray80, 0.8), overlayPrimary: RGBA(GRAY_SCALE.gray90, 0.8),
overlaySecondary: RGBA(GRAY_SCALE.gray80, 0.4), overlaySecondary: RGBA(GRAY_SCALE.gray90, 0.4),
overlayTertiary: RGBA(GRAY_SCALE.gray90, 0.08),
radialGradient: `radial-gradient(50% 62.62% at 50% 0%, #505050 0%, ${GRAY_SCALE.gray60} 100%)`, radialGradient: `radial-gradient(50% 62.62% at 50% 0%, #505050 0%, ${GRAY_SCALE.gray60} 100%)`,
radialGradientHover: `radial-gradient(76.32% 95.59% at 50% 0%, #505050 0%, ${GRAY_SCALE.gray60} 100%)`, radialGradientHover: `radial-gradient(76.32% 95.59% at 50% 0%, #505050 0%, ${GRAY_SCALE.gray60} 100%)`,
primaryInverted: GRAY_SCALE.gray60, primaryInverted: GRAY_SCALE.gray60,