Feat: Add tooltips on new column menu (#1893)

* implemented tooltip for view fields

* console.log
This commit is contained in:
Ayush Agrawal
2023-10-06 14:34:39 +05:30
committed by GitHub
parent 2ff35083fb
commit 53021dc64f
11 changed files with 132 additions and 27 deletions

View File

@ -43,6 +43,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata
}, },
isVisible: true, isVisible: true,
buttonIcon: IconArrowUpRight, buttonIcon: IconArrowUpRight,
infoTooltipContent: 'The company name.',
basePathToShowPage: '/companies/', basePathToShowPage: '/companies/',
} satisfies ColumnDefinition<FieldChipMetadata>, } satisfies ColumnDefinition<FieldChipMetadata>,
{ {
@ -58,6 +59,8 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata
}, },
isVisible: true, isVisible: true,
buttonIcon: IconPencil, buttonIcon: IconPencil,
infoTooltipContent:
'The company website URL. We use this url to fetch the company icon.',
} satisfies ColumnDefinition<FieldURLMetadata>, } satisfies ColumnDefinition<FieldURLMetadata>,
{ {
key: 'accountOwner', key: 'accountOwner',
@ -71,6 +74,8 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata
relationType: Entity.User, relationType: Entity.User,
}, },
isVisible: true, isVisible: true,
infoTooltipContent:
'Your team member responsible for managing the company account.',
} satisfies ColumnDefinition<FieldRelationMetadata>, } satisfies ColumnDefinition<FieldRelationMetadata>,
{ {
key: 'createdAt', key: 'createdAt',
@ -83,6 +88,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata
fieldName: 'createdAt', fieldName: 'createdAt',
}, },
isVisible: true, isVisible: true,
infoTooltipContent: "Date when the company's record was created.",
} satisfies ColumnDefinition<FieldDateMetadata>, } satisfies ColumnDefinition<FieldDateMetadata>,
{ {
key: 'employees', key: 'employees',
@ -97,6 +103,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata
placeHolder: 'Employees', placeHolder: 'Employees',
}, },
isVisible: true, isVisible: true,
infoTooltipContent: 'Number of employees in the company.',
} satisfies ColumnDefinition<FieldNumberMetadata>, } satisfies ColumnDefinition<FieldNumberMetadata>,
{ {
key: 'linkedin', key: 'linkedin',
@ -111,6 +118,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata
}, },
isVisible: true, isVisible: true,
buttonIcon: IconPencil, buttonIcon: IconPencil,
infoTooltipContent: 'The company Linkedin account.',
} satisfies ColumnDefinition<FieldURLMetadata>, } satisfies ColumnDefinition<FieldURLMetadata>,
{ {
key: 'address', key: 'address',
@ -124,6 +132,7 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata
placeHolder: 'Address', // Hack: Fake character to prevent password-manager from filling the field placeHolder: 'Address', // Hack: Fake character to prevent password-manager from filling the field
}, },
isVisible: true, isVisible: true,
infoTooltipContent: 'The company address.',
} satisfies ColumnDefinition<FieldTextMetadata>, } satisfies ColumnDefinition<FieldTextMetadata>,
{ {
key: 'idealCustomerProfile', key: 'idealCustomerProfile',
@ -136,6 +145,8 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata
fieldName: 'idealCustomerProfile', fieldName: 'idealCustomerProfile',
}, },
isVisible: false, isVisible: false,
infoTooltipContent:
'Ideal Customer Profile: Indicates whether the company is the most suitable and valuable customer for you.',
} satisfies ColumnDefinition<FieldBooleanMetadata>, } satisfies ColumnDefinition<FieldBooleanMetadata>,
{ {
key: 'annualRecurringRevenue', key: 'annualRecurringRevenue',
@ -148,6 +159,8 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata
fieldName: 'annualRecurringRevenue', fieldName: 'annualRecurringRevenue',
placeHolder: 'ARR', placeHolder: 'ARR',
}, },
infoTooltipContent:
'Annual Recurring Revenue: The actual or estimated annual revenue of the company.',
} satisfies ColumnDefinition<FieldMoneyMetadata>, } satisfies ColumnDefinition<FieldMoneyMetadata>,
{ {
key: 'xUrl', key: 'xUrl',
@ -162,5 +175,6 @@ export const companiesAvailableColumnDefinitions: ColumnDefinition<FieldMetadata
}, },
isVisible: false, isVisible: false,
buttonIcon: IconPencil, buttonIcon: IconPencil,
infoTooltipContent: 'The company Twitter account.',
} satisfies ColumnDefinition<FieldURLMetadata>, } satisfies ColumnDefinition<FieldURLMetadata>,
]; ];

