From 4c5b0a8f78a37b515fd3eef53898c5385612906e Mon Sep 17 00:00:00 2001 From: Igor Kretov Date: Mon, 30 Mar 2026 17:43:24 +0300 Subject: [PATCH 1/3] chore: Storybook shot selector support added --- Makefile | 3 ++- .../v1-components/storybook/scripts/shot.ts | 23 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index bdfeceb4..eddef699 100644 --- a/Makefile +++ b/Makefile @@ -45,11 +45,12 @@ storybook.serve: .require-compose ## [Build][docker] Runs Storybook for v1-compo storybook.shot: .require-compose ## [Research][docker] Captures a Storybook screenshot for v1-components docs/story page $(TARGET_HEADER) @$(COMPOSE) up -d v1-components - @UID=$$(id -u) GID=$$(id -g) $(COMPOSE) run --rm playwright \ + @$(COMPOSE) run --rm --user "$$(id -u):$$(id -g)" playwright \ yarn workspace @retailcrm/embed-ui-v1-components run storybook:shot \ --base-url http://v1-components:6006 \ --path "$(if $(story_path),$(story_path),/iframe.html?viewMode=docs&id=components-uitable--docs)" \ --output "$(if $(output),$(output),artifacts/storybook/UiTable.docs.png)" \ + $(if $(selector),--selector "$(selector)",) \ --wait-for-selector "$(if $(wait_for),$(wait_for),#storybook-docs)" \ --settle-ms "$(if $(settle_ms),$(settle_ms),2500)" \ --timeout-ms "$(if $(timeout_ms),$(timeout_ms),60000)" \ diff --git a/packages/v1-components/storybook/scripts/shot.ts b/packages/v1-components/storybook/scripts/shot.ts index 51db72fb..504a3b2f 100644 --- a/packages/v1-components/storybook/scripts/shot.ts +++ b/packages/v1-components/storybook/scripts/shot.ts @@ -10,6 +10,7 @@ import { chromium } from 'playwright' type CliOptions = { baseUrl: string; output: string; + selector?: string; settleMs: number; storyPath: string; timeoutMs: number; @@ -75,6 +76,11 @@ const parseArgs = (argv: string[]): CliOptions => { continue } + if (argument === '--selector') { + options.selector = readValue(argument) + continue + } + if (argument === '--timeout-ms') { options.timeoutMs = parseInteger(readValue(argument), argument) continue @@ -253,10 +259,19 @@ const run = async () => { await page.waitForTimeout(250) await mkdir(dirname(outputPath), { recursive: true }) - await page.screenshot({ - fullPage: true, - path: outputPath, - }) + if (options.selector) { + const locator = page.locator(options.selector).last() + + await locator.scrollIntoViewIfNeeded() + await locator.screenshot({ + path: outputPath, + }) + } else { + await page.screenshot({ + fullPage: true, + path: outputPath, + }) + } console.log(outputPath) } finally { From d6159d15493cc962ecf88b0c01a3001b6740644f Mon Sep 17 00:00:00 2001 From: Igor Kretov Date: Thu, 30 Apr 2026 18:23:01 +0400 Subject: [PATCH 2/3] feat(v1-components): UiLogicTree component was added Co-authored-by: Kirill Zaytsev --- Makefile | 12 + .../sprites/actions/add-square-outlined.svg | 8 + .../assets/sprites/actions/drag.svg | 8 + .../src/common/components/logic-tree.ts | 139 +++ .../src/common/components/popper.ts | 1 + .../src/host/components/index.ts | 5 + .../logic-tree/UiLogicTreeCaret.vue | 19 + .../logic-tree/UiLogicTreeCaret.vue.d.ts | 7 + .../components/logic-tree/UiLogicTreeNode.vue | 388 ++++++ .../logic-tree/UiLogicTreeNode.vue.d.ts | 7 + .../logic-tree/UiLogicTreeNodeIcon.vue | 5 + .../logic-tree/UiLogicTreeNodeIcon.vue.d.ts | 5 + .../logic-tree/UiLogicTreeNodeItem.vue | 21 + .../logic-tree/UiLogicTreeNodeItem.vue.d.ts | 5 + .../components/logic-tree/UiLogicTreeRoot.vue | 84 ++ .../logic-tree/UiLogicTreeRoot.vue.d.ts | 7 + .../src/host/components/logic-tree/i18n.ts | 13 + .../components/logic-tree/i18n/en-GB.json | 5 + .../components/logic-tree/i18n/es-ES.json | 5 + .../components/logic-tree/i18n/ru-RU.json | 5 + .../src/host/components/logic-tree/index.ts | 5 + .../components/logic-tree/logic-tree.less | 702 +++++++++++ packages/v1-components/src/remote.ts | 1 + .../components/logic-tree/UiLogicTree.vue | 1053 +++++++++++++++++ .../logic-tree/UiLogicTreeDragHandle.vue | 30 + .../src/remote/components/logic-tree/index.ts | 9 + .../src/remote/components/logic-tree/parts.ts | 63 + .../src/remote/components/popper.ts | 20 +- packages/v1-components/src/remote/endpoint.ts | 5 + .../storybook/createRemoteStoryRender.ts | 6 +- packages/v1-components/storybook/endpoint.ts | 34 +- .../storybook/host-components.ts | 39 + packages/v1-components/storybook/provider.ts | 19 + .../storybook/stories/UiField.stories.ts | 15 - .../storybook/stories/UiLogicTree.mdx | 188 +++ .../UiLogicTree.remote.example.module.less | 73 ++ .../stories/UiLogicTree.remote.example.vue | 715 +++++++++++ .../storybook/stories/UiLogicTree.remote.ts | 33 + .../storybook/stories/UiLogicTree.stories.ts | 616 ++++++++++ .../storybook/stories/UiPageHeader.stories.ts | 19 - .../stories/UiRadioSwitch.stories.ts | 11 - .../storybook/stories/UiSelect.stories.ts | 17 - .../storybook/stories/UiTab.stories.ts | 11 - .../storybook/stories/UiTable.stories.ts | 34 - .../stories/UiToggleGroup.stories.ts | 11 - .../tests/__fixtures__/logic-tree.worker.ts | 369 ++++++ .../host/components/logic-tree-node.test.ts | 95 ++ .../tests/logic-tree.worker.e2e.ts | 328 +++++ 48 files changed, 5118 insertions(+), 152 deletions(-) create mode 100644 packages/v1-components/assets/sprites/actions/add-square-outlined.svg create mode 100644 packages/v1-components/assets/sprites/actions/drag.svg create mode 100644 packages/v1-components/src/common/components/logic-tree.ts create mode 100644 packages/v1-components/src/host/components/logic-tree/UiLogicTreeCaret.vue create mode 100644 packages/v1-components/src/host/components/logic-tree/UiLogicTreeCaret.vue.d.ts create mode 100644 packages/v1-components/src/host/components/logic-tree/UiLogicTreeNode.vue create mode 100644 packages/v1-components/src/host/components/logic-tree/UiLogicTreeNode.vue.d.ts create mode 100644 packages/v1-components/src/host/components/logic-tree/UiLogicTreeNodeIcon.vue create mode 100644 packages/v1-components/src/host/components/logic-tree/UiLogicTreeNodeIcon.vue.d.ts create mode 100644 packages/v1-components/src/host/components/logic-tree/UiLogicTreeNodeItem.vue create mode 100644 packages/v1-components/src/host/components/logic-tree/UiLogicTreeNodeItem.vue.d.ts create mode 100644 packages/v1-components/src/host/components/logic-tree/UiLogicTreeRoot.vue create mode 100644 packages/v1-components/src/host/components/logic-tree/UiLogicTreeRoot.vue.d.ts create mode 100644 packages/v1-components/src/host/components/logic-tree/i18n.ts create mode 100644 packages/v1-components/src/host/components/logic-tree/i18n/en-GB.json create mode 100644 packages/v1-components/src/host/components/logic-tree/i18n/es-ES.json create mode 100644 packages/v1-components/src/host/components/logic-tree/i18n/ru-RU.json create mode 100644 packages/v1-components/src/host/components/logic-tree/index.ts create mode 100644 packages/v1-components/src/host/components/logic-tree/logic-tree.less create mode 100644 packages/v1-components/src/remote/components/logic-tree/UiLogicTree.vue create mode 100644 packages/v1-components/src/remote/components/logic-tree/UiLogicTreeDragHandle.vue create mode 100644 packages/v1-components/src/remote/components/logic-tree/index.ts create mode 100644 packages/v1-components/src/remote/components/logic-tree/parts.ts create mode 100644 packages/v1-components/storybook/host-components.ts create mode 100644 packages/v1-components/storybook/provider.ts create mode 100644 packages/v1-components/storybook/stories/UiLogicTree.mdx create mode 100644 packages/v1-components/storybook/stories/UiLogicTree.remote.example.module.less create mode 100644 packages/v1-components/storybook/stories/UiLogicTree.remote.example.vue create mode 100644 packages/v1-components/storybook/stories/UiLogicTree.remote.ts create mode 100644 packages/v1-components/storybook/stories/UiLogicTree.stories.ts create mode 100644 packages/v1-components/tests/__fixtures__/logic-tree.worker.ts create mode 100644 packages/v1-components/tests/host/components/logic-tree-node.test.ts create mode 100644 packages/v1-components/tests/logic-tree.worker.e2e.ts diff --git a/Makefile b/Makefile index eddef699..c4421117 100644 --- a/Makefile +++ b/Makefile @@ -84,6 +84,18 @@ else endif $(TARGET_OK) +.PHONY: tests-e2e +tests-e2e: .require-compose ## [Tests][docker] Runs v1-components browser e2e tests + $(TARGET_HEADER) +ifdef cli + @$(COMPOSE) run --rm --user "$$(id -u):$$(id -g)" playwright \ + yarn workspace @retailcrm/embed-ui-v1-components run test:e2e $(cli) +else + @$(COMPOSE) run --rm --user "$$(id -u):$$(id -g)" playwright \ + yarn workspace @retailcrm/embed-ui-v1-components run test:e2e +endif + $(TARGET_OK) + .PHONY: tests-coverage tests-coverage: .require-compose ## [Tests][docker][heavy] Runs autotests with coverage report $(TARGET_HEADER) diff --git a/packages/v1-components/assets/sprites/actions/add-square-outlined.svg b/packages/v1-components/assets/sprites/actions/add-square-outlined.svg new file mode 100644 index 00000000..01a7bc12 --- /dev/null +++ b/packages/v1-components/assets/sprites/actions/add-square-outlined.svg @@ -0,0 +1,8 @@ + + + diff --git a/packages/v1-components/assets/sprites/actions/drag.svg b/packages/v1-components/assets/sprites/actions/drag.svg new file mode 100644 index 00000000..4b96ef5a --- /dev/null +++ b/packages/v1-components/assets/sprites/actions/drag.svg @@ -0,0 +1,8 @@ + + + diff --git a/packages/v1-components/src/common/components/logic-tree.ts b/packages/v1-components/src/common/components/logic-tree.ts new file mode 100644 index 00000000..e6b093bf --- /dev/null +++ b/packages/v1-components/src/common/components/logic-tree.ts @@ -0,0 +1,139 @@ +export enum LogicTreeNodeKind { + BRANCH = 'branch', + CONDITION = 'condition', + GROUP = 'group', +} + +export enum LogicTreeChildrenView { + GROUPED = 'grouped', + PLAIN = 'plain', +} + +export enum LogicTreeConjunction { + AND = 'and', + OR = 'or', +} + +export enum LogicTreeNodeView { + ACTIONS = 'actions', + SUMMARY = 'summary', +} + +export enum LogicTreeTone { + BLUE = 'blue', + GREEN = 'green', + GREY = 'grey', + RED = 'red', + YELLOW = 'yellow', +} + +export type UiLogicTreeConnector = { + continues: boolean; + placeholder?: boolean; + tone: LogicTreeTone; + visible: boolean; +} + +export type UiLogicTreeNodeData> = { + view: LogicTreeNodeView; + editable: boolean; + disabled?: boolean; + draggable?: boolean; + selected?: boolean; + highlighted?: boolean; +} & TNodeData + +export type UiLogicTreeNode> = { + id: string; + kind: LogicTreeNodeKind; + tone?: LogicTreeTone; + conjunction?: LogicTreeConjunction | string; + collapsible?: boolean; + expanded?: boolean; + childrenView?: LogicTreeChildrenView; + children?: UiLogicTreeNode[]; + data: UiLogicTreeNodeData; +} + +export type UiLogicTreeProperties> = { + items?: UiLogicTreeNode[]; +} + +export type UiLogicTreeDropPayload = { + itemId: string; + sourceContainerId: string; + targetContainerId: string; + targetIndex: number | null; + targetItemId: string | null; + placement: 'after' | 'before'; + payload?: unknown; +} + +export type UiLogicTreeNodeAddPayload = { + actionId: string; + kind: Exclude; + parentNodeId: string | null; + parentPathKey: string | null; + triggerNodeId: string; +} + +export type UiLogicTreeNodeEditPayload = { + controlId: string; + nodeId: string; + pathKey: string; + value: string | number | null; +} + +export type UiLogicTreeNodeRemovePayload = { + index: number; + nodeId: string; + parentNodeId: string | null; + parentPathKey: string | null; + pathKey: string; +} + +export type UiLogicTreeNodeSlotProps> = { + editing: boolean; + expanded: boolean; + grouped: boolean; + groupedHeader: boolean; + groupedPosition?: UiLogicTreeNodeProperties['groupedPosition']; + hasChildren: boolean; + highlighted: boolean; + disabled: boolean; + node: UiLogicTreeNode; + path: number[]; + pathKey: string; + nodeView: LogicTreeNodeView; + selected: boolean; + onAction: (actionId: string, kind: Exclude['kind'], LogicTreeNodeKind.BRANCH>) => void; + onControlAction: (controlId: string) => void; + onControlUpdate: (controlId: string, value: string | number | null) => void; + onRemove: () => void; + onToggle: () => void; +} + +export type UiLogicTreeRootProperties = Record + +export type UiLogicTreeCaretProperties = { + active?: boolean; +} + +export type UiLogicTreeNodeProperties = { + pathKey?: string; + nodeView?: LogicTreeNodeView; + connectors?: UiLogicTreeConnector[]; + conjunction?: string; + conjunctionEndPathKey?: string; + conjunctionLabel?: string; + conjunctionOffset?: number; + conjunctionStartPathKey?: string; + conjunctionTone?: LogicTreeTone; + groupedHeader?: boolean; + grouped?: boolean; + groupedPosition?: 'end' | 'middle' | 'single' | 'start'; + editable?: boolean; + disabled?: boolean; + highlighted?: boolean; + selected?: boolean; +} diff --git a/packages/v1-components/src/common/components/popper.ts b/packages/v1-components/src/common/components/popper.ts index b11e5dd2..058f5cf2 100644 --- a/packages/v1-components/src/common/components/popper.ts +++ b/packages/v1-components/src/common/components/popper.ts @@ -34,6 +34,7 @@ export type FloatingOptions = { } export type ShowingOptions = { + visible?: boolean; shown?: boolean; targetTriggers?: Trigger[] | TriggerSchema; popperTriggers?: Trigger[] | TriggerSchema; diff --git a/packages/v1-components/src/host/components/index.ts b/packages/v1-components/src/host/components/index.ts index f777ec60..04d9182f 100644 --- a/packages/v1-components/src/host/components/index.ts +++ b/packages/v1-components/src/host/components/index.ts @@ -16,6 +16,11 @@ export { default as UiImage } from '@/host/components/image/UiImage.vue' export { default as UiInfobox } from '@/host/components/infobox/UiInfobox.vue' export { default as UiLink } from '@/host/components/link/UiLink.vue' export { default as UiLoader } from '@/host/components/loader/UiLoader.vue' +export { default as UiLogicTreeCaret } from '@/host/components/logic-tree/UiLogicTreeCaret.vue' +export { default as UiLogicTreeNode } from '@/host/components/logic-tree/UiLogicTreeNode.vue' +export { default as UiLogicTreeNodeIcon } from '@/host/components/logic-tree/UiLogicTreeNodeIcon.vue' +export { default as UiLogicTreeNodeItem } from '@/host/components/logic-tree/UiLogicTreeNodeItem.vue' +export { default as UiLogicTreeRoot } from '@/host/components/logic-tree/UiLogicTreeRoot.vue' export { default as UiMenuItem } from '@/host/components/menu/UiMenuItem.vue' export { default as UiMenuItemGroup } from '@/host/components/menu/UiMenuItemGroup.vue' export { default as UiModalSidebar } from '@/host/components/modal-sidebar/UiModalSidebar.vue' diff --git a/packages/v1-components/src/host/components/logic-tree/UiLogicTreeCaret.vue b/packages/v1-components/src/host/components/logic-tree/UiLogicTreeCaret.vue new file mode 100644 index 00000000..6b91c7bc --- /dev/null +++ b/packages/v1-components/src/host/components/logic-tree/UiLogicTreeCaret.vue @@ -0,0 +1,19 @@ + + + diff --git a/packages/v1-components/src/host/components/logic-tree/UiLogicTreeCaret.vue.d.ts b/packages/v1-components/src/host/components/logic-tree/UiLogicTreeCaret.vue.d.ts new file mode 100644 index 00000000..27832358 --- /dev/null +++ b/packages/v1-components/src/host/components/logic-tree/UiLogicTreeCaret.vue.d.ts @@ -0,0 +1,7 @@ +import type { DefineComponent } from '@/common/vue' + +import type { UiLogicTreeCaretProperties } from '@/common/components/logic-tree' + +declare const UiLogicTreeCaret: DefineComponent + +export default UiLogicTreeCaret diff --git a/packages/v1-components/src/host/components/logic-tree/UiLogicTreeNode.vue b/packages/v1-components/src/host/components/logic-tree/UiLogicTreeNode.vue new file mode 100644 index 00000000..62262cde --- /dev/null +++ b/packages/v1-components/src/host/components/logic-tree/UiLogicTreeNode.vue @@ -0,0 +1,388 @@ + + + + +