From 1c261f85a316a0e769864d82d80cda088ed9d6d1 Mon Sep 17 00:00:00 2001 From: Alex Grozav Date: Mon, 29 Apr 2024 07:53:42 +0300 Subject: [PATCH] refactor(editor): Migrate header WorkflowDetails to composition api (no-changelog) (#9186) --- .../N8nActionDropdown/ActionDropdown.vue | 16 +- packages/design-system/src/css/tag.scss | 6 +- .../src/types/action-dropdown.ts | 11 + packages/design-system/src/types/index.ts | 1 + packages/editor-ui/src/__tests__/setup.ts | 25 + .../src/components/MainHeader/MainHeader.vue | 7 +- .../MainHeader/WorkflowDetails.spec.ts | 115 ++ .../components/MainHeader/WorkflowDetails.vue | 1115 ++++++++--------- .../editor-ui/src/components/TagsDropdown.vue | 2 +- .../src/composables/useContextMenu.ts | 9 +- packages/editor-ui/src/n8n-theme.scss | 4 +- 11 files changed, 716 insertions(+), 595 deletions(-) create mode 100644 packages/design-system/src/types/action-dropdown.ts create mode 100644 packages/editor-ui/src/components/MainHeader/WorkflowDetails.spec.ts diff --git a/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue b/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue index 063b228a35274..2ec50e72bc066 100644 --- a/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue +++ b/packages/design-system/src/components/N8nActionDropdown/ActionDropdown.vue @@ -61,23 +61,13 @@ import { ref, useCssModule, useAttrs, computed } from 'vue'; import { ElDropdown, ElDropdownMenu, ElDropdownItem, type Placement } from 'element-plus'; import N8nIcon from '../N8nIcon'; import { N8nKeyboardShortcut } from '../N8nKeyboardShortcut'; -import type { KeyboardShortcut } from '../../types'; +import type { ActionDropdownItem } from '../../types'; import type { IconSize } from '@/types/icon'; -interface IActionDropdownItem { - id: string; - label: string; - icon?: string; - divided?: boolean; - disabled?: boolean; - shortcut?: KeyboardShortcut; - customClass?: string; -} - const TRIGGER = ['click', 'hover'] as const; interface ActionDropdownProps { - items: IActionDropdownItem[]; + items: ActionDropdownItem[]; placement?: Placement; activatorIcon?: string; activatorSize?: IconSize; @@ -99,7 +89,7 @@ const $attrs = useAttrs(); const testIdPrefix = $attrs['data-test-id']; const $style = useCssModule(); -const getItemClasses = (item: IActionDropdownItem): Record => { +const getItemClasses = (item: ActionDropdownItem): Record => { return { [$style.itemContainer]: true, [$style.disabled]: !!item.disabled, diff --git a/packages/design-system/src/css/tag.scss b/packages/design-system/src/css/tag.scss index a1634b5d4ab84..10b09cf32b212 100644 --- a/packages/design-system/src/css/tag.scss +++ b/packages/design-system/src/css/tag.scss @@ -127,6 +127,7 @@ border-radius: var.$tag-border-radius; box-sizing: border-box; white-space: nowrap; + line-height: 1; .el-icon.el-tag__close { border-radius: 50%; @@ -137,9 +138,8 @@ height: 16px; width: 16px; line-height: 16px; - vertical-align: middle; - top: -1px; - right: -5px; + margin-top: 0; + margin-right: 0; &::before { display: block; diff --git a/packages/design-system/src/types/action-dropdown.ts b/packages/design-system/src/types/action-dropdown.ts new file mode 100644 index 0000000000000..8caf6560f8fc3 --- /dev/null +++ b/packages/design-system/src/types/action-dropdown.ts @@ -0,0 +1,11 @@ +import type { KeyboardShortcut } from '@/types/keyboardshortcut'; + +export interface ActionDropdownItem { + id: string; + label: string; + icon?: string; + divided?: boolean; + disabled?: boolean; + shortcut?: KeyboardShortcut; + customClass?: string; +} diff --git a/packages/design-system/src/types/index.ts b/packages/design-system/src/types/index.ts index eec00e1154eea..1336f17b63149 100644 --- a/packages/design-system/src/types/index.ts +++ b/packages/design-system/src/types/index.ts @@ -1,3 +1,4 @@ +export * from './action-dropdown'; export * from './button'; export * from './datatable'; export * from './form'; diff --git a/packages/editor-ui/src/__tests__/setup.ts b/packages/editor-ui/src/__tests__/setup.ts index 4c75c226e6d37..3b9c07462ea29 100644 --- a/packages/editor-ui/src/__tests__/setup.ts +++ b/packages/editor-ui/src/__tests__/setup.ts @@ -19,3 +19,28 @@ Range.prototype.getClientRects = vi.fn(() => ({ length: 0, [Symbol.iterator]: vi.fn(), })); + +export class IntersectionObserver { + root = null; + rootMargin = ''; + thresholds = []; + + disconnect() { + return null; + } + + observe() { + return null; + } + + takeRecords() { + return []; + } + + unobserve() { + return null; + } +} + +window.IntersectionObserver = IntersectionObserver; +global.IntersectionObserver = IntersectionObserver; diff --git a/packages/editor-ui/src/components/MainHeader/MainHeader.vue b/packages/editor-ui/src/components/MainHeader/MainHeader.vue index 668b8b5d2a580..53eb1fd6348b0 100644 --- a/packages/editor-ui/src/components/MainHeader/MainHeader.vue +++ b/packages/editor-ui/src/components/MainHeader/MainHeader.vue @@ -2,7 +2,7 @@
- + { + const actual = await import('vue-router'); + + return { + ...actual, + useRoute: () => ({ + value: { + params: { + id: '1', + }, + }, + }), + }; +}); + +const initialState = { + [STORES.SETTINGS]: { + settings: { + enterprise: { + sharing: true, + }, + }, + areTagsEnabled: true, + }, + [STORES.TAGS]: { + tags: { + 1: { + id: '1', + name: 'tag1', + }, + 2: { + id: '2', + name: 'tag2', + }, + }, + }, +}; + +const renderComponent = createComponentRenderer(WorkflowDetails, { + pinia: createTestingPinia({ initialState }), +}); + +describe('WorkflowDetails', () => { + it('renders workflow name and tags', async () => { + const workflow = { + id: '1', + name: 'Test Workflow', + tags: ['1', '2'], + }; + + const { getByTestId, getByText } = renderComponent({ + props: { + workflow, + readOnly: false, + }, + }); + + const workflowName = getByTestId('workflow-name-input'); + const workflowNameInput = workflowName.querySelector('input'); + + expect(workflowNameInput).toHaveValue('Test Workflow'); + expect(getByText('tag1')).toBeInTheDocument(); + expect(getByText('tag2')).toBeInTheDocument(); + }); + + it('calls save function on save button click', async () => { + const onSaveButtonClick = vi.fn(); + const { getByTestId } = renderComponent({ + props: { + workflow: { + id: '1', + name: 'Test Workflow', + tags: [], + }, + readOnly: false, + }, + global: { + mocks: { + onSaveButtonClick, + }, + }, + }); + + await fireEvent.click(getByTestId('workflow-save-button')); + expect(onSaveButtonClick).toHaveBeenCalled(); + }); + + it('opens share modal on share button click', async () => { + const onShareButtonClick = vi.fn(); + const { getByTestId } = renderComponent({ + props: { + workflow: { + id: '1', + name: 'Test Workflow', + tags: [], + }, + readOnly: false, + }, + global: { + mocks: { + onShareButtonClick, + }, + }, + }); + + await fireEvent.click(getByTestId('workflow-share-button')); + expect(onShareButtonClick).toHaveBeenCalled(); + }); +}); diff --git a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue index b38f72235ce98..4dc59ef378f29 100644 --- a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue +++ b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue @@ -1,16 +1,536 @@ + +