View File

@ -42,6 +42,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[]
entityType: Entity.Person, entityType: Entity.Person,
}, },
buttonIcon: IconArrowUpRight, buttonIcon: IconArrowUpRight,
infoTooltipContent: 'Contacts first and last name.',
basePathToShowPage: '/person/', basePathToShowPage: '/person/',
} satisfies ColumnDefinition<FieldDoubleTextChipMetadata>, } satisfies ColumnDefinition<FieldDoubleTextChipMetadata>,
{ {
@ -56,6 +57,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[]
placeHolder: 'Email', // Hack: Fake character to prevent password-manager from filling the field placeHolder: 'Email', // Hack: Fake character to prevent password-manager from filling the field
}, },
buttonIcon: IconPencil, buttonIcon: IconPencil,
infoTooltipContent: 'Contacts Email.',
} satisfies ColumnDefinition<FieldEmailMetadata>, } satisfies ColumnDefinition<FieldEmailMetadata>,
{ {
key: 'company', key: 'company',
@ -68,6 +70,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[]
fieldName: 'company', fieldName: 'company',
relationType: Entity.Company, relationType: Entity.Company,
}, },
infoTooltipContent: 'Contacts company.',
} satisfies ColumnDefinition<FieldRelationMetadata>, } satisfies ColumnDefinition<FieldRelationMetadata>,
{ {
key: 'phone', key: 'phone',
@ -81,6 +84,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[]
placeHolder: 'Phone', // Hack: Fake character to prevent password-manager from filling the field placeHolder: 'Phone', // Hack: Fake character to prevent password-manager from filling the field
}, },
buttonIcon: IconPencil, buttonIcon: IconPencil,
infoTooltipContent: 'Contacts phone number.',
} satisfies ColumnDefinition<FieldPhoneMetadata>, } satisfies ColumnDefinition<FieldPhoneMetadata>,
{ {
key: 'createdAt', key: 'createdAt',
@ -92,6 +96,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[]
metadata: { metadata: {
fieldName: 'createdAt', fieldName: 'createdAt',
}, },
infoTooltipContent: 'Date when the contact was added.',
} satisfies ColumnDefinition<FieldDateMetadata>, } satisfies ColumnDefinition<FieldDateMetadata>,
{ {
key: 'city', key: 'city',
@ -104,6 +109,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[]
fieldName: 'city', fieldName: 'city',
placeHolder: 'City', // Hack: Fake character to prevent password-manager from filling the field placeHolder: 'City', // Hack: Fake character to prevent password-manager from filling the field
}, },
infoTooltipContent: 'Contacts city.',
} satisfies ColumnDefinition<FieldTextMetadata>, } satisfies ColumnDefinition<FieldTextMetadata>,
{ {
key: 'jobTitle', key: 'jobTitle',
@ -116,6 +122,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[]
fieldName: 'jobTitle', fieldName: 'jobTitle',
placeHolder: 'Job title', placeHolder: 'Job title',
}, },
infoTooltipContent: 'Contacts job title.',
} satisfies ColumnDefinition<FieldTextMetadata>, } satisfies ColumnDefinition<FieldTextMetadata>,
{ {
key: 'linkedin', key: 'linkedin',
@ -129,6 +136,7 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[]
placeHolder: 'LinkedIn', placeHolder: 'LinkedIn',
}, },
buttonIcon: IconPencil, buttonIcon: IconPencil,
infoTooltipContent: 'Contacts Linkedin account.',
} satisfies ColumnDefinition<FieldURLMetadata>, } satisfies ColumnDefinition<FieldURLMetadata>,
{ {
key: 'x', key: 'x',
@ -142,5 +150,6 @@ export const peopleAvailableColumnDefinitions: ColumnDefinition<FieldMetadata>[]
placeHolder: 'X', placeHolder: 'X',
}, },
buttonIcon: IconPencil, buttonIcon: IconPencil,
infoTooltipContent: 'Contacts Twitter account.',
} satisfies ColumnDefinition<FieldURLMetadata>, } satisfies ColumnDefinition<FieldURLMetadata>,
]; ];

