feat: fix Command Menu Side Panel Layout (#15883)
[Figma Design](https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=81380-344641&t=FpjWNOK2gZuDQQfr-0) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Adds a side panel layout for the Command Menu, routes modals into a local container, updates the top bar and context chips, and standardizes small button sizes. > > - **Command Menu**: > - **Side Panel Layout**: Introduces `CommandMenuSidePanelLayout` with animated width, hosts `CommandMenuRouter`, and provides a modal container via `ModalContainerContext`. > - **Top Bar**: Redesign (`CommandMenuTopBar`) with back icon, optional AI sparkles action, compact height (`COMMAND_MENU_SEARCH_BAR_HEIGHT=40`), and updated placeholder. > - **Context Chips**: Adds `CommandMenuLastContextChip` and `CommandMenuRecordInfo`; extends `CommandMenuContextChip` with `page` prop; updates `CommandMenuContextChipGroups` to render last chip as record info when applicable. > - **Container Simplification**: `CommandMenuContainer` simplified to just provide contexts and `AgentChatProvider`. > - **Modal System**: > - Adds `ModalContainerContext` and updates `Modal` to portal into provided container; `Modal.Backdrop` supports `isInContainer`. > - Updates usages (e.g., `UserOrMetadataLoader`, `ActionModal`) to align with new modal behavior. > - **Page Integration**: > - Replaces `PageBody` with `CommandMenuSidePanelLayout` in `RecordShowPage` and `RecordIndexContainerGater`. > - Removes global `CommandMenuRouter` from `DefaultLayout` (keeps keyboard shortcuts). > - **UI/Styling**: > - Standardizes several buttons to `size="small"` (e.g., command actions, open record, options, reply, workflow footer). > - Adjusts `ShowPageSubContainer` styling when rendered inside command menu. > - Storybook tests updated for new placeholder text. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 81fcaa145618a2fa1c3e11e2dc88fe832c51374c. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> Co-authored-by: Devessier <baptiste@devessier.fr> Co-authored-by: Aman Raj <92664006+araj00@users.noreply.github.com> Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> Co-authored-by: Paul Rastoin <45004772+prastoin@users.noreply.github.com>
This commit is contained in:
+94
@@ -0,0 +1,94 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useUpdateAgentLabel } from '../useUpdateAgentLabel';
|
||||
|
||||
const mockUpdateAgent = jest.fn();
|
||||
const mockUseFindOneAgentQuery = jest.fn();
|
||||
|
||||
jest.mock('~/generated-metadata/graphql', () => ({
|
||||
useFindOneAgentQuery: jest.fn(),
|
||||
useUpdateOneAgentMutation: () => [mockUpdateAgent],
|
||||
}));
|
||||
|
||||
describe('useUpdateAgentLabel', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mockUseFindOneAgentQuery.mockReturnValue({
|
||||
data: undefined,
|
||||
});
|
||||
(
|
||||
require('~/generated-metadata/graphql').useFindOneAgentQuery as jest.Mock
|
||||
).mockImplementation(mockUseFindOneAgentQuery);
|
||||
});
|
||||
|
||||
it('should skip query when agentId is undefined', () => {
|
||||
renderHook(() => useUpdateAgentLabel(undefined));
|
||||
|
||||
expect(mockUseFindOneAgentQuery).toHaveBeenCalledWith({
|
||||
variables: { id: '' },
|
||||
skip: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should fetch agent when agentId is provided', () => {
|
||||
const mockAgent = {
|
||||
id: 'agent-123',
|
||||
label: 'Original Label',
|
||||
};
|
||||
|
||||
mockUseFindOneAgentQuery.mockReturnValue({
|
||||
data: {
|
||||
findOneAgent: mockAgent,
|
||||
},
|
||||
});
|
||||
|
||||
renderHook(() => useUpdateAgentLabel('agent-123'));
|
||||
|
||||
expect(mockUseFindOneAgentQuery).toHaveBeenCalledWith({
|
||||
variables: { id: 'agent-123' },
|
||||
skip: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should update agent label when agent is defined', async () => {
|
||||
const mockAgent = {
|
||||
id: 'agent-123',
|
||||
label: 'Original Label',
|
||||
};
|
||||
|
||||
mockUseFindOneAgentQuery.mockReturnValue({
|
||||
data: {
|
||||
findOneAgent: mockAgent,
|
||||
},
|
||||
});
|
||||
|
||||
mockUpdateAgent.mockResolvedValue({});
|
||||
|
||||
const { result } = renderHook(() => useUpdateAgentLabel('agent-123'));
|
||||
|
||||
await result.current.updateAgentLabel('New Label');
|
||||
|
||||
expect(mockUpdateAgent).toHaveBeenCalledWith({
|
||||
variables: {
|
||||
input: {
|
||||
id: 'agent-123',
|
||||
label: 'New Label',
|
||||
},
|
||||
},
|
||||
refetchQueries: ['FindOneAgent'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should not update agent label when agent is undefined', async () => {
|
||||
mockUseFindOneAgentQuery.mockReturnValue({
|
||||
data: {
|
||||
findOneAgent: undefined,
|
||||
},
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useUpdateAgentLabel('agent-123'));
|
||||
|
||||
await result.current.updateAgentLabel('New Label');
|
||||
|
||||
expect(mockUpdateAgent).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
useFindOneAgentQuery,
|
||||
useUpdateOneAgentMutation,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
export const useUpdateAgentLabel = (agentId: string | undefined) => {
|
||||
const { data: agentData } = useFindOneAgentQuery({
|
||||
variables: { id: agentId || '' },
|
||||
skip: !isDefined(agentId),
|
||||
});
|
||||
|
||||
const [updateAgent] = useUpdateOneAgentMutation();
|
||||
|
||||
const agent = agentData?.findOneAgent;
|
||||
|
||||
const updateAgentLabel = async (newLabel: string) => {
|
||||
if (!isDefined(agent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await updateAgent({
|
||||
variables: {
|
||||
input: {
|
||||
id: agent.id,
|
||||
label: newLabel,
|
||||
},
|
||||
},
|
||||
refetchQueries: ['FindOneAgent'],
|
||||
});
|
||||
};
|
||||
|
||||
return { updateAgentLabel };
|
||||
};
|
||||
Reference in New Issue
Block a user