feat: toggle board field visibilities (#1547)

Closes #1537, Closes #1539
This commit is contained in:
Thaïs
2023-09-13 11:58:52 +02:00
committed by GitHub
parent 67f1da038d
commit 28e12d492c
31 changed files with 492 additions and 168 deletions

View File

@ -1,4 +1,4 @@
import { type ComponentProps, useCallback } from 'react';
import type { ComponentProps } from 'react';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
@ -29,18 +29,6 @@ export function BoardHeader<SortField>({
availableSorts,
defaultViewName,
}: BoardHeaderProps<SortField>) {
const OptionsDropdownButton = useCallback(
() => (
<BoardOptionsDropdown
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
onStageAdd={onStageAdd}
onViewsChange={onViewsChange}
scopeContext={scopeContext}
/>
),
[onStageAdd, onViewsChange, scopeContext],
);
return (
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
<ViewBar
@ -48,8 +36,15 @@ export function BoardHeader<SortField>({
defaultViewName={defaultViewName}
onViewsChange={onViewsChange}
onViewSubmit={onViewSubmit}
optionsDropdownButton={
<BoardOptionsDropdown
customHotkeyScope={{ scope: BoardOptionsHotkeyScope.Dropdown }}
onStageAdd={onStageAdd}
onViewsChange={onViewsChange}
scopeContext={scopeContext}
/>
}
optionsDropdownKey={BoardOptionsDropdownKey}
OptionsDropdownButton={OptionsDropdownButton}
scopeContext={scopeContext}
/>
</RecoilScope>

View File

@ -16,6 +16,7 @@ import {
IconLayoutKanban,
IconPlus,
IconSettings,
IconTag,
} from '@/ui/icon';
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
import { MenuItemNavigate } from '@/ui/menu-item/components/MenuItemNavigate';
@ -23,12 +24,16 @@ import { ThemeColor } from '@/ui/theme/constants/colors';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { ViewFieldsVisibilityDropdownSection } from '@/ui/view-bar/components/ViewFieldsVisibilityDropdownSection';
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
import type { View } from '@/ui/view-bar/types/View';
import { useBoardCardFields } from '../hooks/useBoardCardFields';
import { boardColumnsState } from '../states/boardColumnsState';
import { hiddenBoardCardFieldsScopedSelector } from '../states/selectors/hiddenBoardCardFieldsScopedSelector';
import { visibleBoardCardFieldsScopedSelector } from '../states/selectors/visibleBoardCardFieldsScopedSelector';
import type { BoardColumnDefinition } from '../types/BoardColumnDefinition';
import { BoardOptionsDropdownKey } from '../types/BoardOptionsDropdownKey';
@ -43,10 +48,7 @@ const StyledIconSettings = styled(IconSettings)`
margin-right: ${({ theme }) => theme.spacing(1)};
`;
enum BoardOptionsMenu {
StageCreation = 'StageCreation',
Stages = 'Stages',
}
type BoardOptionsMenu = 'fields' | 'stage-creation' | 'stages';
type ColumnForCreate = {
id: string;
@ -72,14 +74,22 @@ export function BoardOptionsDropdownContent({
const [boardColumns, setBoardColumns] = useRecoilState(boardColumnsState);
const hiddenBoardCardFields = useRecoilScopedValue(
hiddenBoardCardFieldsScopedSelector,
scopeContext,
);
const hasHiddenFields = hiddenBoardCardFields.length > 0;
const visibleBoardCardFields = useRecoilScopedValue(
visibleBoardCardFieldsScopedSelector,
scopeContext,
);
const hasVisibleFields = visibleBoardCardFields.length > 0;
const viewsById = useRecoilScopedValue(viewsByIdScopedSelector, scopeContext);
const viewEditMode = useRecoilValue(viewEditModeState);
const handleStageSubmit = () => {
if (
currentMenu !== BoardOptionsMenu.StageCreation ||
!stageInputRef?.current?.value
)
if (currentMenu !== 'stage-creation' || !stageInputRef?.current?.value)
return;
const columnToCreate: ColumnForCreate = {
@ -113,6 +123,8 @@ export function BoardOptionsDropdownContent({
setCurrentMenu(menu);
};
const { handleFieldVisibilityChange } = useBoardCardFields({ scopeContext });
const { closeDropdownButton } = useDropdownButton({
key: BoardOptionsDropdownKey,
});
@ -161,14 +173,19 @@ export function BoardOptionsDropdownContent({
<StyledDropdownMenuSeparator />
<StyledDropdownMenuItemsContainer>
<MenuItemNavigate
onClick={() => handleMenuNavigate(BoardOptionsMenu.Stages)}
onClick={() => handleMenuNavigate('stages')}
LeftIcon={IconLayoutKanban}
text="Stages"
/>
<MenuItemNavigate
onClick={() => handleMenuNavigate('fields')}
LeftIcon={IconTag}
text="Fields"
/>
</StyledDropdownMenuItemsContainer>
</>
)}
{currentMenu === BoardOptionsMenu.Stages && (
{currentMenu === 'stages' && (
<>
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
Stages
@ -176,20 +193,45 @@ export function BoardOptionsDropdownContent({
<StyledDropdownMenuSeparator />
<StyledDropdownMenuItemsContainer>
<MenuItem
onClick={() => setCurrentMenu(BoardOptionsMenu.StageCreation)}
onClick={() => setCurrentMenu('stage-creation')}
LeftIcon={IconPlus}
text="Add stage"
/>
</StyledDropdownMenuItemsContainer>
</>
)}
{currentMenu === BoardOptionsMenu.StageCreation && (
{currentMenu === 'stage-creation' && (
<DropdownMenuInput
autoFocus
placeholder="New stage"
ref={stageInputRef}
/>
)}
{currentMenu === 'fields' && (
<>
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
Fields
</DropdownMenuHeader>
<StyledDropdownMenuSeparator />
{hasVisibleFields && (
<ViewFieldsVisibilityDropdownSection
title="Visible"
fields={visibleBoardCardFields}
onVisibilityChange={handleFieldVisibilityChange}
/>
)}
{hasVisibleFields && hasHiddenFields && (
<StyledDropdownMenuSeparator />
)}
{hasHiddenFields && (
<ViewFieldsVisibilityDropdownSection
title="Hidden"
fields={hiddenBoardCardFields}
onVisibilityChange={handleFieldVisibilityChange}
/>
)}
</>
)}
</StyledDropdownMenu>
);
}

View File

@ -160,8 +160,9 @@ export function EntityBoard({
<EntityBoardColumn
boardOptions={boardOptions}
column={column}
onTitleEdit={onEditColumnTitle}
onDelete={onColumnDelete}
onTitleEdit={onEditColumnTitle}
scopeContext={scopeContext}
/>
</RecoilScope>
</BoardColumnIdContext.Provider>

View File

@ -1,3 +1,4 @@
import type { Context } from 'react';
import { Draggable } from '@hello-pangea/dnd';
import { useSetRecoilState } from 'recoil';
@ -11,10 +12,12 @@ export function EntityBoardCard({
boardOptions,
cardId,
index,
scopeContext,
}: {
boardOptions: BoardOptions;
cardId: string;
index: number;
scopeContext: Context<string | null>;
}) {
const setContextMenuPosition = useSetRecoilState(contextMenuPositionState);
const setContextMenuOpenState = useSetRecoilState(contextMenuIsOpenState);
@ -43,7 +46,7 @@ export function EntityBoardCard({
data-select-disable
onContextMenu={handleContextMenu}
>
{boardOptions.cardComponent}
{<boardOptions.CardComponent scopeContext={scopeContext} />}
</div>
)}
</Draggable>

View File

@ -1,4 +1,4 @@
import { useContext } from 'react';
import { type Context, useContext } from 'react';
import styled from '@emotion/styled';
import { Draggable, Droppable, DroppableProvided } from '@hello-pangea/dnd';
import { useRecoilValue } from 'recoil';
@ -48,15 +48,17 @@ const BoardColumnCardsContainer = ({
};
export function EntityBoardColumn({
column,
boardOptions,
column,
onDelete,
onTitleEdit,
scopeContext,
}: {
column: BoardColumnDefinition;
boardOptions: BoardOptions;
column: BoardColumnDefinition;
onDelete?: (columnId: string) => void;
onTitleEdit: (columnId: string, title: string, color: string) => void;
scopeContext: Context<string | null>;
}) {
const boardColumnId = useContext(BoardColumnIdContext) ?? '';
@ -92,6 +94,7 @@ export function EntityBoardColumn({
index={index}
cardId={cardId}
boardOptions={boardOptions}
scopeContext={scopeContext}
/>
</BoardCardIdContext.Provider>
))}

View File

@ -0,0 +1,42 @@
import type { Context } from 'react';
import type {
ViewFieldDefinition,
ViewFieldMetadata,
} from '@/ui/editable-field/types/ViewField';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState';
import { boardCardFieldsByKeyScopedSelector } from '../states/selectors/boardCardFieldsByKeyScopedSelector';
export const useBoardCardFields = ({
scopeContext,
}: {
scopeContext: Context<string | null>;
}) => {
const [boardCardFields, setBoardCardFields] = useRecoilScopedState(
boardCardFieldsScopedState,
scopeContext,
);
const boardCardFieldsByKey = useRecoilScopedValue(
boardCardFieldsByKeyScopedSelector,
scopeContext,
);
const handleFieldVisibilityChange = (
field: ViewFieldDefinition<ViewFieldMetadata>,
) => {
const nextFields = boardCardFieldsByKey[field.key]
? boardCardFields.map((previousField) =>
previousField.key === field.key
? { ...previousField, isVisible: !field.isVisible }
: previousField,
)
: [...boardCardFields, { ...field, isVisible: true }];
setBoardCardFields(nextFields);
};
return { handleFieldVisibilityChange };
};

View File

@ -0,0 +1,14 @@
import { atomFamily } from 'recoil';
import type {
ViewFieldDefinition,
ViewFieldMetadata,
} from '@/ui/editable-field/types/ViewField';
export const availableBoardCardFieldsScopedState = atomFamily<
ViewFieldDefinition<ViewFieldMetadata>[],
string
>({
key: 'availableBoardCardFieldsScopedState',
default: [],
});

View File

@ -0,0 +1,14 @@
import { atomFamily } from 'recoil';
import type {
ViewFieldDefinition,
ViewFieldMetadata,
} from '@/ui/editable-field/types/ViewField';
export const boardCardFieldsScopedState = atomFamily<
ViewFieldDefinition<ViewFieldMetadata>[],
string
>({
key: 'boardCardFieldsScopedState',
default: [],
});

View File

@ -0,0 +1,18 @@
import { selectorFamily } from 'recoil';
import type {
ViewFieldDefinition,
ViewFieldMetadata,
} from '@/ui/editable-field/types/ViewField';
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
export const boardCardFieldsByKeyScopedSelector = selectorFamily({
key: 'boardCardFieldsByKeyScopedSelector',
get:
(scopeId: string) =>
({ get }) =>
get(boardCardFieldsScopedState(scopeId)).reduce<
Record<string, ViewFieldDefinition<ViewFieldMetadata>>
>((result, field) => ({ ...result, [field.key]: field }), {}),
});

View File

@ -0,0 +1,22 @@
import { selectorFamily } from 'recoil';
import { availableBoardCardFieldsScopedState } from '../availableBoardCardFieldsScopedState';
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
export const hiddenBoardCardFieldsScopedSelector = selectorFamily({
key: 'hiddenBoardCardFieldsScopedSelector',
get:
(scopeId: string) =>
({ get }) => {
const fields = get(boardCardFieldsScopedState(scopeId));
const fieldKeys = fields.map(({ key }) => key);
const otherAvailableKeys = get(
availableBoardCardFieldsScopedState(scopeId),
).filter(({ key }) => !fieldKeys.includes(key));
return [
...fields.filter((field) => !field.isVisible),
...otherAvailableKeys,
];
},
});

View File

@ -0,0 +1,13 @@
import { selectorFamily } from 'recoil';
import { boardCardFieldsScopedState } from '../boardCardFieldsScopedState';
export const visibleBoardCardFieldsScopedSelector = selectorFamily({
key: 'visibleBoardCardFieldsScopedSelector',
get:
(scopeId: string) =>
({ get }) =>
get(boardCardFieldsScopedState(scopeId)).filter(
(field) => field.isVisible,
),
});

View File

@ -1,13 +0,0 @@
import { atom } from 'recoil';
import type {
ViewFieldDefinition,
ViewFieldMetadata,
} from '../../editable-field/types/ViewField';
export const viewFieldsDefinitionsState = atom<
ViewFieldDefinition<ViewFieldMetadata>[]
>({
key: 'viewFieldsDefinitionState',
default: [],
});

View File

@ -1,3 +1,5 @@
import type { ComponentType, Context } from 'react';
import { FilterDefinitionByEntity } from '@/ui/view-bar/types/FilterDefinitionByEntity';
import { SortType } from '@/ui/view-bar/types/interface';
import { PipelineProgress } from '~/generated/graphql';
@ -5,7 +7,7 @@ import { PipelineProgressOrderByWithRelationInput as PipelineProgresses_Order_By
export type BoardOptions = {
newCardComponent: React.ReactNode;
cardComponent: React.ReactNode;
CardComponent: ComponentType<{ scopeContext: Context<string | null> }>;
filters: FilterDefinitionByEntity<PipelineProgress>[];
sorts: Array<SortType<PipelineProgresses_Order_By>>;
};

View File

@ -49,6 +49,7 @@ export function FloatingIconButtonGroup({
applyBlur={false}
applyShadow={false}
Icon={Icon}
key={index}
onClick={onClick}
position={position}
size={size}

View File

@ -117,11 +117,12 @@ export type ViewFieldMetadata = { type: ViewFieldType } & (
);
export type ViewFieldDefinition<T extends ViewFieldMetadata | unknown> = {
key: string;
name: string;
Icon?: IconComponent;
index: number;
isVisible?: boolean;
key: string;
metadata: T;
name: string;
};
export type ViewFieldTextValue = string;

View File

@ -12,25 +12,25 @@ import { IconChevronLeft, IconFileImport, IconTag } from '@/ui/icon';
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import { ViewFieldsVisibilityDropdownSection } from '@/ui/view-bar/components/ViewFieldsVisibilityDropdownSection';
import { useUpsertView } from '@/ui/view-bar/hooks/useUpsertView';
import { viewsByIdScopedSelector } from '@/ui/view-bar/states/selectors/viewsByIdScopedSelector';
import { viewEditModeState } from '@/ui/view-bar/states/viewEditModeState';
import type { View } from '@/ui/view-bar/types/View';
import { useTableColumns } from '../../hooks/useTableColumns';
import { TableRecoilScopeContext } from '../../states/recoil-scope-contexts/TableRecoilScopeContext';
import { hiddenTableColumnsScopedSelector } from '../../states/selectors/hiddenTableColumnsScopedSelector';
import { visibleTableColumnsScopedSelector } from '../../states/selectors/visibleTableColumnsScopedSelector';
import { TableOptionsDropdownKey } from '../../types/TableOptionsDropdownKey';
import { TableOptionsHotkeyScope } from '../../types/TableOptionsHotkeyScope';
import { TableOptionsDropdownColumnVisibility } from './TableOptionsDropdownSection';
type TableOptionsDropdownButtonProps = {
onViewsChange?: (views: View[]) => void | Promise<void>;
onImport?: () => void;
};
type TableOptionsMenu = 'properties';
type TableOptionsMenu = 'fields';
export function TableOptionsDropdownContent({
onViewsChange,
@ -40,9 +40,9 @@ export function TableOptionsDropdownContent({
key: TableOptionsDropdownKey,
});
const [selectedMenu, setSelectedMenu] = useState<
TableOptionsMenu | undefined
>(undefined);
const [currentMenu, setCurrentMenu] = useState<TableOptionsMenu | undefined>(
undefined,
);
const viewEditInputRef = useRef<HTMLInputElement>(null);
@ -65,6 +65,8 @@ export function TableOptionsDropdownContent({
scopeContext: TableRecoilScopeContext,
});
const { handleColumnVisibilityChange } = useTableColumns();
const handleViewNameSubmit = async () => {
const name = viewEditInputRef.current?.value;
await upsertView(name);
@ -72,10 +74,10 @@ export function TableOptionsDropdownContent({
const handleSelectMenu = (option: TableOptionsMenu) => {
handleViewNameSubmit();
setSelectedMenu(option);
setCurrentMenu(option);
};
const resetMenu = () => setSelectedMenu(undefined);
const resetMenu = () => setCurrentMenu(undefined);
useScopedHotkeys(
Key.Escape,
@ -97,7 +99,7 @@ export function TableOptionsDropdownContent({
return (
<StyledDropdownMenu>
{!selectedMenu && (
{!currentMenu && (
<>
{!!viewEditMode.mode ? (
<DropdownMenuInput
@ -118,9 +120,9 @@ export function TableOptionsDropdownContent({
<StyledDropdownMenuSeparator />
<StyledDropdownMenuItemsContainer>
<MenuItem
onClick={() => handleSelectMenu('properties')}
onClick={() => handleSelectMenu('fields')}
LeftIcon={IconTag}
text="Properties"
text="Fields"
/>
{onImport && (
<MenuItem
@ -132,22 +134,24 @@ export function TableOptionsDropdownContent({
</StyledDropdownMenuItemsContainer>
</>
)}
{selectedMenu === 'properties' && (
{currentMenu === 'fields' && (
<>
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={resetMenu}>
Properties
Fields
</DropdownMenuHeader>
<StyledDropdownMenuSeparator />
<TableOptionsDropdownColumnVisibility
<ViewFieldsVisibilityDropdownSection
title="Visible"
columns={visibleTableColumns}
fields={visibleTableColumns}
onVisibilityChange={handleColumnVisibilityChange}
/>
{hiddenTableColumns.length > 0 && (
<>
<StyledDropdownMenuSeparator />
<TableOptionsDropdownColumnVisibility
<ViewFieldsVisibilityDropdownSection
title="Hidden"
columns={hiddenTableColumns}
fields={hiddenTableColumns}
onVisibilityChange={handleColumnVisibilityChange}
/>
</>
)}

View File

@ -1,41 +0,0 @@
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
import { StyledDropdownMenuSubheader } from '@/ui/dropdown/components/StyledDropdownMenuSubheader';
import type { ViewFieldMetadata } from '@/ui/editable-field/types/ViewField';
import { IconMinus, IconPlus } from '@/ui/icon';
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
import { useTableColumns } from '../../hooks/useTableColumns';
import type { ColumnDefinition } from '../../types/ColumnDefinition';
type OwnProps = {
title: string;
columns: ColumnDefinition<ViewFieldMetadata>[];
};
export function TableOptionsDropdownColumnVisibility({
title,
columns,
}: OwnProps) {
const { handleColumnVisibilityChange } = useTableColumns();
return (
<>
<StyledDropdownMenuSubheader>{title}</StyledDropdownMenuSubheader>
<StyledDropdownMenuItemsContainer>
{columns.map((column) => (
<MenuItem
key={column.key}
LeftIcon={column.Icon}
iconButtons={[
{
Icon: column.isVisible ? IconMinus : IconPlus,
onClick: () => handleColumnVisibilityChange(column),
},
]}
text={column.name}
/>
))}
</StyledDropdownMenuItemsContainer>
</>
);
}

View File

@ -1,4 +1,3 @@
import { useCallback } from 'react';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
import { DropdownRecoilScopeContext } from '@/ui/dropdown/states/recoil-scope-contexts/DropdownRecoilScopeContext';
@ -70,17 +69,6 @@ export function TableHeader<SortField>({
await onViewSubmit?.();
}
const OptionsDropdownButton = useCallback(
() => (
<TableOptionsDropdown
onImport={onImport}
onViewsChange={onViewsChange}
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
/>
),
[onImport, onViewsChange],
);
return (
<RecoilScope SpecificContext={DropdownRecoilScopeContext}>
<ViewBar
@ -89,7 +77,13 @@ export function TableHeader<SortField>({
onReset={handleViewBarReset}
onViewSelect={handleViewSelect}
onViewSubmit={handleViewSubmit}
OptionsDropdownButton={OptionsDropdownButton}
optionsDropdownButton={
<TableOptionsDropdown
onImport={onImport}
onViewsChange={onViewsChange}
customHotkeyScope={{ scope: TableOptionsHotkeyScope.Dropdown }}
/>
}
optionsDropdownKey={TableOptionsDropdownKey}
scopeContext={TableRecoilScopeContext}
/>

View File

@ -6,5 +6,4 @@ import type {
export type ColumnDefinition<T extends ViewFieldMetadata | unknown> =
ViewFieldDefinition<T> & {
size: number;
index: number;
};

View File

@ -1,4 +1,4 @@
import type { ComponentProps, ComponentType, Context } from 'react';
import type { ComponentProps, Context, ReactNode } from 'react';
import { useDropdownButton } from '@/ui/dropdown/hooks/useDropdownButton';
import { TopBar } from '@/ui/top-bar/TopBar';
@ -22,7 +22,7 @@ import {
} from './ViewsDropdownButton';
export type ViewBarProps<SortField> = ComponentProps<'div'> & {
OptionsDropdownButton: ComponentType;
optionsDropdownButton: ReactNode;
optionsDropdownKey: string;
scopeContext: Context<string | null>;
} & Pick<
@ -41,7 +41,7 @@ export const ViewBar = <SortField,>({
onViewsChange,
onViewSelect,
onViewSubmit,
OptionsDropdownButton,
optionsDropdownButton,
optionsDropdownKey,
scopeContext,
...props
@ -76,7 +76,7 @@ export const ViewBar = <SortField,>({
hotkeyScope={FiltersHotkeyScope.FilterDropdownButton}
isPrimaryButton
/>
<OptionsDropdownButton />
{optionsDropdownButton}
</>
}
bottomComponent={

View File

@ -0,0 +1,39 @@
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
import { StyledDropdownMenuSubheader } from '@/ui/dropdown/components/StyledDropdownMenuSubheader';
import type {
ViewFieldDefinition,
ViewFieldMetadata,
} from '@/ui/editable-field/types/ViewField';
import { IconMinus, IconPlus } from '@/ui/icon';
import { MenuItem } from '@/ui/menu-item/components/MenuItem';
type OwnProps<Field> = {
fields: Field[];
onVisibilityChange: (field: Field) => void;
title: string;
};
export function ViewFieldsVisibilityDropdownSection<
Field extends ViewFieldDefinition<ViewFieldMetadata>,
>({ fields, onVisibilityChange, title }: OwnProps<Field>) {
return (
<>
<StyledDropdownMenuSubheader>{title}</StyledDropdownMenuSubheader>
<StyledDropdownMenuItemsContainer>
{fields.map((field) => (
<MenuItem
key={field.key}
LeftIcon={field.Icon}
iconButtons={[
{
Icon: field.isVisible ? IconMinus : IconPlus,
onClick: () => onVisibilityChange(field),
},
]}
text={field.name}
/>
))}
</StyledDropdownMenuItemsContainer>
</>
);
}