View File

@ -27,6 +27,8 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
fieldName: 'closeDate', fieldName: 'closeDate',
}, },
isVisible: true, isVisible: true,
infoTooltipContent:
'Specified date by which an opportunity must be completed.',
} satisfies BoardFieldDefinition<FieldDateMetadata>, } satisfies BoardFieldDefinition<FieldDateMetadata>,
{ {
key: 'amount', key: 'amount',
@ -39,6 +41,7 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
placeHolder: '0', placeHolder: '0',
}, },
isVisible: true, isVisible: true,
infoTooltipContent: 'Potential monetary value of a business opportunity.',
} satisfies BoardFieldDefinition<FieldNumberMetadata>, } satisfies BoardFieldDefinition<FieldNumberMetadata>,
{ {
key: 'probability', key: 'probability',
@ -50,6 +53,8 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
fieldName: 'probability', fieldName: 'probability',
}, },
isVisible: true, isVisible: true,
infoTooltipContent:
"Level of certainty in the lead's potential to convert into a success.",
} satisfies BoardFieldDefinition<FieldProbabilityMetadata>, } satisfies BoardFieldDefinition<FieldProbabilityMetadata>,
{ {
key: 'pointOfContact', key: 'pointOfContact',
@ -64,5 +69,6 @@ export const pipelineAvailableFieldDefinitions: BoardFieldDefinition<FieldMetada
}, },
isVisible: true, isVisible: true,
buttonIcon: IconPencil, buttonIcon: IconPencil,
infoTooltipContent: 'Primary contact within the company.',
} satisfies BoardFieldDefinition<FieldRelationMetadata>, } satisfies BoardFieldDefinition<FieldRelationMetadata>,
]; ];

View File

