Refactor command menu items deprecated code (#19508)

- Removes the intermediate `CommandMenuItemConfig` /
`CommandConfigContext` / `CommandMenuItemDisplay` abstraction layers,
replacing them with a single `CommandMenuItemRenderer` that renders
directly from the command menu items from the backend
- Eliminates the server-items/ subdirectory by moving its contents
(hooks/, contexts/, states/, display/, edit/) up into the parent
command-menu-item/ module, removing an unnecessary nesting level.
This commit is contained in:
Raphaël Bosi
2026-04-10 11:28:27 +02:00
committed by GitHub
parent f13e7e01fe
commit 4b3a46d953
132 changed files with 1376 additions and 2723 deletions
@@ -0,0 +1,39 @@
import { getVisibleCommandMenuItemCountForContainerWidth } from '@/command-menu-item/display/utils/getVisibleCommandMenuItemCountForContainerWidth';
describe('getVisibleCommandMenuItemCountForContainerWidth', () => {
it('should return all items when container width is zero', () => {
const visibleCommandMenuItemCount =
getVisibleCommandMenuItemCountForContainerWidth({
commandMenuItemKeysInDisplayOrder: ['a', 'b', 'c'],
commandMenuItemWidthsByKey: { a: 20, b: 20, c: 20 },
commandMenuItemsContainerWidth: 0,
commandMenuItemsGapWidth: 8,
});
expect(visibleCommandMenuItemCount).toBe(3);
});
it('should return all items when at least one width is missing', () => {
const visibleCommandMenuItemCount =
getVisibleCommandMenuItemCountForContainerWidth({
commandMenuItemKeysInDisplayOrder: ['a', 'b'],
commandMenuItemWidthsByKey: { a: 20 },
commandMenuItemsContainerWidth: 40,
commandMenuItemsGapWidth: 8,
});
expect(visibleCommandMenuItemCount).toBe(2);
});
it('should stop before exceeding the container width with gaps', () => {
const visibleCommandMenuItemCount =
getVisibleCommandMenuItemCountForContainerWidth({
commandMenuItemKeysInDisplayOrder: ['a', 'b', 'c'],
commandMenuItemWidthsByKey: { a: 40, b: 40, c: 40 },
commandMenuItemsContainerWidth: 100,
commandMenuItemsGapWidth: 8,
});
expect(visibleCommandMenuItemCount).toBe(2);
});
});
@@ -0,0 +1,42 @@
type GetVisibleCommandMenuItemCountForContainerWidthParams = {
commandMenuItemKeysInDisplayOrder: string[];
commandMenuItemWidthsByKey: Record<string, number>;
commandMenuItemsContainerWidth: number;
commandMenuItemsGapWidth: number;
};
export const getVisibleCommandMenuItemCountForContainerWidth = ({
commandMenuItemKeysInDisplayOrder,
commandMenuItemWidthsByKey,
commandMenuItemsContainerWidth,
commandMenuItemsGapWidth,
}: GetVisibleCommandMenuItemCountForContainerWidthParams): number => {
if (commandMenuItemsContainerWidth <= 0) {
return commandMenuItemKeysInDisplayOrder.length;
}
let usedWidth = 0;
let visibleCommandMenuItemCount = 0;
for (const commandMenuItemKey of commandMenuItemKeysInDisplayOrder) {
const commandMenuItemWidth = commandMenuItemWidthsByKey[commandMenuItemKey];
if (typeof commandMenuItemWidth !== 'number') {
return commandMenuItemKeysInDisplayOrder.length;
}
const nextWidth =
visibleCommandMenuItemCount === 0
? commandMenuItemWidth
: usedWidth + commandMenuItemsGapWidth + commandMenuItemWidth;
if (nextWidth > commandMenuItemsContainerWidth) {
break;
}
usedWidth = nextWidth;
visibleCommandMenuItemCount += 1;
}
return visibleCommandMenuItemCount;
};
@@ -0,0 +1,32 @@
import { type CommandMenuContextApi, type Nullable } from 'twenty-shared/types';
import { interpolateCommandMenuItemTemplate } from 'twenty-shared/utils';
import { type CommandMenuItemFieldsFragment } from '~/generated-metadata/graphql';
type InterpolatedCommandMenuItemFields = {
iconKey: Nullable<string>;
label: string;
shortLabel: Nullable<string>;
};
export const interpolateCommandMenuItemFields = (
item: CommandMenuItemFieldsFragment,
commandMenuContextApi: CommandMenuContextApi,
): InterpolatedCommandMenuItemFields => {
const iconKey = interpolateCommandMenuItemTemplate({
label: item.icon,
context: commandMenuContextApi,
});
const label =
interpolateCommandMenuItemTemplate({
label: item.label,
context: commandMenuContextApi,
}) ?? item.label;
const shortLabel = interpolateCommandMenuItemTemplate({
label: item.shortLabel,
context: commandMenuContextApi,
});
return { iconKey, label, shortLabel };
};