Various fixes (#11108)

Fixes many bug regarding TableCell and InlineCells
This commit is contained in:
Charles Bochet
2025-03-22 14:19:10 +01:00
committed by GitHub
parent 692e08f0d4
commit ccf60284cf
61 changed files with 473 additions and 374 deletions

View File

@ -17,6 +17,7 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell'; import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox'; import { PropertyBox } from '@/object-record/record-inline-cell/property-box/components/PropertyBox';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { mapArrayToObject } from '~/utils/array/mapArrayToObject'; import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
import { beautifyPastDateRelativeToNow } from '~/utils/date-utils'; import { beautifyPastDateRelativeToNow } from '~/utils/date-utils';
@ -102,7 +103,6 @@ export const CalendarEventDetails = ({
value={{ value={{
recordId: calendarEvent.id, recordId: calendarEvent.id,
hotkeyScope: 'calendar-event-details', hotkeyScope: 'calendar-event-details',
recoilScopeId: `${calendarEvent.id}-${fieldName}`,
isLabelIdentifier: false, isLabelIdentifier: false,
fieldDefinition: formatFieldMetadataItemAsFieldDefinition({ fieldDefinition: formatFieldMetadataItemAsFieldDefinition({
field: fieldsByName[fieldName], field: fieldsByName[fieldName],
@ -116,7 +116,7 @@ export const CalendarEventDetails = ({
> >
<RecordFieldComponentInstanceContext.Provider <RecordFieldComponentInstanceContext.Provider
value={{ value={{
instanceId: `${calendarEvent.id}-${fieldName}`, instanceId: getRecordFieldInputId(calendarEvent.id, fieldName),
}} }}
> >
<RecordInlineCell readonly /> <RecordInlineCell readonly />

View File

@ -14,7 +14,7 @@ import {
import { TextInput } from '@/ui/input/components/TextInput'; import { TextInput } from '@/ui/input/components/TextInput';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useMemo, useState } from 'react'; import { useState } from 'react';
import { isDefined } from 'twenty-shared'; import { isDefined } from 'twenty-shared';
import { import {
IconCalendar, IconCalendar,
@ -94,11 +94,6 @@ export const AttachmentRow = ({
const [attachmentFileName, setAttachmentFileName] = const [attachmentFileName, setAttachmentFileName] =
useState(originalFileName); useState(originalFileName);
const fieldContext = useMemo(
() => ({ recoilScopeId: attachment?.id ?? '' }),
[attachment?.id],
);
const { destroyOneRecord: destroyOneAttachment } = useDestroyOneRecord({ const { destroyOneRecord: destroyOneAttachment } = useDestroyOneRecord({
objectNameSingular: CoreObjectNameSingular.Attachment, objectNameSingular: CoreObjectNameSingular.Attachment,
}); });
@ -161,7 +156,13 @@ export const AttachmentRow = ({
}; };
return ( return (
<FieldContext.Provider value={fieldContext as GenericFieldContextType}> <FieldContext.Provider
value={
{
recordId: attachment.id,
} as GenericFieldContextType
}
>
<ActivityRow disabled> <ActivityRow disabled>
<StyledLeftContent> <StyledLeftContent>
<AttachmentIcon attachmentType={attachment.type} /> <AttachmentIcon attachmentType={attachment.type} />

View File

@ -1,23 +1,21 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { Key } from 'ts-key-enum';
import { IconArrowUpRight, IconPencil } from 'twenty-ui'; import { IconArrowUpRight, IconPencil } from 'twenty-ui';
import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips'; import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips';
import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords'; import { useActivityTargetObjectRecords } from '@/activities/hooks/useActivityTargetObjectRecords';
import { useOpenActivityTargetInlineCellEditMode } from '@/activities/inline-cell/hooks/useOpenActivityTargetInlineCellEditMode'; import { useOpenActivityTargetInlineCellEditMode } from '@/activities/inline-cell/hooks/useOpenActivityTargetInlineCellEditMode';
import { useUpdateActivityTargetFromInlineCell } from '@/activities/inline-cell/hooks/useUpdateActivityTargetFromInlineCell'; import { useUpdateActivityTargetFromInlineCell } from '@/activities/inline-cell/hooks/useUpdateActivityTargetFromInlineCell';
import { ActivityEditorHotkeyScope } from '@/activities/types/ActivityEditorHotkeyScope';
import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular';
import { useFieldContext } from '@/object-record/hooks/useFieldContext'; import { useFieldContext } from '@/object-record/hooks/useFieldContext';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { FieldFocusContextProvider } from '@/object-record/record-field/contexts/FieldFocusContextProvider'; import { FieldFocusContextProvider } from '@/object-record/record-field/contexts/FieldFocusContextProvider';
import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly'; import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly';
import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { RecordInlineCellContainer } from '@/object-record/record-inline-cell/components/RecordInlineCellContainer'; import { RecordInlineCellContainer } from '@/object-record/record-inline-cell/components/RecordInlineCellContainer';
import { RecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext'; import { RecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell'; import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
import { MultipleRecordPicker } from '@/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker'; import { MultipleRecordPicker } from '@/object-record/record-picker/multiple-record-picker/components/MultipleRecordPicker';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys'; import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope';
type ActivityTargetsInlineCellProps = { type ActivityTargetsInlineCellProps = {
activityRecordId: string; activityRecordId: string;
@ -26,6 +24,7 @@ type ActivityTargetsInlineCellProps = {
activityObjectNameSingular: activityObjectNameSingular:
| CoreObjectNameSingular.Note | CoreObjectNameSingular.Note
| CoreObjectNameSingular.Task; | CoreObjectNameSingular.Task;
componentInstanceId: string;
}; };
export const ActivityTargetsInlineCell = ({ export const ActivityTargetsInlineCell = ({
@ -33,26 +32,17 @@ export const ActivityTargetsInlineCell = ({
showLabel = true, showLabel = true,
maxWidth, maxWidth,
activityObjectNameSingular, activityObjectNameSingular,
componentInstanceId,
}: ActivityTargetsInlineCellProps) => { }: ActivityTargetsInlineCellProps) => {
const { activityTargetObjectRecords } = const { activityTargetObjectRecords } =
useActivityTargetObjectRecords(activityRecordId); useActivityTargetObjectRecords(activityRecordId);
const multipleRecordPickerInstanceId = `multiple-record-picker-target-${activityRecordId}`; const { closeInlineCell } = useInlineCell(componentInstanceId);
const { closeInlineCell } = useInlineCell();
const { fieldDefinition } = useContext(FieldContext); const { fieldDefinition } = useContext(FieldContext);
const isFieldReadOnly = useIsFieldValueReadOnly(); const isFieldReadOnly = useIsFieldValueReadOnly();
useScopedHotkeys(
Key.Escape,
() => {
closeInlineCell();
},
ActivityEditorHotkeyScope.ActivityTargets,
);
const { FieldContextProvider: ActivityTargetsContextProvider } = const { FieldContextProvider: ActivityTargetsContextProvider } =
useFieldContext({ useFieldContext({
objectNameSingular: activityObjectNameSingular, objectNameSingular: activityObjectNameSingular,
@ -72,7 +62,11 @@ export const ActivityTargetsInlineCell = ({
}); });
return ( return (
<RecordFieldInputScope recordFieldInputScopeId={activityRecordId}> <RecordFieldComponentInstanceContext.Provider
value={{
instanceId: componentInstanceId,
}}
>
<FieldFocusContextProvider> <FieldFocusContextProvider>
{ActivityTargetsContextProvider && ( {ActivityTargetsContextProvider && (
<ActivityTargetsContextProvider> <ActivityTargetsContextProvider>
@ -80,20 +74,20 @@ export const ActivityTargetsInlineCell = ({
value={{ value={{
buttonIcon: IconPencil, buttonIcon: IconPencil,
customEditHotkeyScope: customEditHotkeyScope:
ActivityEditorHotkeyScope.ActivityTargets, MultipleRecordPickerHotkeyScope.MultipleRecordPicker,
IconLabel: showLabel ? IconArrowUpRight : undefined, IconLabel: showLabel ? IconArrowUpRight : undefined,
showLabel: showLabel, showLabel: showLabel,
readonly: isFieldReadOnly, readonly: isFieldReadOnly,
labelWidth: fieldDefinition?.labelWidth, labelWidth: fieldDefinition?.labelWidth,
editModeContent: ( editModeContent: (
<MultipleRecordPicker <MultipleRecordPicker
componentInstanceId={multipleRecordPickerInstanceId} componentInstanceId={componentInstanceId}
onClickOutside={() => { onClickOutside={() => {
closeInlineCell(); closeInlineCell();
}} }}
onChange={(morphItem) => { onChange={(morphItem) => {
updateActivityTargetFromInlineCell({ updateActivityTargetFromInlineCell({
recordPickerInstanceId: multipleRecordPickerInstanceId, recordPickerInstanceId: componentInstanceId,
morphItem, morphItem,
activityTargetWithTargetRecords: activityTargetWithTargetRecords:
activityTargetObjectRecords, activityTargetObjectRecords,
@ -113,7 +107,7 @@ export const ActivityTargetsInlineCell = ({
), ),
onOpenEditMode: () => { onOpenEditMode: () => {
openActivityTargetInlineCellEditMode({ openActivityTargetInlineCellEditMode({
recordPickerInstanceId: multipleRecordPickerInstanceId, recordPickerInstanceId: componentInstanceId,
activityTargetObjectRecords, activityTargetObjectRecords,
}); });
}, },
@ -124,6 +118,6 @@ export const ActivityTargetsInlineCell = ({
</ActivityTargetsContextProvider> </ActivityTargetsContextProvider>
)} )}
</FieldFocusContextProvider> </FieldFocusContextProvider>
</RecordFieldInputScope> </RecordFieldComponentInstanceContext.Provider>
); );
}; };

View File

@ -96,6 +96,7 @@ export const NoteCard = ({
{NoteTargetsContextProvider && ( {NoteTargetsContextProvider && (
<NoteTargetsContextProvider> <NoteTargetsContextProvider>
<ActivityTargetsInlineCell <ActivityTargetsInlineCell
componentInstanceId={`note-card-${note.id}-targets`}
activityRecordId={note.id} activityRecordId={note.id}
activityObjectNameSingular={CoreObjectNameSingular.Note} activityObjectNameSingular={CoreObjectNameSingular.Note}
/> />

View File

@ -135,6 +135,7 @@ export const TaskRow = ({ task }: { task: Task }) => {
activityRecordId={task.id} activityRecordId={task.id}
showLabel={false} showLabel={false}
maxWidth={200} maxWidth={200}
componentInstanceId={`task-row-targets-${task.id}`}
/> />
</TaskTargetsContextProvider> </TaskTargetsContextProvider>
)} )}

View File

@ -1,5 +1,4 @@
export enum ActivityEditorHotkeyScope { export enum ActivityEditorHotkeyScope {
ActivityTitle = 'activity-title', ActivityTitle = 'activity-title',
ActivityBody = 'activity-body', ActivityBody = 'activity-body',
ActivityTargets = 'activity-targets',
} }

View File

@ -22,6 +22,8 @@ import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/reco
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext'; import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext'; import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId'; import { getRecordIndexIdFromObjectNamePluralAndViewId } from '@/object-record/utils/getRecordIndexIdFromObjectNamePluralAndViewId';
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
@ -29,7 +31,7 @@ import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { AnimatePresence, motion } from 'framer-motion'; import { AnimatePresence, motion } from 'framer-motion';
import { useRef } from 'react'; import { useRef } from 'react';
import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
import { useIsMobile } from 'twenty-ui'; import { useIsMobile } from 'twenty-ui';
import { FeatureFlagKey } from '~/generated-metadata/graphql'; import { FeatureFlagKey } from '~/generated-metadata/graphql';
@ -65,9 +67,23 @@ export const CommandMenuContainer = ({
useCommandMenuHotKeys(); useCommandMenuHotKeys();
const handleClickOutside = useRecoilCallback(
({ snapshot }) =>
() => {
const hotkeyScope = snapshot
.getLoadable(currentHotkeyScopeState)
.getValue();
if (hotkeyScope.scope === AppHotkeyScope.CommandMenuOpen) {
closeCommandMenu();
}
},
[closeCommandMenu],
);
useListenClickOutside({ useListenClickOutside({
refs: [commandMenuRef], refs: [commandMenuRef],
callback: closeCommandMenu, callback: handleClickOutside,
listenerId: 'COMMAND_MENU_LISTENER_ID', listenerId: 'COMMAND_MENU_LISTENER_ID',
excludeClassNames: ['page-header-command-menu-button'], excludeClassNames: ['page-header-command-menu-button'],
}); });

View File

@ -77,7 +77,12 @@ export const RecordChip = ({
avatarType={recordChipData.avatarType} avatarType={recordChipData.avatarType}
avatarUrl={recordChipData.avatarUrl ?? ''} avatarUrl={recordChipData.avatarUrl ?? ''}
className={className} className={className}
variant={variant} variant={
variant ??
(!forceDisableClick
? AvatarChipVariant.Regular
: AvatarChipVariant.Transparent)
}
to={to ?? getLinkToShowPage(objectNameSingular, record)} to={to ?? getLinkToShowPage(objectNameSingular, record)}
onClick={(clickEvent) => { onClick={(clickEvent) => {
// TODO refactor wrapper event listener to avoid colliding events // TODO refactor wrapper event listener to avoid colliding events

View File

@ -59,7 +59,6 @@ export const useFieldContext = ({
key={objectRecordId + fieldMetadataItem.id} key={objectRecordId + fieldMetadataItem.id}
value={{ value={{
recordId: objectRecordId, recordId: objectRecordId,
recoilScopeId: objectRecordId + fieldMetadataItem.id,
isLabelIdentifier, isLabelIdentifier,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({ fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem, field: fieldMetadataItem,

View File

@ -13,6 +13,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
import { getFieldButtonIcon } from '@/object-record/record-field/utils/getFieldButtonIcon'; import { getFieldButtonIcon } from '@/object-record/record-field/utils/getFieldButtonIcon';
import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell'; import { RecordInlineCell } from '@/object-record/record-inline-cell/components/RecordInlineCell';
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope'; import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { useContext } from 'react'; import { useContext } from 'react';
export const RecordBoardCardBody = ({ export const RecordBoardCardBody = ({
@ -43,7 +44,6 @@ export const RecordBoardCardBody = ({
value={{ value={{
recordId, recordId,
maxWidth: 156, maxWidth: 156,
recoilScopeId: `board-card-${recordId}-${fieldDefinition.fieldMetadataId}`,
isLabelIdentifier: false, isLabelIdentifier: false,
fieldDefinition: { fieldDefinition: {
disableTooltip: false, disableTooltip: false,
@ -64,7 +64,11 @@ export const RecordBoardCardBody = ({
> >
<RecordFieldComponentInstanceContext.Provider <RecordFieldComponentInstanceContext.Provider
value={{ value={{
instanceId: `board-card-${recordId}-${fieldDefinition.fieldMetadataId}`, instanceId: getRecordFieldInputId(
recordId,
fieldDefinition.metadata.fieldName,
'record-board-card',
),
}} }}
> >
<RecordInlineCell /> <RecordInlineCell />

View File

@ -10,11 +10,8 @@ import { PhonesFieldInput } from '@/object-record/record-field/meta-types/input/
import { RawJsonFieldInput } from '@/object-record/record-field/meta-types/input/components/RawJsonFieldInput'; import { RawJsonFieldInput } from '@/object-record/record-field/meta-types/input/components/RawJsonFieldInput';
import { RelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/components/RelationFromManyFieldInput'; import { RelationFromManyFieldInput } from '@/object-record/record-field/meta-types/input/components/RelationFromManyFieldInput';
import { SelectFieldInput } from '@/object-record/record-field/meta-types/input/components/SelectFieldInput'; import { SelectFieldInput } from '@/object-record/record-field/meta-types/input/components/SelectFieldInput';
import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope';
import { isFieldPhones } from '@/object-record/record-field/types/guards/isFieldPhones'; import { isFieldPhones } from '@/object-record/record-field/types/guards/isFieldPhones';
import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects'; import { isFieldRelationFromManyObjects } from '@/object-record/record-field/types/guards/isFieldRelationFromManyObjects';
import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject';
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
import { ArrayFieldInput } from '@/object-record/record-field/meta-types/input/components/ArrayFieldInput'; import { ArrayFieldInput } from '@/object-record/record-field/meta-types/input/components/ArrayFieldInput';
import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress'; import { isFieldAddress } from '@/object-record/record-field/types/guards/isFieldAddress';
@ -30,6 +27,7 @@ import { isFieldMultiSelect } from '@/object-record/record-field/types/guards/is
import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber'; import { isFieldNumber } from '@/object-record/record-field/types/guards/isFieldNumber';
import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating'; import { isFieldRating } from '@/object-record/record-field/types/guards/isFieldRating';
import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson'; import { isFieldRawJson } from '@/object-record/record-field/types/guards/isFieldRawJson';
import { isFieldRelationToOneObject } from '@/object-record/record-field/types/guards/isFieldRelationToOneObject';
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
import { FieldContext } from '../contexts/FieldContext'; import { FieldContext } from '../contexts/FieldContext';
import { BooleanFieldInput } from '../meta-types/input/components/BooleanFieldInput'; import { BooleanFieldInput } from '../meta-types/input/components/BooleanFieldInput';
@ -58,7 +56,6 @@ type FieldInputProps = {
}; };
export const FieldInput = ({ export const FieldInput = ({
recordFieldInputdId,
onCancel, onCancel,
onSubmit, onSubmit,
onEnter, onEnter,
@ -71,9 +68,7 @@ export const FieldInput = ({
const { fieldDefinition } = useContext(FieldContext); const { fieldDefinition } = useContext(FieldContext);
return ( return (
<RecordFieldInputScope <>
recordFieldInputScopeId={getScopeIdFromComponentId(recordFieldInputdId)}
>
{isFieldRelationToOneObject(fieldDefinition) ? ( {isFieldRelationToOneObject(fieldDefinition) ? (
<RelationToOneFieldInput onSubmit={onSubmit} onCancel={onCancel} /> <RelationToOneFieldInput onSubmit={onSubmit} onCancel={onCancel} />
) : isFieldRelationFromManyObjects(fieldDefinition) ? ( ) : isFieldRelationFromManyObjects(fieldDefinition) ? (
@ -173,6 +168,6 @@ export const FieldInput = ({
) : ( ) : (
<></> <></>
)} )}
</RecordFieldInputScope> </>
); );
}; };

View File

@ -23,7 +23,6 @@ export type GenericFieldContextType = {
fieldDefinition: FieldDefinition<FieldMetadata>; fieldDefinition: FieldDefinition<FieldMetadata>;
useUpdateRecord?: RecordUpdateHook; useUpdateRecord?: RecordUpdateHook;
recordId: string; recordId: string;
recoilScopeId?: string;
hotkeyScope: string; hotkeyScope: string;
isLabelIdentifier: boolean; isLabelIdentifier: boolean;
labelIdentifierLink?: string; labelIdentifierLink?: string;

View File

@ -1,22 +0,0 @@
import { RecordFieldInputScopeInternalContext } from '@/object-record/record-field/scopes/scope-internal-context/RecordFieldInputScopeInternalContext';
import { recordFieldInputDraftValueComponentSelector } from '@/object-record/record-field/states/selectors/recordFieldInputDraftValueComponentSelector';
import { FieldInputDraftValue } from '@/object-record/record-field/types/FieldInputDraftValue';
import { useAvailableScopeIdOrThrow } from '@/ui/utilities/recoil-scope/scopes-internal/hooks/useAvailableScopeId';
import { getScopeIdOrUndefinedFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdOrUndefinedFromComponentId';
import { extractComponentSelector } from '@/ui/utilities/state/component-state/utils/extractComponentSelector';
export const useRecordFieldInputStates = <FieldValue>(
recordFieldInputId?: string,
) => {
const scopeId = useAvailableScopeIdOrThrow(
RecordFieldInputScopeInternalContext,
getScopeIdOrUndefinedFromComponentId(recordFieldInputId),
);
return {
scopeId,
getDraftValueSelector: extractComponentSelector<
FieldInputDraftValue<FieldValue> | undefined
>(recordFieldInputDraftValueComponentSelector, scopeId),
};
};

View File

@ -9,7 +9,6 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
import { computeDraftValueFromFieldValue } from '@/object-record/record-field/utils/computeDraftValueFromFieldValue'; import { computeDraftValueFromFieldValue } from '@/object-record/record-field/utils/computeDraftValueFromFieldValue';
import { computeDraftValueFromString } from '@/object-record/record-field/utils/computeDraftValueFromString'; import { computeDraftValueFromString } from '@/object-record/record-field/utils/computeDraftValueFromString';
import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector'; import { recordStoreFamilySelector } from '@/object-record/record-store/states/selectors/recordStoreFamilySelector';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { extractComponentSelector } from '@/ui/utilities/state/component-state/utils/extractComponentSelector'; import { extractComponentSelector } from '@/ui/utilities/state/component-state/utils/extractComponentSelector';
export const useInitDraftValueV2 = <FieldValue>() => { export const useInitDraftValueV2 = <FieldValue>() => {
@ -19,19 +18,19 @@ export const useInitDraftValueV2 = <FieldValue>() => {
value, value,
recordId, recordId,
fieldDefinition, fieldDefinition,
fieldComponentInstanceId,
}: { }: {
value?: string; value?: string;
recordId: string; recordId: string;
fieldDefinition: FieldDefinition<FieldMetadata>; fieldDefinition: FieldDefinition<FieldMetadata>;
fieldComponentInstanceId: string;
}) => { }) => {
const recordFieldInputScopeId = `${getRecordFieldInputId(
recordId,
fieldDefinition?.metadata?.fieldName,
)}`;
const getDraftValueSelector = extractComponentSelector< const getDraftValueSelector = extractComponentSelector<
FieldInputDraftValue<FieldValue> | undefined FieldInputDraftValue<FieldValue> | undefined
>(recordFieldInputDraftValueComponentSelector, recordFieldInputScopeId); >(
recordFieldInputDraftValueComponentSelector,
fieldComponentInstanceId,
);
const recordFieldValue = snapshot const recordFieldValue = snapshot
.getLoadable( .getLoadable(

View File

@ -1,13 +1,22 @@
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
import { useRecordFieldInputStates } from '@/object-record/record-field/hooks/internal/useRecordFieldInputStates'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { recordFieldInputDraftValueComponentSelector } from '@/object-record/record-field/states/selectors/recordFieldInputDraftValueComponentSelector';
import { FieldInputDraftValue } from '@/object-record/record-field/types/FieldInputDraftValue'; import { FieldInputDraftValue } from '@/object-record/record-field/types/FieldInputDraftValue';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { extractComponentSelector } from '@/ui/utilities/state/component-state/utils/extractComponentSelector';
export const useRecordFieldInput = <FieldValue>( export const useRecordFieldInput = <FieldValue>() => {
recordFieldInputId?: string, const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
) => { RecordFieldComponentInstanceContext,
const { scopeId, getDraftValueSelector } = );
useRecordFieldInputStates<FieldValue>(recordFieldInputId);
const getDraftValueSelector = extractComponentSelector<
FieldInputDraftValue<FieldValue> | undefined
>(
recordFieldInputDraftValueComponentSelector,
recordFieldComponentInstanceId,
);
const setDraftValue = useSetRecoilState(getDraftValueSelector()); const setDraftValue = useSetRecoilState(getDraftValueSelector());
@ -26,7 +35,6 @@ export const useRecordFieldInput = <FieldValue>(
}; };
return { return {
scopeId,
setDraftValue, setDraftValue,
getDraftValueSelector, getDraftValueSelector,
isDraftValueEmpty, isDraftValueEmpty,

View File

@ -19,7 +19,6 @@ export const FieldContextProvider = ({
value={{ value={{
recordId: recordId ?? '1', recordId: recordId ?? '1',
isLabelIdentifier: false, isLabelIdentifier: false,
recoilScopeId: '1',
hotkeyScope: 'hotkey-scope', hotkeyScope: 'hotkey-scope',
fieldDefinition, fieldDefinition,
useUpdateRecord: () => [() => undefined, {}], useUpdateRecord: () => [() => undefined, {}],

View File

@ -41,7 +41,7 @@ export const useAddressField = () => {
}; };
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldAddressValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldAddressValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -56,7 +56,7 @@ export const useCurrencyField = () => {
}; };
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldCurrencyValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldCurrencyValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -25,9 +25,7 @@ export const useDateField = () => {
}), }),
); );
const { setDraftValue } = useRecordFieldInput<FieldDateValue>( const { setDraftValue } = useRecordFieldInput<FieldDateValue>();
`${recordId}-${fieldName}`,
);
return { return {
fieldDefinition, fieldDefinition,

View File

@ -29,9 +29,7 @@ export const useDateTimeField = () => {
}), }),
); );
const { setDraftValue } = useRecordFieldInput<FieldDateTimeValue>( const { setDraftValue } = useRecordFieldInput<FieldDateTimeValue>();
`${recordId}-${fieldName}`,
);
return { return {
fieldDefinition, fieldDefinition,

View File

@ -27,7 +27,7 @@ export const useEmailsField = () => {
); );
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldEmailsValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldEmailsValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -41,7 +41,7 @@ export const useFullNameField = () => {
}; };
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldFullNameValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldFullNameValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -43,7 +43,7 @@ export const useJsonField = () => {
}; };
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldJsonValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldJsonValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -27,7 +27,7 @@ export const useLinksField = () => {
); );
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldLinksValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldLinksValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -35,7 +35,7 @@ export const useMultiSelectField = () => {
const persistField = usePersistField(); const persistField = usePersistField();
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldMultiSelectValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldMultiSelectValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());
return { return {

View File

@ -55,7 +55,7 @@ export const useNumberField = () => {
}; };
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldNumberValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldNumberValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -27,7 +27,7 @@ export const usePhonesField = () => {
); );
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldPhonesValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldPhonesValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -28,9 +28,8 @@ export const useRelationField = <T extends ObjectRecord | ObjectRecord[]>() => {
recordStoreFamilySelector({ recordId, fieldName }), recordStoreFamilySelector({ recordId, fieldName }),
); );
const { getDraftValueSelector } = useRecordFieldInput<FieldRelationValue<T>>( const { getDraftValueSelector } =
`${recordId}-${fieldName}`, useRecordFieldInput<FieldRelationValue<T>>();
);
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());
const initialSearchValue = draftValue; const initialSearchValue = draftValue;

View File

@ -35,7 +35,7 @@ export const useRichTextField = () => {
const fieldRichTextValue = isFieldRichTextValue(fieldValue) ? fieldValue : ''; const fieldRichTextValue = isFieldRichTextValue(fieldValue) ? fieldValue : '';
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldRichTextValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldRichTextValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -40,7 +40,7 @@ export const useRichTextV2Field = () => {
: ({ blocknote: null, markdown: null } as FieldRichTextV2Value); : ({ blocknote: null, markdown: null } as FieldRichTextV2Value);
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldRichTextValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldRichTextValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -30,7 +30,7 @@ export const useSelectField = () => {
const persistField = usePersistField(); const persistField = usePersistField();
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldSelectValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldSelectValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());
return { return {

View File

@ -28,7 +28,7 @@ export const useTextField = () => {
const fieldTextValue = isFieldTextValue(fieldValue) ? fieldValue : ''; const fieldTextValue = isFieldTextValue(fieldValue) ? fieldValue : '';
const { setDraftValue, getDraftValueSelector } = const { setDraftValue, getDraftValueSelector } =
useRecordFieldInput<FieldTextValue>(`${recordId}-${fieldName}`); useRecordFieldInput<FieldTextValue>();
const draftValue = useRecoilValue(getDraftValueSelector()); const draftValue = useRecoilValue(getDraftValueSelector());

View File

@ -12,6 +12,8 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider'; import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
const AddressValueSetterEffect = ({ const AddressValueSetterEffect = ({
value, value,
@ -49,32 +51,42 @@ const AddressInputWithContext = ({
return ( return (
<div> <div>
<FieldContextProvider <RecordFieldComponentInstanceContext.Provider
fieldDefinition={{ value={{
fieldMetadataId: 'text', instanceId: getRecordFieldInputId(
label: 'Address', recordId ?? '',
type: FieldMetadataType.ADDRESS, 'Address',
iconName: 'IconTag', 'record-table-cell',
metadata: { ),
fieldName: 'Address',
placeHolder: 'Enter text',
objectMetadataNameSingular: 'person',
},
}} }}
recordId={recordId}
> >
<AddressValueSetterEffect value={value} /> <FieldContextProvider
<AddressInput fieldDefinition={{
onEnter={onEnter} fieldMetadataId: 'text',
onEscape={onEscape} label: 'Address',
onClickOutside={onClickOutside} type: FieldMetadataType.ADDRESS,
value={value} iconName: 'IconTag',
hotkeyScope="hotkey-scope" metadata: {
onTab={onTab} fieldName: 'Address',
onShiftTab={onShiftTab} placeHolder: 'Enter text',
/> objectMetadataNameSingular: 'person',
</FieldContextProvider> },
<div data-testid="data-field-input-click-outside-div" /> }}
recordId={recordId}
>
<AddressValueSetterEffect value={value} />
<AddressInput
onEnter={onEnter}
onEscape={onEscape}
onClickOutside={onClickOutside}
value={value}
hotkeyScope="hotkey-scope"
onTab={onTab}
onShiftTab={onShiftTab}
/>
</FieldContextProvider>
<div data-testid="data-field-input-click-outside-div" />
</RecordFieldComponentInstanceContext.Provider>
</div> </div>
); );
}; };

View File

@ -7,6 +7,8 @@ import { recordStoreFamilyState } from '@/object-record/record-store/states/reco
import { FieldMetadataType } from '~/generated/graphql'; import { FieldMetadataType } from '~/generated/graphql';
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider'; import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { import {
BooleanFieldInput, BooleanFieldInput,
BooleanFieldInputProps, BooleanFieldInputProps,
@ -39,23 +41,36 @@ const BooleanFieldInputWithContext = ({
onSubmit, onSubmit,
}: BooleanFieldInputWithContextProps) => { }: BooleanFieldInputWithContextProps) => {
return ( return (
<FieldContextProvider <RecordFieldComponentInstanceContext.Provider
fieldDefinition={{ value={{
defaultValue: false, instanceId: getRecordFieldInputId(
fieldMetadataId: 'boolean', recordId ?? '',
label: 'Boolean', 'Boolean',
iconName: 'Icon123', 'record-table-cell',
type: FieldMetadataType.BOOLEAN, ),
metadata: {
fieldName: 'Boolean',
objectMetadataNameSingular: 'person',
},
}} }}
recordId={recordId}
> >
<BooleanFieldValueSetterEffect value={value} recordId={recordId ?? ''} /> <FieldContextProvider
<BooleanFieldInput onSubmit={onSubmit} testId="boolean-field-input" /> fieldDefinition={{
</FieldContextProvider> defaultValue: false,
fieldMetadataId: 'boolean',
label: 'Boolean',
iconName: 'Icon123',
type: FieldMetadataType.BOOLEAN,
metadata: {
fieldName: 'Boolean',
objectMetadataNameSingular: 'person',
},
}}
recordId={recordId}
>
<BooleanFieldValueSetterEffect
value={value}
recordId={recordId ?? ''}
/>
<BooleanFieldInput onSubmit={onSubmit} testId="boolean-field-input" />
</FieldContextProvider>
</RecordFieldComponentInstanceContext.Provider>
); );
}; };

View File

@ -6,6 +6,8 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
import { FieldMetadataType } from '~/generated/graphql'; import { FieldMetadataType } from '~/generated/graphql';
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider'; import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect'; import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect';
import { useDateTimeField } from '../../../hooks/useDateTimeField'; import { useDateTimeField } from '../../../hooks/useDateTimeField';
import { import {
@ -66,7 +68,15 @@ const DateFieldInputWithContext = ({
}, [setHotkeyScope]); }, [setHotkeyScope]);
return ( return (
<div> <RecordFieldComponentInstanceContext.Provider
value={{
instanceId: getRecordFieldInputId(
recordId ?? '',
'Date',
'record-table-cell',
),
}}
>
<FieldContextProvider <FieldContextProvider
fieldDefinition={{ fieldDefinition={{
fieldMetadataId: 'date', fieldMetadataId: 'date',
@ -90,7 +100,7 @@ const DateFieldInputWithContext = ({
/> />
</FieldContextProvider> </FieldContextProvider>
<div data-testid="data-field-input-click-outside-div"></div> <div data-testid="data-field-input-click-outside-div"></div>
</div> </RecordFieldComponentInstanceContext.Provider>
); );
}; };

View File

@ -7,6 +7,8 @@ import { FieldMetadataType } from '~/generated/graphql';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider'; import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect'; import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { useNumberField } from '../../../hooks/useNumberField'; import { useNumberField } from '../../../hooks/useNumberField';
@ -43,7 +45,15 @@ const NumberFieldInputWithContext = ({
}, [setHotKeyScope]); }, [setHotKeyScope]);
return ( return (
<div> <RecordFieldComponentInstanceContext.Provider
value={{
instanceId: getRecordFieldInputId(
recordId ?? '',
'Number',
'record-table-cell',
),
}}
>
<FieldContextProvider <FieldContextProvider
fieldDefinition={{ fieldDefinition={{
fieldMetadataId: 'number', fieldMetadataId: 'number',
@ -69,7 +79,7 @@ const NumberFieldInputWithContext = ({
/> />
</FieldContextProvider> </FieldContextProvider>
<div data-testid="data-field-input-click-outside-div" /> <div data-testid="data-field-input-click-outside-div" />
</div> </RecordFieldComponentInstanceContext.Provider>
); );
}; };

View File

@ -7,6 +7,8 @@ import { isDefined } from 'twenty-shared';
import { FieldMetadataType } from '~/generated-metadata/graphql'; import { FieldMetadataType } from '~/generated-metadata/graphql';
import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider'; import { FieldContextProvider } from '@/object-record/record-field/meta-types/components/FieldContextProvider';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { FieldRatingValue } from '../../../../types/FieldMetadata'; import { FieldRatingValue } from '../../../../types/FieldMetadata';
import { useRatingField } from '../../../hooks/useRatingField'; import { useRatingField } from '../../../hooks/useRatingField';
import { RatingFieldInput, RatingFieldInputProps } from '../RatingFieldInput'; import { RatingFieldInput, RatingFieldInputProps } from '../RatingFieldInput';
@ -42,22 +44,32 @@ const RatingFieldInputWithContext = ({
}, [setHotKeyScope]); }, [setHotKeyScope]);
return ( return (
<FieldContextProvider <RecordFieldComponentInstanceContext.Provider
fieldDefinition={{ value={{
fieldMetadataId: 'rating', instanceId: getRecordFieldInputId(
label: 'Rating', recordId ?? '',
type: FieldMetadataType.RATING, 'Rating',
iconName: 'Icon123', 'record-table-cell',
metadata: { ),
fieldName: 'Rating',
objectMetadataNameSingular: 'person',
},
}} }}
recordId={recordId}
> >
<RatingFieldValueSetterEffect value={value} /> <FieldContextProvider
<RatingFieldInput onSubmit={onSubmit} /> fieldDefinition={{
</FieldContextProvider> fieldMetadataId: 'rating',
label: 'Rating',
type: FieldMetadataType.RATING,
iconName: 'Icon123',
metadata: {
fieldName: 'Rating',
objectMetadataNameSingular: 'person',
},
}}
recordId={recordId}
>
<RatingFieldValueSetterEffect value={value} />
<RatingFieldInput onSubmit={onSubmit} />
</FieldContextProvider>
</RecordFieldComponentInstanceContext.Provider>
); );
}; };

View File

@ -1,13 +1,14 @@
import { Decorator, Meta, StoryObj } from '@storybook/react';
import { expect, fn, userEvent, waitFor, within } from '@storybook/test'; import { expect, fn, userEvent, waitFor, within } from '@storybook/test';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope'; import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { FieldMetadataType } from '~/generated/graphql'; import { FieldMetadataType } from '~/generated/graphql';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { Decorator, Meta, StoryObj } from '@storybook/react';
import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect'; import { StorybookFieldInputDropdownFocusIdSetterEffect } from '~/testing/components/StorybookFieldInputDropdownFocusIdSetterEffect';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { FieldContextProvider } from '../../../components/FieldContextProvider'; import { FieldContextProvider } from '../../../components/FieldContextProvider';
import { useTextField } from '../../../hooks/useTextField'; import { useTextField } from '../../../hooks/useTextField';
import { TextFieldInput, TextFieldInputProps } from '../TextFieldInput'; import { TextFieldInput, TextFieldInputProps } from '../TextFieldInput';
@ -43,7 +44,11 @@ const TextFieldInputWithContext = ({
}, [setHotKeyScope]); }, [setHotKeyScope]);
return ( return (
<div> <RecordFieldComponentInstanceContext.Provider
value={{
instanceId: 'record-field-component-instance-id',
}}
>
<FieldContextProvider <FieldContextProvider
fieldDefinition={{ fieldDefinition={{
fieldMetadataId: 'text', fieldMetadataId: 'text',
@ -69,7 +74,7 @@ const TextFieldInputWithContext = ({
/> />
</FieldContextProvider> </FieldContextProvider>
<div data-testid="data-field-input-click-outside-div" /> <div data-testid="data-field-input-click-outside-div" />
</div> </RecordFieldComponentInstanceContext.Provider>
); );
}; };

View File

@ -1,21 +0,0 @@
import { ReactNode } from 'react';
import { RecordFieldInputScopeInternalContext } from '@/object-record/record-field/scopes/scope-internal-context/RecordFieldInputScopeInternalContext';
type RecordFieldInputScopeProps = {
children: ReactNode;
recordFieldInputScopeId: string;
};
export const RecordFieldInputScope = ({
children,
recordFieldInputScopeId,
}: RecordFieldInputScopeProps) => {
return (
<RecordFieldInputScopeInternalContext.Provider
value={{ scopeId: recordFieldInputScopeId }}
>
{children}
</RecordFieldInputScopeInternalContext.Provider>
);
};

View File

@ -1,7 +0,0 @@
import { createScopeInternalContext } from '@/ui/utilities/recoil-scope/scopes-internal/utils/createScopeInternalContext';
import { RecoilComponentStateKey } from '@/ui/utilities/state/component-state/types/RecoilComponentStateKey';
type RecordFieldInputScopeInternalContextProps = RecoilComponentStateKey;
export const RecordFieldInputScopeInternalContext =
createScopeInternalContext<RecordFieldInputScopeInternalContextProps>();

View File

@ -12,15 +12,19 @@ import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFie
import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly'; import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly';
import { useOpenFieldInputEditMode } from '@/object-record/record-field/hooks/useOpenFieldInputEditMode'; import { useOpenFieldInputEditMode } from '@/object-record/record-field/hooks/useOpenFieldInputEditMode';
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput'; import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition'; import { FieldDefinition } from '@/object-record/record-field/types/FieldDefinition';
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'; import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation'; import { isFieldRelation } from '@/object-record/record-field/types/guards/isFieldRelation';
import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect'; import { isFieldSelect } from '@/object-record/record-field/types/guards/isFieldSelect';
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell'; import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
import { InlineCellHotkeyScope } from '@/object-record/record-inline-cell/types/InlineCellHotkeyScope';
import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope'; import { MultipleRecordPickerHotkeyScope } from '@/object-record/record-picker/multiple-record-picker/types/MultipleRecordPickerHotkeyScope';
import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope'; import { SingleRecordPickerHotkeyScope } from '@/object-record/record-picker/single-record-picker/types/SingleRecordPickerHotkeyScope';
import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope'; import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId'; import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useRecoilCallback } from 'recoil';
import { RelationDefinitionType } from '~/generated-metadata/graphql'; import { RelationDefinitionType } from '~/generated-metadata/graphql';
import { RecordInlineCellContainer } from './RecordInlineCellContainer'; import { RecordInlineCellContainer } from './RecordInlineCellContainer';
import { import {
@ -42,6 +46,10 @@ export const RecordInlineCell = ({ loading }: RecordInlineCellProps) => {
onCloseEditMode, onCloseEditMode,
} = useContext(FieldContext); } = useContext(FieldContext);
const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
RecordFieldComponentInstanceContext,
);
const buttonIcon = useGetButtonIcon(); const buttonIcon = useGetButtonIcon();
const isFieldInputOnly = useIsFieldInputOnly(); const isFieldInputOnly = useIsFieldInputOnly();
@ -78,15 +86,22 @@ export const RecordInlineCell = ({ loading }: RecordInlineCellProps) => {
closeInlineCell(); closeInlineCell();
}; };
const handleClickOutside: FieldInputClickOutsideEvent = ( const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback(
persistField, ({ snapshot }) =>
event, (persistField, event) => {
) => { const hotkeyScope = snapshot
event.stopImmediatePropagation(); .getLoadable(currentHotkeyScopeState)
.getValue();
if (hotkeyScope.scope !== InlineCellHotkeyScope.InlineCell) {
return;
}
event.stopImmediatePropagation();
persistField(); persistField();
closeInlineCell(); closeInlineCell();
}; },
[closeInlineCell],
);
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const { openFieldInput, closeFieldInput } = useOpenFieldInputEditMode(); const { openFieldInput, closeFieldInput } = useOpenFieldInputEditMode();
@ -132,10 +147,7 @@ export const RecordInlineCell = ({ loading }: RecordInlineCellProps) => {
isCentered, isCentered,
editModeContent: ( editModeContent: (
<FieldInput <FieldInput
recordFieldInputdId={getRecordFieldInputId( recordFieldInputdId={recordFieldComponentInstanceId}
recordId,
fieldDefinition?.metadata?.fieldName,
)}
onEnter={handleEnter} onEnter={handleEnter}
onCancel={handleCancel} onCancel={handleCancel}
onEscape={handleEscape} onEscape={handleEscape}

View File

@ -1,10 +1,10 @@
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState'; import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState';
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState'; import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState'; import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
import { RecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext'; import { RecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer'; import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
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 { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
@ -37,21 +37,19 @@ export const RecordInlineCellEditMode = ({
children, children,
}: RecordInlineCellEditModeProps) => { }: RecordInlineCellEditModeProps) => {
const { isCentered } = useContext(RecordInlineCellContext); const { isCentered } = useContext(RecordInlineCellContext);
const { recordId, fieldDefinition } = useContext(FieldContext);
const instanceId = getRecordFieldInputId( const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
recordId, RecordFieldComponentInstanceContext,
fieldDefinition?.metadata?.fieldName,
); );
const setFieldInputLayoutDirection = useSetRecoilComponentStateV2( const setFieldInputLayoutDirection = useSetRecoilComponentStateV2(
recordFieldInputLayoutDirectionComponentState, recordFieldInputLayoutDirectionComponentState,
instanceId, recordFieldComponentInstanceId,
); );
const setFieldInputLayoutDirectionLoading = useSetRecoilComponentStateV2( const setFieldInputLayoutDirectionLoading = useSetRecoilComponentStateV2(
recordFieldInputLayoutDirectionLoadingComponentState, recordFieldInputLayoutDirectionLoadingComponentState,
instanceId, recordFieldComponentInstanceId,
); );
const setFieldInputLayoutDirectionMiddleware = { const setFieldInputLayoutDirectionMiddleware = {

View File

@ -6,22 +6,28 @@ import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousH
import { isDefined } from 'twenty-shared'; import { isDefined } from 'twenty-shared';
import { useInitDraftValueV2 } from '@/object-record/record-field/hooks/useInitDraftValueV2'; import { useInitDraftValueV2 } from '@/object-record/record-field/hooks/useInitDraftValueV2';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext'; import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField'; import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId'; import { useGoBackToPreviousDropdownFocusId } from '@/ui/layout/dropdown/hooks/useGoBackToPreviousDropdownFocusId';
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious'; import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { isInlineCellInEditModeScopedState } from '../states/isInlineCellInEditModeScopedState'; import { isInlineCellInEditModeScopedState } from '../states/isInlineCellInEditModeScopedState';
import { InlineCellHotkeyScope } from '../types/InlineCellHotkeyScope'; import { InlineCellHotkeyScope } from '../types/InlineCellHotkeyScope';
export const useInlineCell = () => { export const useInlineCell = (
const { recordFieldComponentInstanceIdFromProps?: string,
recoilScopeId = '', ) => {
recordId, const { recordId, fieldDefinition } = useContext(FieldContext);
fieldDefinition,
} = useContext(FieldContext); const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
RecordFieldComponentInstanceContext,
recordFieldComponentInstanceIdFromProps,
);
const [isInlineCellInEditMode, setIsInlineCellInEditMode] = useRecoilState( const [isInlineCellInEditMode, setIsInlineCellInEditMode] = useRecoilState(
isInlineCellInEditModeScopedState(recoilScopeId), isInlineCellInEditModeScopedState(recordFieldComponentInstanceId),
); );
const { onOpenEditMode, onCloseEditMode } = useRecordInlineCellContext(); const { onOpenEditMode, onCloseEditMode } = useRecordInlineCellContext();
@ -50,7 +56,15 @@ export const useInlineCell = () => {
const openInlineCell = (customEditHotkeyScopeForField?: string) => { const openInlineCell = (customEditHotkeyScopeForField?: string) => {
onOpenEditMode?.(); onOpenEditMode?.();
setIsInlineCellInEditMode(true); setIsInlineCellInEditMode(true);
initFieldInputDraftValue({ recordId, fieldDefinition }); initFieldInputDraftValue({
recordId,
fieldDefinition,
fieldComponentInstanceId: getRecordFieldInputId(
recordId,
fieldDefinition.metadata.fieldName,
'inline-cell',
),
});
if (isDefined(customEditHotkeyScopeForField)) { if (isDefined(customEditHotkeyScopeForField)) {
setHotkeyScopeAndMemorizePreviousScope(customEditHotkeyScopeForField); setHotkeyScopeAndMemorizePreviousScope(customEditHotkeyScopeForField);

View File

@ -15,6 +15,7 @@ import { useRecordShowContainerActions } from '@/object-record/record-show/hooks
import { useRecordShowContainerData } from '@/object-record/record-show/hooks/useRecordShowContainerData'; import { useRecordShowContainerData } from '@/object-record/record-show/hooks/useRecordShowContainerData';
import { RecordDetailDuplicatesSection } from '@/object-record/record-show/record-detail-section/components/RecordDetailDuplicatesSection'; import { RecordDetailDuplicatesSection } from '@/object-record/record-show/record-detail-section/components/RecordDetailDuplicatesSection';
import { RecordDetailRelationSection } from '@/object-record/record-show/record-detail-section/components/RecordDetailRelationSection'; import { RecordDetailRelationSection } from '@/object-record/record-show/record-detail-section/components/RecordDetailRelationSection';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { isFieldCellSupported } from '@/object-record/utils/isFieldCellSupported'; import { isFieldCellSupported } from '@/object-record/utils/isFieldCellSupported';
import { FieldMetadataType } from '~/generated/graphql'; import { FieldMetadataType } from '~/generated/graphql';
@ -97,7 +98,6 @@ export const FieldsCard = ({
value={{ value={{
recordId: objectRecordId, recordId: objectRecordId,
maxWidth: 200, maxWidth: 200,
recoilScopeId: objectRecordId + fieldMetadataItem.id,
isLabelIdentifier: false, isLabelIdentifier: false,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({ fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem, field: fieldMetadataItem,
@ -110,22 +110,21 @@ export const FieldsCard = ({
hotkeyScope: InlineCellHotkeyScope.InlineCell, hotkeyScope: InlineCellHotkeyScope.InlineCell,
}} }}
> >
<RecordFieldComponentInstanceContext.Provider <ActivityTargetsInlineCell
value={{ componentInstanceId={getRecordFieldInputId(
instanceId: objectRecordId + fieldMetadataItem.id, objectRecordId,
}} fieldMetadataItem.name,
> 'fields-card',
<ActivityTargetsInlineCell )}
activityObjectNameSingular={ activityObjectNameSingular={
objectNameSingular as objectNameSingular as
| CoreObjectNameSingular.Note | CoreObjectNameSingular.Note
| CoreObjectNameSingular.Task | CoreObjectNameSingular.Task
} }
activityRecordId={objectRecordId} activityRecordId={objectRecordId}
showLabel={true} showLabel={true}
maxWidth={200} maxWidth={200}
/> />
</RecordFieldComponentInstanceContext.Provider>
</FieldContext.Provider> </FieldContext.Provider>
), ),
)} )}
@ -135,7 +134,6 @@ export const FieldsCard = ({
value={{ value={{
recordId: objectRecordId, recordId: objectRecordId,
maxWidth: 200, maxWidth: 200,
recoilScopeId: objectRecordId + fieldMetadataItem.id,
isLabelIdentifier: false, isLabelIdentifier: false,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({ fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem, field: fieldMetadataItem,
@ -150,7 +148,11 @@ export const FieldsCard = ({
> >
<RecordFieldComponentInstanceContext.Provider <RecordFieldComponentInstanceContext.Provider
value={{ value={{
instanceId: `${objectRecordId}-${fieldMetadataItem.id}`, instanceId: getRecordFieldInputId(
objectRecordId,
fieldMetadataItem.name,
'fields-card',
),
}} }}
> >
<RecordInlineCell loading={recordLoading} /> <RecordInlineCell loading={recordLoading} />
@ -169,7 +171,6 @@ export const FieldsCard = ({
key={objectRecordId + fieldMetadataItem.id} key={objectRecordId + fieldMetadataItem.id}
value={{ value={{
recordId: objectRecordId, recordId: objectRecordId,
recoilScopeId: objectRecordId + fieldMetadataItem.id,
isLabelIdentifier: false, isLabelIdentifier: false,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({ fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem, field: fieldMetadataItem,

View File

@ -68,8 +68,6 @@ export const ObjectRecordShowPageBreadcrumb = ({
<FieldContext.Provider <FieldContext.Provider
value={{ value={{
recordId: objectRecordId, recordId: objectRecordId,
recoilScopeId:
objectRecordId + labelIdentifierFieldMetadataItem?.id,
isLabelIdentifier: false, isLabelIdentifier: false,
fieldDefinition: { fieldDefinition: {
type: type:

View File

@ -70,8 +70,6 @@ export const SummaryCard = ({
<FieldContext.Provider <FieldContext.Provider
value={{ value={{
recordId: objectRecordId, recordId: objectRecordId,
recoilScopeId:
objectRecordId + labelIdentifierFieldMetadataItem?.id,
isLabelIdentifier: false, isLabelIdentifier: false,
fieldDefinition: { fieldDefinition: {
type: type:

View File

@ -37,6 +37,7 @@ import { RecordDetailRecordsListItem } from '@/object-record/record-show/record-
import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect'; import { RecordValueSetterEffect } from '@/object-record/record-store/components/RecordValueSetterEffect';
import { ObjectRecord } from '@/object-record/types/ObjectRecord'; import { ObjectRecord } from '@/object-record/types/ObjectRecord';
import { getForeignKeyNameFromRelationFieldName } from '@/object-record/utils/getForeignKeyNameFromRelationFieldName'; import { getForeignKeyNameFromRelationFieldName } from '@/object-record/utils/getForeignKeyNameFromRelationFieldName';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { isFieldCellSupported } from '@/object-record/utils/isFieldCellSupported'; import { isFieldCellSupported } from '@/object-record/utils/isFieldCellSupported';
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
@ -268,7 +269,6 @@ export const RecordDetailRelationRecordsListItem = ({
value={{ value={{
recordId: relationRecord.id, recordId: relationRecord.id,
maxWidth: 200, maxWidth: 200,
recoilScopeId: `${relationRecord.id}-${fieldMetadataItem.id}`,
isLabelIdentifier: false, isLabelIdentifier: false,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({ fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem, field: fieldMetadataItem,
@ -283,7 +283,11 @@ export const RecordDetailRelationRecordsListItem = ({
> >
<RecordFieldComponentInstanceContext.Provider <RecordFieldComponentInstanceContext.Provider
value={{ value={{
instanceId: `${relationRecord.id}-${fieldMetadataItem.id}`, instanceId: getRecordFieldInputId(
relationRecord.id,
fieldMetadataItem.name,
'record-detail',
),
}} }}
> >
<RecordInlineCell /> <RecordInlineCell />

View File

@ -13,7 +13,10 @@ export const useUpsertRecordsInStore = () => {
.getValue(); .getValue();
if (JSON.stringify(currentRecord) !== JSON.stringify(record)) { if (JSON.stringify(currentRecord) !== JSON.stringify(record)) {
set(recordStoreFamilyState(record.id), record); set(recordStoreFamilyState(record.id), {
...currentRecord,
...record,
});
} }
} }
}, },

View File

@ -62,7 +62,10 @@ export const useSetRecordTableData = ({
.getValue(); .getValue();
if (JSON.stringify(currentRecord) !== JSON.stringify(record)) { if (JSON.stringify(currentRecord) !== JSON.stringify(record)) {
set(recordStoreFamilyState(record.id), record); set(recordStoreFamilyState(record.id), {
...currentRecord,
...record,
});
} }
} }

View File

@ -1,9 +1,9 @@
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState'; import { recordFieldInputIsFieldInErrorComponentState } from '@/object-record/record-field/states/recordFieldInputIsFieldInErrorComponentState';
import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState'; import { recordFieldInputLayoutDirectionComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionComponentState';
import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState'; import { recordFieldInputLayoutDirectionLoadingComponentState } from '@/object-record/record-field/states/recordFieldInputLayoutDirectionLoadingComponentState';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer'; import { OverlayContainer } from '@/ui/layout/overlay/components/OverlayContainer';
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 { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
@ -14,7 +14,7 @@ import {
offset, offset,
useFloating, useFloating,
} from '@floating-ui/react'; } from '@floating-ui/react';
import { ReactElement, useContext } from 'react'; import { ReactElement } from 'react';
const StyledEditableCellEditModeContainer = styled.div<RecordTableCellEditModeProps>` const StyledEditableCellEditModeContainer = styled.div<RecordTableCellEditModeProps>`
align-items: center; align-items: center;
@ -35,25 +35,21 @@ export type RecordTableCellEditModeProps = {
export const RecordTableCellEditMode = ({ export const RecordTableCellEditMode = ({
children, children,
}: RecordTableCellEditModeProps) => { }: RecordTableCellEditModeProps) => {
const { recordId, fieldDefinition } = useContext(FieldContext);
const isFieldInError = useRecoilComponentValueV2( const isFieldInError = useRecoilComponentValueV2(
recordFieldInputIsFieldInErrorComponentState, recordFieldInputIsFieldInErrorComponentState,
); );
const instanceId = getRecordFieldInputId( const recordFieldComponentInstanceId = useAvailableComponentInstanceIdOrThrow(
recordId, RecordFieldComponentInstanceContext,
fieldDefinition?.metadata?.fieldName,
); );
const setFieldInputLayoutDirection = useSetRecoilComponentStateV2( const setFieldInputLayoutDirection = useSetRecoilComponentStateV2(
recordFieldInputLayoutDirectionComponentState, recordFieldInputLayoutDirectionComponentState,
instanceId, recordFieldComponentInstanceId,
); );
const setFieldInputLayoutDirectionLoading = useSetRecoilComponentStateV2( const setFieldInputLayoutDirectionLoading = useSetRecoilComponentStateV2(
recordFieldInputLayoutDirectionLoadingComponentState, recordFieldInputLayoutDirectionLoadingComponentState,
instanceId, recordFieldComponentInstanceId,
); );
const setFieldInputLayoutDirectionMiddleware = { const setFieldInputLayoutDirectionMiddleware = {

View File

@ -16,6 +16,7 @@ import { useRecordTableRowContextOrThrow } from '@/object-record/record-table/co
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition'; import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope'; import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope'; import { SelectFieldHotkeyScope } from '@/object-record/select/types/SelectFieldHotkeyScope';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { RelationDefinitionType } from '~/generated-metadata/graphql'; import { RelationDefinitionType } from '~/generated-metadata/graphql';
import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull'; import { isUndefinedOrNull } from '~/utils/isUndefinedOrNull';
@ -72,7 +73,6 @@ export const RecordTableCellFieldContextWrapper = ({
return ( return (
<FieldContext.Provider <FieldContext.Provider
value={{ value={{
recoilScopeId: recordId + columnDefinition.label,
recordId, recordId,
fieldDefinition: columnDefinition, fieldDefinition: columnDefinition,
useUpdateRecord: () => [updateRecord, {}], useUpdateRecord: () => [updateRecord, {}],
@ -89,7 +89,13 @@ export const RecordTableCellFieldContextWrapper = ({
}} }}
> >
<RecordFieldComponentInstanceContext.Provider <RecordFieldComponentInstanceContext.Provider
value={{ instanceId: recordId + columnDefinition.label }} value={{
instanceId: getRecordFieldInputId(
recordId,
columnDefinition.metadata.fieldName,
'record-table-cell',
),
}}
> >
{children} {children}
</RecordFieldComponentInstanceContext.Provider> </RecordFieldComponentInstanceContext.Provider>

View File

@ -3,9 +3,13 @@ import { useContext } from 'react';
import { FieldInput } from '@/object-record/record-field/components/FieldInput'; import { FieldInput } from '@/object-record/record-field/components/FieldInput';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly'; import { useIsFieldValueReadOnly } from '@/object-record/record-field/hooks/useIsFieldValueReadOnly';
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput';
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent'; import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext'; import { useRecordTableBodyContextOrThrow } from '@/object-record/record-table/contexts/RecordTableBodyContext';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId'; import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { currentHotkeyScopeState } from '@/ui/utilities/hotkey/states/internal/currentHotkeyScopeState';
import { useRecoilCallback } from 'recoil';
export const RecordTableCellFieldInput = () => { export const RecordTableCellFieldInput = () => {
const { recordId, fieldDefinition } = useContext(FieldContext); const { recordId, fieldDefinition } = useContext(FieldContext);
@ -31,16 +35,22 @@ export const RecordTableCellFieldInput = () => {
onCloseTableCell(); onCloseTableCell();
}; };
const handleClickOutside = ( const handleClickOutside: FieldInputClickOutsideEvent = useRecoilCallback(
persistField: () => void, ({ snapshot }) =>
event: MouseEvent | TouchEvent, (persistField, event) => {
) => { const hotkeyScope = snapshot
event.stopImmediatePropagation(); .getLoadable(currentHotkeyScopeState)
.getValue();
if (hotkeyScope.scope !== TableHotkeyScope.CellEditMode) {
return;
}
event.stopImmediatePropagation();
persistField(); persistField();
onCloseTableCell();
onCloseTableCell(); },
}; [onCloseTableCell],
);
const handleEscape: FieldInputEvent = (persistField) => { const handleEscape: FieldInputEvent = (persistField) => {
persistField(); persistField();

View File

@ -24,6 +24,7 @@ import { recordIndexOpenRecordInState } from '@/object-record/record-index/state
import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState'; import { viewableRecordNameSingularState } from '@/object-record/record-right-drawer/states/viewableRecordNameSingularState';
import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId'; import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId';
import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField'; import { getDropdownFocusIdForRecordField } from '@/object-record/utils/getDropdownFocusIdForRecordField';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious'; import { useSetActiveDropdownFocusIdAndMemorizePrevious } from '@/ui/layout/dropdown/hooks/useSetFocusedDropdownIdAndMemorizePrevious';
import { useClickOustideListenerStates } from '@/ui/utilities/pointer-event/hooks/useClickOustideListenerStates'; import { useClickOustideListenerStates } from '@/ui/utilities/pointer-event/hooks/useClickOustideListenerStates';
import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType'; import { ViewOpenRecordInType } from '@/views/types/ViewOpenRecordInType';
@ -158,6 +159,11 @@ export const useOpenRecordTableCellV2 = (tableScopeId: string) => {
value: initialValue, value: initialValue,
recordId, recordId,
fieldDefinition, fieldDefinition,
fieldComponentInstanceId: getRecordFieldInputId(
recordId,
fieldDefinition.metadata.fieldName,
'record-table-cell',
),
}); });
toggleClickOutsideListener(false); toggleClickOutsideListener(false);

View File

@ -8,6 +8,7 @@ import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEv
import { useInlineCell } from '../../record-inline-cell/hooks/useInlineCell'; import { useInlineCell } from '../../record-inline-cell/hooks/useInlineCell';
import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput'; import { FieldInputClickOutsideEvent } from '@/object-record/record-field/meta-types/input/components/DateTimeFieldInput';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { RecordTitleCellContainer } from '@/object-record/record-title-cell/components/RecordTitleCellContainer'; import { RecordTitleCellContainer } from '@/object-record/record-title-cell/components/RecordTitleCellContainer';
import { import {
RecordTitleCellContext, RecordTitleCellContext,
@ -30,7 +31,13 @@ export const RecordTitleCell = ({
const isFieldInputOnly = useIsFieldInputOnly(); const isFieldInputOnly = useIsFieldInputOnly();
const { closeInlineCell } = useInlineCell(); const { closeInlineCell } = useInlineCell(
getRecordFieldInputId(
recordId,
fieldDefinition?.metadata?.fieldName,
'title',
),
);
const handleEnter: FieldInputEvent = (persistField) => { const handleEnter: FieldInputEvent = (persistField) => {
persistField(); persistField();
@ -64,10 +71,6 @@ export const RecordTitleCell = ({
const recordTitleCellContextValue: RecordTitleCellContextProps = { const recordTitleCellContextValue: RecordTitleCellContextProps = {
editModeContent: ( editModeContent: (
<RecordTitleCellFieldInput <RecordTitleCellFieldInput
recordFieldInputId={getRecordFieldInputId(
recordId,
fieldDefinition?.metadata?.fieldName,
)}
onEnter={handleEnter} onEnter={handleEnter}
onEscape={handleEscape} onEscape={handleEscape}
onTab={handleTab} onTab={handleTab}
@ -82,10 +85,20 @@ export const RecordTitleCell = ({
}; };
return ( return (
<FieldFocusContextProvider> <RecordFieldComponentInstanceContext.Provider
<RecordTitleCellContext.Provider value={recordTitleCellContextValue}> value={{
<RecordTitleCellContainer /> instanceId: getRecordFieldInputId(
</RecordTitleCellContext.Provider> recordId,
</FieldFocusContextProvider> fieldDefinition?.metadata?.fieldName,
'title',
),
}}
>
<FieldFocusContextProvider>
<RecordTitleCellContext.Provider value={recordTitleCellContextValue}>
<RecordTitleCellContainer />
</RecordTitleCellContext.Provider>
</FieldFocusContextProvider>
</RecordFieldComponentInstanceContext.Provider>
); );
}; };

View File

@ -1,8 +1,5 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope';
import { getScopeIdFromComponentId } from '@/ui/utilities/recoil-scope/utils/getScopeIdFromComponentId';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent'; import { FieldInputEvent } from '@/object-record/record-field/types/FieldInputEvent';
import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName'; import { isFieldFullName } from '@/object-record/record-field/types/guards/isFieldFullName';
@ -11,7 +8,6 @@ import { RecordTitleCellTextFieldInput } from '@/object-record/record-title-cell
import { RecordTitleFullNameFieldInput } from '@/object-record/record-title-cell/components/RecordTitleFullNameFieldInput'; import { RecordTitleFullNameFieldInput } from '@/object-record/record-title-cell/components/RecordTitleFullNameFieldInput';
type RecordTitleCellFieldInputProps = { type RecordTitleCellFieldInputProps = {
recordFieldInputId: string;
onClickOutside?: ( onClickOutside?: (
persist: () => void, persist: () => void,
event: MouseEvent | TouchEvent, event: MouseEvent | TouchEvent,
@ -25,7 +21,6 @@ type RecordTitleCellFieldInputProps = {
export const RecordTitleCellFieldInput = ({ export const RecordTitleCellFieldInput = ({
sizeVariant, sizeVariant,
recordFieldInputId,
onEnter, onEnter,
onEscape, onEscape,
onShiftTab, onShiftTab,
@ -39,9 +34,7 @@ export const RecordTitleCellFieldInput = ({
} }
return ( return (
<RecordFieldInputScope <>
recordFieldInputScopeId={getScopeIdFromComponentId(recordFieldInputId)}
>
{isFieldText(fieldDefinition) ? ( {isFieldText(fieldDefinition) ? (
<RecordTitleCellTextFieldInput <RecordTitleCellTextFieldInput
onEnter={onEnter} onEnter={onEnter}
@ -61,6 +54,6 @@ export const RecordTitleCellFieldInput = ({
sizeVariant={sizeVariant} sizeVariant={sizeVariant}
/> />
) : null} ) : null}
</RecordFieldInputScope> </>
); );
}; };

View File

@ -1,6 +1,13 @@
import { isDefined } from 'twenty-shared';
export const getRecordFieldInputId = ( export const getRecordFieldInputId = (
recordId: string, recordId: string,
fieldName?: string, fieldName?: string,
prefix?: string,
): string => { ): string => {
if (isDefined(prefix)) {
return `${prefix}-${recordId}-${fieldName}`;
}
return `${recordId}-${fieldName}`; return `${recordId}-${fieldName}`;
}; };

View File

@ -9,6 +9,7 @@ import { FieldDisplay } from '@/object-record/record-field/components/FieldDispl
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { BooleanFieldInput } from '@/object-record/record-field/meta-types/input/components/BooleanFieldInput'; import { BooleanFieldInput } from '@/object-record/record-field/meta-types/input/components/BooleanFieldInput';
import { RatingFieldInput } from '@/object-record/record-field/meta-types/input/components/RatingFieldInput'; import { RatingFieldInput } from '@/object-record/record-field/meta-types/input/components/RatingFieldInput';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { SettingsDataModelSetFieldValueEffect } from '@/settings/data-model/fields/preview/components/SettingsDataModelSetFieldValueEffect'; import { SettingsDataModelSetFieldValueEffect } from '@/settings/data-model/fields/preview/components/SettingsDataModelSetFieldValueEffect';
import { SettingsDataModelSetPreviewRecordEffect } from '@/settings/data-model/fields/preview/components/SettingsDataModelSetRecordEffect'; import { SettingsDataModelSetPreviewRecordEffect } from '@/settings/data-model/fields/preview/components/SettingsDataModelSetRecordEffect';
import { useFieldPreviewValue } from '@/settings/data-model/fields/preview/hooks/useFieldPreviewValue'; import { useFieldPreviewValue } from '@/settings/data-model/fields/preview/hooks/useFieldPreviewValue';
@ -98,59 +99,65 @@ export const SettingsDataModelFieldPreview = ({
return ( return (
<> <>
{previewRecord ? ( <RecordFieldComponentInstanceContext.Provider
<SettingsDataModelSetPreviewRecordEffect value={{
fieldName={fieldName} instanceId: 'record-field-component-instance-id',
record={previewRecord} }}
/> >
) : ( {previewRecord ? (
<SettingsDataModelSetFieldValueEffect <SettingsDataModelSetPreviewRecordEffect
recordId={recordId} fieldName={fieldName}
fieldName={fieldName} record={previewRecord}
value={fieldPreviewValue} />
/> ) : (
)} <SettingsDataModelSetFieldValueEffect
<StyledFieldPreview shrink={shrink}> recordId={recordId}
{!!withFieldLabel && ( fieldName={fieldName}
<StyledFieldLabel> value={fieldPreviewValue}
<FieldIcon />
size={theme.icon.size.md}
stroke={theme.icon.stroke.sm}
/>
{fieldMetadataItem.label}:
</StyledFieldLabel>
)} )}
<FieldContext.Provider <StyledFieldPreview shrink={shrink}>
value={{ {!!withFieldLabel && (
recordId, <StyledFieldLabel>
isLabelIdentifier, <FieldIcon
fieldDefinition: { size={theme.icon.size.md}
type: fieldMetadataItem.type, stroke={theme.icon.stroke.sm}
iconName: 'FieldIcon', />
fieldMetadataId: fieldMetadataItem.id || '', {fieldMetadataItem.label}:
label: fieldMetadataItem.label, </StyledFieldLabel>
metadata: {
fieldName,
objectMetadataNameSingular: objectMetadataItem.nameSingular,
relationObjectMetadataNameSingular:
relationObjectMetadataItem?.nameSingular,
options: fieldMetadataItem.options ?? [],
settings: fieldMetadataItem.settings,
},
defaultValue: fieldMetadataItem.defaultValue,
},
hotkeyScope: 'field-preview',
}}
>
{fieldMetadataItem.type === FieldMetadataType.BOOLEAN ? (
<BooleanFieldInput readonly />
) : fieldMetadataItem.type === FieldMetadataType.RATING ? (
<RatingFieldInput readonly />
) : (
<FieldDisplay />
)} )}
</FieldContext.Provider> <FieldContext.Provider
</StyledFieldPreview> value={{
recordId,
isLabelIdentifier,
fieldDefinition: {
type: fieldMetadataItem.type,
iconName: 'FieldIcon',
fieldMetadataId: fieldMetadataItem.id || '',
label: fieldMetadataItem.label,
metadata: {
fieldName,
objectMetadataNameSingular: objectMetadataItem.nameSingular,
relationObjectMetadataNameSingular:
relationObjectMetadataItem?.nameSingular,
options: fieldMetadataItem.options ?? [],
settings: fieldMetadataItem.settings,
},
defaultValue: fieldMetadataItem.defaultValue,
},
hotkeyScope: 'field-preview',
}}
>
{fieldMetadataItem.type === FieldMetadataType.BOOLEAN ? (
<BooleanFieldInput readonly />
) : fieldMetadataItem.type === FieldMetadataType.RATING ? (
<RatingFieldInput readonly />
) : (
<FieldDisplay />
)}
</FieldContext.Provider>
</StyledFieldPreview>
</RecordFieldComponentInstanceContext.Provider>
</> </>
); );
}; };

View File

@ -8,8 +8,6 @@ import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
import { CurrencyPickerHotkeyScope } from '../types/CurrencyPickerHotkeyScope'; import { CurrencyPickerHotkeyScope } from '../types/CurrencyPickerHotkeyScope';
import { TableHotkeyScope } from '@/object-record/record-table/types/TableHotkeyScope';
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
import { CurrencyPickerDropdownSelect } from './CurrencyPickerDropdownSelect'; import { CurrencyPickerDropdownSelect } from './CurrencyPickerDropdownSelect';
const StyledDropdownButtonContainer = styled.div` const StyledDropdownButtonContainer = styled.div`
@ -69,10 +67,6 @@ export const CurrencyPickerDropdownButton = ({
closeDropdown(); closeDropdown();
}; };
const { toggleClickOutsideListener } = useClickOutsideListener(
TableHotkeyScope.CellEditMode,
);
const currency = currencies.find(({ value }) => value === valueCode); const currency = currencies.find(({ value }) => value === valueCode);
const currencyCode = currency?.value ?? CurrencyCode.USD; const currencyCode = currency?.value ?? CurrencyCode.USD;
@ -98,8 +92,6 @@ export const CurrencyPickerDropdownButton = ({
} }
dropdownPlacement="bottom-start" dropdownPlacement="bottom-start"
dropdownOffset={{ x: 0, y: 4 }} dropdownOffset={{ x: 0, y: 4 }}
onOpen={() => toggleClickOutsideListener(false)}
onClose={() => toggleClickOutsideListener(true)}
/> />
); );
}; };

View File

@ -5,6 +5,7 @@ import { useRecoilCallback } from 'recoil';
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition'; import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField'; import { isLabelIdentifierField } from '@/object-metadata/utils/isLabelIdentifierField';
import { FieldContext } from '@/object-record/record-field/contexts/FieldContext'; import { FieldContext } from '@/object-record/record-field/contexts/FieldContext';
import { RecordFieldComponentInstanceContext } from '@/object-record/record-field/states/contexts/RecordFieldComponentInstanceContext';
import { import {
RecordFieldValueSelectorContextProvider, RecordFieldValueSelectorContextProvider,
useSetRecordValue, useSetRecordValue,
@ -124,26 +125,32 @@ export const getFieldDecorator =
}); });
return ( return (
<RecordFieldValueSelectorContextProvider> <RecordFieldComponentInstanceContext.Provider
<FieldContext.Provider value={{
value={{ instanceId: 'record-field-component-instance-id',
recordId: record.id, }}
isLabelIdentifier, >
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({ <RecordFieldValueSelectorContextProvider>
field: fieldMetadataItem, <FieldContext.Provider
position: 0, value={{
objectMetadataItem, recordId: record.id,
}), isLabelIdentifier,
hotkeyScope: 'hotkey-scope', fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
}} field: fieldMetadataItem,
> position: 0,
<RecordMockSetterEffect objectMetadataItem,
companies={companies} }),
people={people} hotkeyScope: 'hotkey-scope',
tasks={tasks} }}
/> >
<Story /> <RecordMockSetterEffect
</FieldContext.Provider> companies={companies}
</RecordFieldValueSelectorContextProvider> people={people}
tasks={tasks}
/>
<Story />
</FieldContext.Provider>
</RecordFieldValueSelectorContextProvider>
</RecordFieldComponentInstanceContext.Provider>
); );
}; };

View File

@ -46,7 +46,6 @@ export const useMockFieldContext = ({
key={objectRecordId + fieldMetadataItem.id} key={objectRecordId + fieldMetadataItem.id}
value={{ value={{
recordId: objectRecordId, recordId: objectRecordId,
recoilScopeId: objectRecordId + fieldMetadataItem.id,
isLabelIdentifier, isLabelIdentifier,
fieldDefinition: formatFieldMetadataItemAsColumnDefinition({ fieldDefinition: formatFieldMetadataItemAsColumnDefinition({
field: fieldMetadataItem, field: fieldMetadataItem,

View File

@ -361,7 +361,7 @@ const StyledButton = styled('button', {
const StyledButtonWrapper = styled.div< const StyledButtonWrapper = styled.div<
Pick< Pick<
ButtonProps, ButtonProps,
'isLoading' | 'variant' | 'accent' | 'inverted' | 'disabled' 'isLoading' | 'variant' | 'accent' | 'inverted' | 'disabled' | 'fullWidth'
> >
>` >`
${({ theme, variant, accent, inverted, disabled }) => css` ${({ theme, variant, accent, inverted, disabled }) => css`
@ -409,7 +409,9 @@ const StyledButtonWrapper = styled.div<
max-width: ${({ isLoading, theme }) => max-width: ${({ isLoading, theme }) =>
isLoading ? `calc(100% - ${theme.spacing(8)})` : 'none'}; isLoading ? `calc(100% - ${theme.spacing(8)})` : 'none'};
position: relative; position: relative;
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
`; `;
export const Button = ({ export const Button = ({
@ -445,6 +447,7 @@ export const Button = ({
accent={accent} accent={accent}
inverted={inverted} inverted={inverted}
disabled={soon || disabled} disabled={soon || disabled}
fullWidth={fullWidth}
> >
{(isLoading || Icon) && ( {(isLoading || Icon) && (
<ButtonIcon Icon={Icon} isLoading={!!isLoading} /> <ButtonIcon Icon={Icon} isLoading={!!isLoading} />