@ -21,17 +21,21 @@ export type FloatingIconButtonProps = {
disabled?: boolean; disabled?: boolean;
focus?: boolean; focus?: boolean;
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void; onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
isActive?: boolean;
}; };
const StyledButton = styled.button< const StyledButton = styled.button<
Pick< Pick<
FloatingIconButtonProps, FloatingIconButtonProps,
'size' | 'position' | 'applyShadow' | 'applyBlur' | 'focus' 'size' | 'position' | 'applyShadow' | 'applyBlur' | 'focus' | 'isActive'
> >
>` >`
align-items: center; align-items: center;
backdrop-filter: ${({ applyBlur }) => (applyBlur ? 'blur(20px)' : 'none')}; backdrop-filter: ${({ applyBlur }) => (applyBlur ? 'blur(20px)' : 'none')};
background: ${({ theme }) => theme.background.primary}; background: ${({ theme, isActive }) =>
!!isActive
? theme.background.transparent.medium
: theme.background.primary};
border: ${({ focus, theme }) => border: ${({ focus, theme }) =>
focus ? `1px solid ${theme.color.blue}` : 'transparent'}; focus ? `1px solid ${theme.color.blue}` : 'transparent'};
border-radius: ${({ position, theme }) => { border-radius: ${({ position, theme }) => {
@ -87,7 +91,8 @@ const StyledButton = styled.button<
}} }}
&:hover { &:hover {
background: ${({ theme }) => theme.background.transparent.lighter}; background: ${({ theme, isActive }) =>
!!isActive ?? theme.background.transparent.lighter};
} }
&:active { &:active {
@ -110,6 +115,7 @@ export const FloatingIconButton = ({
disabled = false, disabled = false,
focus = false, focus = false,
onClick, onClick,
isActive,
}: FloatingIconButtonProps) => { }: FloatingIconButtonProps) => {
const theme = useTheme(); const theme = useTheme();
return ( return (
@ -122,6 +128,7 @@ export const FloatingIconButton = ({
className={className} className={className}
position={position} position={position}
onClick={onClick} onClick={onClick}
isActive={isActive}
> >
{Icon && <Icon size={theme.icon.size.md} />} {Icon && <Icon size={theme.icon.size.md} />}
</StyledButton> </StyledButton>

View File

@ -27,15 +27,17 @@ export type FloatingIconButtonGroupProps = Pick<
iconButtons: { iconButtons: {
Icon: IconComponent; Icon: IconComponent;
onClick?: (event: MouseEvent<any>) => void; onClick?: (event: MouseEvent<any>) => void;
isActive?: boolean;
}[]; }[];
}; };
export const FloatingIconButtonGroup = ({ export const FloatingIconButtonGroup = ({
iconButtons, iconButtons,
size, size,
className,
}: FloatingIconButtonGroupProps) => ( }: FloatingIconButtonGroupProps) => (
<StyledFloatingIconButtonGroupContainer> <StyledFloatingIconButtonGroupContainer className={className}>
{iconButtons.map(({ Icon, onClick }, index) => { {iconButtons.map(({ Icon, onClick, isActive }, index) => {
const position: FloatingIconButtonPosition = const position: FloatingIconButtonPosition =
iconButtons.length === 1 iconButtons.length === 1
? 'standalone' ? 'standalone'
@ -54,6 +56,7 @@ export const FloatingIconButtonGroup = ({
onClick={onClick} onClick={onClick}
position={position} position={position}
size={size} size={size}
isActive={isActive}
/> />
); );
})} })}

View File

@ -11,4 +11,5 @@ export type FieldDefinition<T extends FieldMetadata> = {
metadata: T; metadata: T;
buttonIcon?: IconComponent; buttonIcon?: IconComponent;
basePathToShowPage?: string; basePathToShowPage?: string;
infoTooltipContent?: string;
}; };

View File

@ -20,6 +20,7 @@ export type MenuItemProps = {
accent?: MenuItemAccent; accent?: MenuItemAccent;
text: string; text: string;
iconButtons?: MenuItemIconButton[]; iconButtons?: MenuItemIconButton[];
isTooltipOpen?: boolean;
className?: string; className?: string;
testId?: string; testId?: string;
onClick?: () => void; onClick?: () => void;
@ -30,6 +31,7 @@ export const MenuItem = ({
accent = 'default', accent = 'default',
text, text,
iconButtons, iconButtons,
isTooltipOpen,
className, className,
testId, testId,
onClick, onClick,
@ -42,6 +44,7 @@ export const MenuItem = ({
onClick={onClick} onClick={onClick}
className={className} className={className}
accent={accent} accent={accent}
isMenuOpen={!!isTooltipOpen}
> >
<StyledMenuItemLeftContent> <StyledMenuItemLeftContent>
<MenuItemLeftContent LeftIcon={LeftIcon ?? undefined} text={text} /> <MenuItemLeftContent LeftIcon={LeftIcon ?? undefined} text={text} />

View File

@ -11,6 +11,7 @@ export type MenuItemDraggableProps = {
LeftIcon: IconComponent | undefined; LeftIcon: IconComponent | undefined;
accent?: MenuItemAccent; accent?: MenuItemAccent;
iconButtons?: MenuItemIconButton[]; iconButtons?: MenuItemIconButton[];
isTooltipOpen?: boolean;
onClick?: () => void; onClick?: () => void;
text: string; text: string;
isDragDisabled?: boolean; isDragDisabled?: boolean;
@ -20,6 +21,7 @@ export const MenuItemDraggable = ({
LeftIcon, LeftIcon,
accent = 'default', accent = 'default',
iconButtons, iconButtons,
isTooltipOpen,
onClick, onClick,
text, text,
isDragDisabled = false, isDragDisabled = false,
@ -32,17 +34,19 @@ export const MenuItemDraggable = ({
onClick={onClick} onClick={onClick}
accent={accent} accent={accent}
className={className} className={className}
isMenuOpen={!!isTooltipOpen}
> >
<MenuItemLeftContent <MenuItemLeftContent
LeftIcon={LeftIcon} LeftIcon={LeftIcon}
text={text} text={text}
showGrip={!isDragDisabled} showGrip={!isDragDisabled}
/> />
<div className="hoverable-buttons"> {showIconButtons && (
{showIconButtons && ( <FloatingIconButtonGroup
<FloatingIconButtonGroup iconButtons={iconButtons} /> className="hoverable-buttons"
)} iconButtons={iconButtons}
</div> />
)}
</StyledHoverableMenuItemBase> </StyledHoverableMenuItemBase>
); );
}; };

View File

@ -89,9 +89,11 @@ export const StyledMenuItemRightContent = styled.div`
flex-direction: row; flex-direction: row;
`; `;
export const StyledHoverableMenuItemBase = styled(StyledMenuItemBase)` export const StyledHoverableMenuItemBase = styled(StyledMenuItemBase)<{
isMenuOpen: boolean;
}>`
& .hoverable-buttons { & .hoverable-buttons {
opacity: 0; opacity: ${({ isMenuOpen }) => (isMenuOpen ? 1 : 0)};
pointer-events: none; pointer-events: none;
position: fixed; position: fixed;
right: ${({ theme }) => theme.spacing(2)}; right: ${({ theme }) => theme.spacing(2)};

View File

@ -1,3 +1,5 @@
import { useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { import {
DropResult, DropResult,
OnDragEndResponder, OnDragEndResponder,
@ -9,8 +11,12 @@ import { DraggableList } from '@/ui/draggable-list/components/DraggableList';
import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer'; import { StyledDropdownMenuItemsContainer } from '@/ui/dropdown/components/StyledDropdownMenuItemsContainer';
import { StyledDropdownMenuSubheader } from '@/ui/dropdown/components/StyledDropdownMenuSubheader'; import { StyledDropdownMenuSubheader } from '@/ui/dropdown/components/StyledDropdownMenuSubheader';
import { IconMinus, IconPlus } from '@/ui/icon'; import { IconMinus, IconPlus } from '@/ui/icon';
import { IconInfoCircle } from '@/ui/input/constants/icons';
import { MenuItem } from '@/ui/menu-item/components/MenuItem'; import { MenuItem } from '@/ui/menu-item/components/MenuItem';
import { MenuItemDraggable } from '@/ui/menu-item/components/MenuItemDraggable'; import { MenuItemDraggable } from '@/ui/menu-item/components/MenuItemDraggable';
import { AppTooltip } from '@/ui/tooltip/AppTooltip';
import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside';
import { isDefined } from '~/utils/isDefined';
import { ViewFieldForVisibility } from '../types/ViewFieldForVisibility'; import { ViewFieldForVisibility } from '../types/ViewFieldForVisibility';
@ -33,8 +39,38 @@ export const ViewFieldsVisibilityDropdownSection = ({
onDragEnd?.(result, provided); onDragEnd?.(result, provided);
}; };
const [openToolTipIndex, setOpenToolTipIndex] = useState<number>();
const handleInfoButtonClick = (index: number) => {
if (index === openToolTipIndex) setOpenToolTipIndex(undefined);
else setOpenToolTipIndex(index);
};
const getIconButtons = (index: number, field: ViewFieldForVisibility) => { const getIconButtons = (index: number, field: ViewFieldForVisibility) => {
if (index !== 0) { const isFirstColumn = isDraggable && index === 0;
if (isFirstColumn && field.infoTooltipContent) {
return [
{
Icon: IconInfoCircle,
onClick: () => handleInfoButtonClick(index),
isActive: openToolTipIndex === index,
},
];
}
if (!isFirstColumn && field.infoTooltipContent) {
return [
{
Icon: IconInfoCircle,
onClick: () => handleInfoButtonClick(index),
isActive: openToolTipIndex === index,
},
{
Icon: field.isVisible ? IconMinus : IconPlus,
onClick: () => onVisibilityChange(field),
},
];
}
if (!isFirstColumn && !field.infoTooltipContent) {
return [ return [
{ {
Icon: field.isVisible ? IconMinus : IconPlus, Icon: field.isVisible ? IconMinus : IconPlus,
@ -44,11 +80,20 @@ export const ViewFieldsVisibilityDropdownSection = ({
} }
}; };
const ref = useRef<HTMLDivElement>(null);
useListenClickOutside({
refs: [ref],
callback: () => {
setOpenToolTipIndex(undefined);
},
});
return ( return (
<> <div ref={ref}>
<StyledDropdownMenuSubheader>{title}</StyledDropdownMenuSubheader> <StyledDropdownMenuSubheader>{title}</StyledDropdownMenuSubheader>
<StyledDropdownMenuItemsContainer> <StyledDropdownMenuItemsContainer>
{isDraggable && ( {isDraggable ? (
<DraggableList <DraggableList
onDragEnd={handleOnDrag} onDragEnd={handleOnDrag}
draggableItems={ draggableItems={
@ -64,8 +109,10 @@ export const ViewFieldsVisibilityDropdownSection = ({
key={field.key} key={field.key}
LeftIcon={field.Icon} LeftIcon={field.Icon}
iconButtons={getIconButtons(index, field)} iconButtons={getIconButtons(index, field)}
isTooltipOpen={openToolTipIndex === index}
text={field.name} text={field.name}
isDragDisabled={index === 0} isDragDisabled={index === 0}
className={`${title}-draggable-item-tooltip-anchor-${index}`}
/> />
} }
/> />
@ -73,22 +120,31 @@ export const ViewFieldsVisibilityDropdownSection = ({
</> </>
} }
/> />
)} ) : (
{!isDraggable && fields.map((field, index) => (
fields.map((field) => (
<MenuItem <MenuItem
key={field.key} key={field.key}
LeftIcon={field.Icon} LeftIcon={field.Icon}
iconButtons={[ iconButtons={getIconButtons(index, field)}
{ isTooltipOpen={openToolTipIndex === index}
Icon: field.isVisible ? IconMinus : IconPlus,
onClick: () => onVisibilityChange(field),
},
]}
text={field.name} text={field.name}
className={`${title}-fixed-item-tooltip-anchor-${index}`}
/> />
))} ))
)}
</StyledDropdownMenuItemsContainer> </StyledDropdownMenuItemsContainer>
</> {isDefined(openToolTipIndex) &&
createPortal(
<AppTooltip
anchorSelect={`.${title}-${
isDraggable ? 'draggable' : 'fixed'
}-item-tooltip-anchor-${openToolTipIndex}`}
place="left"
content={fields[openToolTipIndex].infoTooltipContent}
isOpen={true}
/>,
document.body,
)}
</div>
); );
}; };

View File

@ -3,7 +3,7 @@ import { FieldMetadata } from '@/ui/field/types/FieldMetadata';
export type ViewFieldForVisibility = Pick< export type ViewFieldForVisibility = Pick<
FieldDefinition<FieldMetadata>, FieldDefinition<FieldMetadata>,
'key' | 'name' | 'Icon' 'key' | 'name' | 'Icon' | 'infoTooltipContent'
> & { > & {
isVisible?: boolean; isVisible?: boolean;
index: number; index: number;