Issue6335: RecordInlineCell tree refactor with RecordInlineCellContext (#6537)

Fixes [#6335](https://github.com/twentyhq/twenty/issues/6335)

This pull request is for issue
[#6335](https://github.com/twentyhq/twenty/issues/6335): Refactor
RecordInlineCell tree with a Context to avoid props drilling. For the
refactoring, this PR made changes as below:

- Created new script RecordInlineCellContext.tsx: Defining a context to
pass in useContext()
- Updated RecordInlineCell.tsx: Passing the necessary props as context
values, wrapping with RecordInlineCellContext.Provider
- Updated RecordInlineCellContainer.tsx: Passing the props to
RecordInlineContainer as RecordInlineCellContext
- Updated RecordInlineCellDisplayMode.tsx: retrieves values from
useRecordInlineCellContext instead of directly assigning them
- RecordInlineCellValue.tsx: Removed values passed through
<RecordInlineCellDisplayMode> as they are now retrieved through
useRecordInlineCellContext + Removed the null check for
RecordInlineCellContextProps.

Using RecordInlineCellContext, I believe the context goes to the top of
the hierarchy and passed to the required layers without going through
several layers. However, please let me know if I understood the issue
incorrectly or it is not solved properly.

Thank you in advance for your review!

---------

Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com>
This commit is contained in:
Hansol Yang 2024-08-12 19:18:05 +09:00 committed by GitHub
parent b16437e6c8
commit b6202fe98c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 160 additions and 168 deletions

View File

@ -14,6 +14,7 @@ import { FieldContext } from '@/object-record/record-field/contexts/FieldContext
import { FieldFocusContextProvider } from '@/object-record/record-field/contexts/FieldFocusContextProvider';
import { RecordFieldInputScope } from '@/object-record/record-field/scopes/RecordFieldInputScope';
import { RecordInlineCellContainer } from '@/object-record/record-inline-cell/components/RecordInlineCellContainer';
import { RecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
import { useInlineCell } from '@/object-record/record-inline-cell/hooks/useInlineCell';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
@ -65,30 +66,36 @@ export const ActivityTargetsInlineCell = ({
<FieldFocusContextProvider>
{ActivityTargetsContextProvider && (
<ActivityTargetsContextProvider>
<RecordInlineCellContainer
buttonIcon={IconPencil}
customEditHotkeyScope={{
scope: ActivityEditorHotkeyScope.ActivityTargets,
<RecordInlineCellContext.Provider
value={{
buttonIcon: IconPencil,
customEditHotkeyScope: {
scope: ActivityEditorHotkeyScope.ActivityTargets,
},
IconLabel: showLabel ? IconArrowUpRight : undefined,
showLabel: showLabel,
readonly: readonly,
labelWidth: fieldDefinition?.labelWidth,
editModeContent: (
<ActivityTargetInlineCellEditMode
activity={activity}
activityTargetWithTargetRecords={
activityTargetObjectRecords
}
activityObjectNameSingular={activityObjectNameSingular}
/>
),
label: 'Relations',
displayModeContent: (
<ActivityTargetChips
activityTargetObjectRecords={activityTargetObjectRecords}
maxWidth={maxWidth}
/>
),
}}
IconLabel={showLabel ? IconArrowUpRight : undefined}
showLabel={showLabel}
readonly={readonly}
labelWidth={fieldDefinition?.labelWidth}
editModeContent={
<ActivityTargetInlineCellEditMode
activity={activity}
activityTargetWithTargetRecords={activityTargetObjectRecords}
activityObjectNameSingular={activityObjectNameSingular}
/>
}
label="Relations"
displayModeContent={
<ActivityTargetChips
activityTargetObjectRecords={activityTargetObjectRecords}
maxWidth={maxWidth}
/>
}
/>
>
<RecordInlineCellContainer />
</RecordInlineCellContext.Provider>
</ActivityTargetsContextProvider>
)}
</FieldFocusContextProvider>

View File

@ -16,6 +16,10 @@ import { useInlineCell } from '../hooks/useInlineCell';
import { useIsFieldReadOnly } from '@/object-record/record-field/hooks/useIsFieldReadOnly';
import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInputId';
import { RecordInlineCellContainer } from './RecordInlineCellContainer';
import {
RecordInlineCellContext,
RecordInlineCellContextProps,
} from './RecordInlineCellContext';
type RecordInlineCellProps = {
readonly?: boolean;
@ -23,7 +27,6 @@ type RecordInlineCellProps = {
isCentered?: boolean;
};
// TODO: refactor props drilling with a RecordInlineCellContext
export const RecordInlineCell = ({
readonly,
loading,
@ -75,48 +78,46 @@ export const RecordInlineCell = ({
const { getIcon } = useIcons();
const RecordInlineCellContextValue: RecordInlineCellContextProps = {
readonly: cellIsReadOnly,
buttonIcon: buttonIcon,
customEditHotkeyScope: isFieldRelation(fieldDefinition)
? { scope: RelationPickerHotkeyScope.RelationPicker }
: undefined,
IconLabel: fieldDefinition.iconName
? getIcon(fieldDefinition.iconName)
: undefined,
label: fieldDefinition.label,
labelWidth: fieldDefinition.labelWidth,
showLabel: fieldDefinition.showLabel,
isCentered: isCentered,
editModeContent: (
<FieldInput
recordFieldInputdId={getRecordFieldInputId(
recordId,
fieldDefinition?.metadata?.fieldName,
)}
onEnter={handleEnter}
onCancel={handleCancel}
onEscape={handleEscape}
onSubmit={handleSubmit}
onTab={handleTab}
onShiftTab={handleShiftTab}
onClickOutside={handleClickOutside}
isReadOnly={cellIsReadOnly}
/>
),
displayModeContent: <FieldDisplay />,
isDisplayModeFixHeight: undefined,
editModeContentOnly: isFieldInputOnly,
loading: loading,
};
return (
<FieldFocusContextProvider>
<RecordInlineCellContainer
readonly={cellIsReadOnly}
buttonIcon={buttonIcon}
customEditHotkeyScope={
isFieldRelation(fieldDefinition)
? {
scope: RelationPickerHotkeyScope.RelationPicker,
}
: undefined
}
IconLabel={
fieldDefinition.iconName
? getIcon(fieldDefinition.iconName)
: undefined
}
label={fieldDefinition.label}
labelWidth={fieldDefinition.labelWidth}
showLabel={fieldDefinition.showLabel}
isCentered={isCentered}
editModeContent={
<FieldInput
recordFieldInputdId={getRecordFieldInputId(
recordId,
fieldDefinition?.metadata?.fieldName,
)}
onEnter={handleEnter}
onCancel={handleCancel}
onEscape={handleEscape}
onSubmit={handleSubmit}
onTab={handleTab}
onShiftTab={handleShiftTab}
onClickOutside={handleClickOutside}
isReadOnly={cellIsReadOnly}
/>
}
displayModeContent={<FieldDisplay />}
isDisplayModeFixHeight
editModeContentOnly={isFieldInputOnly}
loading={loading}
/>
<RecordInlineCellContext.Provider value={RecordInlineCellContextValue}>
<RecordInlineCellContainer />
</RecordInlineCellContext.Provider>
</FieldFocusContextProvider>
);
};

View File

@ -10,6 +10,8 @@ import { getRecordFieldInputId } from '@/object-record/utils/getRecordFieldInput
import { EllipsisDisplay } from '@/ui/field/display/components/EllipsisDisplay';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecordInlineCellContext } from './RecordInlineCellContext';
const StyledIconContainer = styled.div`
align-items: center;
color: ${({ theme }) => theme.font.color.tertiary};
@ -78,23 +80,10 @@ export type RecordInlineCellContainerProps = {
isCentered?: boolean;
};
// TODO: refactor props drilling with a RecordInlineCellContext
export const RecordInlineCellContainer = ({
readonly,
IconLabel,
label,
labelWidth,
showLabel,
buttonIcon,
editModeContent,
displayModeContent,
customEditHotkeyScope,
editModeContentOnly,
isDisplayModeFixHeight,
disableHoverEffect,
loading = false,
isCentered,
}: RecordInlineCellContainerProps) => {
export const RecordInlineCellContainer = () => {
const { readonly, IconLabel, label, labelWidth, showLabel } =
useRecordInlineCellContext();
const { recordId, fieldDefinition } = useContext(FieldContext);
const { setIsFocused } = useFieldFocus();
@ -149,22 +138,7 @@ export const RecordInlineCellContainer = ({
</StyledLabelAndIconContainer>
)}
<StyledValueContainer>
<RecordInlineCellValue
{...{
displayModeContent,
customEditHotkeyScope,
disableHoverEffect,
editModeContent,
editModeContentOnly,
isDisplayModeFixHeight,
buttonIcon,
label,
loading,
readonly,
showLabel,
isCentered,
}}
/>
<RecordInlineCellValue />
</StyledValueContainer>
</StyledInlineCellBaseContainer>
);

View File

@ -0,0 +1,47 @@
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { createContext, ReactElement, useContext } from 'react';
import { IconComponent } from 'twenty-ui';
export type RecordInlineCellContextProps = {
readonly?: boolean;
IconLabel?: IconComponent;
label?: string;
labelWidth?: number;
showLabel?: boolean;
buttonIcon?: IconComponent;
editModeContent?: ReactElement;
editModeContentOnly?: boolean;
displayModeContent?: ReactElement;
customEditHotkeyScope?: HotkeyScope;
isDisplayModeFixHeight?: boolean;
disableHoverEffect?: boolean;
loading?: boolean;
isCentered?: boolean;
};
const defaultRecordInlineCellContextProp: RecordInlineCellContextProps = {
readonly: false,
IconLabel: undefined,
label: '',
labelWidth: 0,
showLabel: false,
buttonIcon: undefined,
editModeContent: undefined,
editModeContentOnly: false,
displayModeContent: undefined,
customEditHotkeyScope: undefined,
isDisplayModeFixHeight: false,
disableHoverEffect: false,
loading: false,
isCentered: false,
};
export const RecordInlineCellContext =
createContext<RecordInlineCellContextProps>(
defaultRecordInlineCellContextProp,
);
export const useRecordInlineCellContext = (): RecordInlineCellContextProps => {
const context = useContext(RecordInlineCellContext);
return context;
};

View File

@ -4,14 +4,17 @@ import styled from '@emotion/styled';
import { useFieldFocus } from '@/object-record/record-field/hooks/useFieldFocus';
import { useIsFieldEmpty } from '@/object-record/record-field/hooks/useIsFieldEmpty';
import { useIsFieldInputOnly } from '@/object-record/record-field/hooks/useIsFieldInputOnly';
import { RecordInlineCellContainerProps } from '@/object-record/record-inline-cell/components/RecordInlineCellContainer';
import {
RecordInlineCellContextProps,
useRecordInlineCellContext,
} from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
import { RecordInlineCellButton } from '@/object-record/record-inline-cell/components/RecordInlineCellEditButton';
const StyledRecordInlineCellNormalModeOuterContainer = styled.div<
Pick<
RecordInlineCellDisplayModeProps,
'disableHoverEffect' | 'isDisplayModeFixHeight' | 'isHovered'
>
RecordInlineCellContextProps,
'isDisplayModeFixHeight' | 'disableHoverEffect'
> & { isHovered?: boolean }
>`
align-items: center;
border-radius: ${({ theme }) => theme.border.radius.sm};
@ -53,23 +56,19 @@ const StyledEmptyField = styled.div`
color: ${({ theme }) => theme.font.color.light};
`;
type RecordInlineCellDisplayModeProps = {
disableHoverEffect?: boolean;
isDisplayModeFixHeight?: boolean;
isHovered?: boolean;
emptyPlaceholder?: string;
} & Pick<RecordInlineCellContainerProps, 'buttonIcon' | 'editModeContentOnly'>;
export const RecordInlineCellDisplayMode = ({
children,
disableHoverEffect,
isDisplayModeFixHeight,
emptyPlaceholder = 'Empty',
isHovered,
buttonIcon,
editModeContentOnly,
}: React.PropsWithChildren<RecordInlineCellDisplayModeProps>) => {
}: React.PropsWithChildren<unknown>) => {
const { isFocused } = useFieldFocus();
const {
editModeContentOnly,
showLabel,
label,
buttonIcon,
} = useRecordInlineCellContext();
const isDisplayModeContentEmpty = useIsFieldEmpty();
const showEditButton =
buttonIcon &&
@ -81,17 +80,15 @@ export const RecordInlineCellDisplayMode = ({
const shouldDisplayEditModeOnFocus = isFocused && isFieldInputOnly;
const emptyPlaceHolder = showLabel ? 'Empty' : label;
return (
<>
<StyledRecordInlineCellNormalModeOuterContainer
disableHoverEffect={disableHoverEffect}
isDisplayModeFixHeight={isDisplayModeFixHeight}
isHovered={isHovered}
>
<StyledRecordInlineCellNormalModeOuterContainer isHovered={isFocused}>
<StyledRecordInlineCellNormalModeInnerContainer>
{(isDisplayModeContentEmpty && !shouldDisplayEditModeOnFocus) ||
!children ? (
<StyledEmptyField>{emptyPlaceholder}</StyledEmptyField>
<StyledEmptyField>{emptyPlaceHolder}</StyledEmptyField>
) : (
children
)}

View File

@ -1,8 +1,7 @@
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { useFieldFocus } from '@/object-record/record-field/hooks/useFieldFocus';
import { RecordInlineCellContainerProps } from '@/object-record/record-inline-cell/components/RecordInlineCellContainer';
import { useRecordInlineCellContext } from '@/object-record/record-inline-cell/components/RecordInlineCellContext';
import { RecordInlineCellDisplayMode } from '@/object-record/record-inline-cell/components/RecordInlineCellDisplayMode';
import { RecordInlineCellEditMode } from '@/object-record/record-inline-cell/components/RecordInlineCellEditMode';
import { RecordInlineCellSkeletonLoader } from '@/object-record/record-inline-cell/components/RecordInlineCellSkeletonLoader';
@ -29,37 +28,16 @@ const StyledClickableContainer = styled.div<{
`};
`;
type RecordInlineCellValueProps = Pick<
RecordInlineCellContainerProps,
| 'displayModeContent'
| 'customEditHotkeyScope'
| 'editModeContent'
| 'editModeContentOnly'
| 'isDisplayModeFixHeight'
| 'disableHoverEffect'
| 'readonly'
| 'buttonIcon'
| 'loading'
| 'showLabel'
| 'label'
| 'isCentered'
>;
export const RecordInlineCellValue = ({
displayModeContent,
customEditHotkeyScope,
disableHoverEffect,
editModeContent,
editModeContentOnly,
isDisplayModeFixHeight,
readonly,
buttonIcon,
loading,
showLabel,
label,
isCentered,
}: RecordInlineCellValueProps) => {
const { isFocused } = useFieldFocus();
export const RecordInlineCellValue = () => {
const {
displayModeContent,
customEditHotkeyScope,
editModeContent,
editModeContentOnly,
readonly,
loading,
isCentered,
} = useRecordInlineCellContext();
const { isInlineCellInEditMode, openInlineCell } = useInlineCell();
@ -80,12 +58,7 @@ export const RecordInlineCellValue = ({
)}
{editModeContentOnly ? (
<StyledClickableContainer readonly={readonly} isCentered={isCentered}>
<RecordInlineCellDisplayMode
disableHoverEffect={disableHoverEffect}
isDisplayModeFixHeight={isDisplayModeFixHeight}
isHovered={isFocused}
emptyPlaceholder={showLabel ? 'Empty' : label}
>
<RecordInlineCellDisplayMode>
{editModeContent}
</RecordInlineCellDisplayMode>
</StyledClickableContainer>
@ -95,14 +68,7 @@ export const RecordInlineCellValue = ({
onClick={handleDisplayModeClick}
isCentered={isCentered}
>
<RecordInlineCellDisplayMode
disableHoverEffect={disableHoverEffect}
isDisplayModeFixHeight={isDisplayModeFixHeight}
isHovered={isFocused}
emptyPlaceholder={showLabel ? 'Empty' : label}
buttonIcon={buttonIcon}
editModeContentOnly={editModeContentOnly}
>
<RecordInlineCellDisplayMode>
{displayModeContent}
</RecordInlineCellDisplayMode>
</StyledClickableContainer>