mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-25 13:02:15 +03:00
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:
parent
b16437e6c8
commit
b6202fe98c
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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;
|
||||
};
|
@ -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
|
||||
)}
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user