From fb40d459150fa527eebbe616414792e63454fdbb Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Wed, 6 Mar 2024 16:01:04 +0100 Subject: [PATCH] Improve GLSP UI Extension mechanism for easier re-use of HTML elements - Allow more fine-grained definition of container and parent - Allow more fine-grained definition of container within parent - Replace hard-coded styles with 'hidden' class - Rework index file to define structure and use grid for layouting - Properly align ALL index files with same structure, loading and style - Remove workaround for toolbar height (48px adjustment) - Ensure Quick Action UI is still rendering correctly Minor fix: - Ensure that mouse move is only ever executed on drag --- integration/eclipse/index.html | 140 ++++++++++++------ integration/eclipse/src/index.css | 33 +++-- integration/standalone/index.html | 54 ++++++- integration/standalone/src/index.css | 30 ++-- integration/viewer/index.html | 130 ++++++++++++++-- integration/viewer/src/index.css | 30 ++-- packages/editor/src/diagram/di.config.ts | 8 +- packages/editor/src/diagram/diagram.css | 10 +- packages/editor/src/diagram/model.ts | 5 - packages/editor/src/index.ts | 1 + packages/editor/src/jump/jump-out-ui.ts | 10 +- .../editor/src/tools/change-bounds-tool.ts | 19 ++- .../ui-tools/quick-action/quick-action-ui.ts | 8 +- .../editor/src/ui-tools/tool-bar/tool-bar.css | 1 - .../editor/src/ui-tools/tool-bar/tool-bar.ts | 4 +- .../src/ui-tools/viewport/viewport-bar.ts | 9 +- .../ui-tools/viewport/viewport-commands.ts | 2 +- packages/editor/src/utils/ivy-ui-extension.ts | 94 ++++++++++++ .../src/inscription/inscription-ui.tsx | 36 +---- playwright/tests/page-objects/inscription.ts | 2 +- playwright/tests/page-objects/toolbar.ts | 2 +- playwright/tests/page-objects/viewport.ts | 2 +- 22 files changed, 450 insertions(+), 180 deletions(-) create mode 100644 packages/editor/src/utils/ivy-ui-extension.ts diff --git a/integration/eclipse/index.html b/integration/eclipse/index.html index 1969f7dd..224d35b1 100644 --- a/integration/eclipse/index.html +++ b/integration/eclipse/index.html @@ -8,61 +8,109 @@ Process Editor + + -
-
-
-
+
+
+
+ + +
+
+
+
-
-
-
-
- - +
+
+
+ + + + +
+
+
+
-
-
+
+
+
-
-
- -
- - + +
-
- + - + \ No newline at end of file diff --git a/integration/eclipse/src/index.css b/integration/eclipse/src/index.css index 0317cbb2..8cf18bdc 100644 --- a/integration/eclipse/src/index.css +++ b/integration/eclipse/src/index.css @@ -5,26 +5,29 @@ body { overflow: hidden; } -body { +#process-editor { display: grid; - grid-template-areas: 'sprotty inscription'; - grid-auto-columns: minmax(250px, 100%) min-content; -} -#sprotty { - grid-area: sprotty; - height: 100vh; + grid-template-columns: 1fr auto; /* Adjust the auto value based on sidebar width */ + grid-template-rows: auto 1fr; /* Adjust based on toolbar height */ + height: 100vh; /* Full viewport height */ } -#inscription { - grid-area: inscription; - background-color: var(--glsp-editor-background); - height: 100vh; + +#sprotty, +.main-widget { + grid-column: 1 / 2; + grid-row: 2 / 3; + position: relative; /* to ensure that absolute positioning of elements within the diagram work relative to the diagram. */ } -#inscription.hidden { - display: none; + +#ivy-tool-bar { + grid-column: 1 / 2; + grid-row: 1 / 2; } -.main-widget { - position: relative; +#inscription-ui { + grid-column: 2 / 3; + grid-row: 1 / 3; /* Spanning across both the rows */ + background-color: var(--glsp-editor-background); } .sprotty svg { diff --git a/integration/standalone/index.html b/integration/standalone/index.html index 17b57bb6..224d35b1 100644 --- a/integration/standalone/index.html +++ b/integration/standalone/index.html @@ -8,13 +8,26 @@ Process Editor + + + + + +
+
+
+ + +
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+
+
+
+
+ +
+
+ +
+ + + + \ No newline at end of file diff --git a/integration/viewer/src/index.css b/integration/viewer/src/index.css index 0317cbb2..a6f07cb2 100644 --- a/integration/viewer/src/index.css +++ b/integration/viewer/src/index.css @@ -5,26 +5,28 @@ body { overflow: hidden; } -body { +#process-editor { display: grid; - grid-template-areas: 'sprotty inscription'; - grid-auto-columns: minmax(250px, 100%) min-content; + grid-template-columns: 1fr auto; /* Adjust the auto value based on sidebar width */ + grid-template-rows: auto 1fr; /* Adjust based on toolbar height */ + height: 100vh; /* Full viewport height */ } + #sprotty { - grid-area: sprotty; - height: 100vh; -} -#inscription { - grid-area: inscription; - background-color: var(--glsp-editor-background); - height: 100vh; + grid-column: 1 / 2; + grid-row: 2 / 3; + position: relative; /* to ensure that absolute positioning of elements within the diagram work relative to the diagram. */ } -#inscription.hidden { - display: none; + +#ivy-tool-bar { + grid-column: 1 / 2; + grid-row: 1 / 2; } -.main-widget { - position: relative; +#inscription-ui { + grid-column: 2 / 3; + grid-row: 1 / 3; /* Spanning across both the rows */ + background-color: var(--glsp-editor-background); } .sprotty svg { diff --git a/packages/editor/src/diagram/di.config.ts b/packages/editor/src/diagram/di.config.ts index 99244b49..4b3f32db 100644 --- a/packages/editor/src/diagram/di.config.ts +++ b/packages/editor/src/diagram/di.config.ts @@ -3,8 +3,10 @@ import './diagram.css'; import { ConsoleLogger, CustomFeatures, + DefaultTypes, DeleteElementContextMenuItemProvider, FeatureModule, + GGraph, GLSPProjectionView, GModelElement, IView, @@ -17,7 +19,6 @@ import { moveFeature, selectFeature } from '@eclipse-glsp/client'; -import { DefaultTypes } from '@eclipse-glsp/protocol'; import { interfaces } from 'inversify'; import { ShowGridAction } from '@axonivy/process-editor-protocol'; @@ -44,7 +45,6 @@ import { EndEventNode, EventNode, GatewayNode, - IvyGGraph, LaneNode, MulitlineEditLabel, RotateLabel, @@ -77,9 +77,7 @@ const ivyDiagramModule = new FeatureModule((bind, unbind, isBound, rebind) => { configureCommand({ bind, isBound }, GridFeedbackCommand); const context = { bind, unbind, isBound, rebind }; - - configureIvyModelElement(DefaultTypes.GRAPH, IvyGGraph, GLSPProjectionView); - + configureIvyModelElement(DefaultTypes.GRAPH, GGraph, GLSPProjectionView); configureStartEvent(EventStartTypes.START); configureStartEvent(EventStartTypes.START_ERROR); configureStartEvent(EventStartTypes.START_SIGNAL); diff --git a/packages/editor/src/diagram/diagram.css b/packages/editor/src/diagram/diagram.css index 8a70881e..b9592e44 100644 --- a/packages/editor/src/diagram/diagram.css +++ b/packages/editor/src/diagram/diagram.css @@ -9,7 +9,8 @@ body { .sprotty { user-select: none; } -.sprotty > div:focus-visible { +.sprotty > div:focus-visible, +.sprotty-graph:focus { outline: none; } .sprotty svg:where(.sprotty-graph, .sprotty-empty) { @@ -306,3 +307,10 @@ g[class^='end'] .sprotty-node { svg { border: none; } + +/* UI Extension */ +.ui-extension.hidden { + display: none; + visibility: hidden; + opacity: 0; +} diff --git a/packages/editor/src/diagram/model.ts b/packages/editor/src/diagram/model.ts index 7266f528..df5fb911 100644 --- a/packages/editor/src/diagram/model.ts +++ b/packages/editor/src/diagram/model.ts @@ -12,7 +12,6 @@ import { editFeature, editLabelFeature, fadeFeature, - GGraph, hoverFeedbackFeature, isBoundsAware, isEditableLabel, @@ -47,10 +46,6 @@ import { WithCustomIcon } from './icon/model'; import { ActivityTypes, EdgeTypes, LabelType, LaneTypes } from './view-types'; import { multipleOutgoingEdgesFeature } from '../ui-tools/quick-action/edge/model'; -export class IvyGGraph extends GGraph { - scroll = { x: 0, y: -48 }; -} - export class LaneNode extends RectangularNode implements WithEditableLabel, ArgsAware { static readonly DEFAULT_FEATURES = [ boundsFeature, diff --git a/packages/editor/src/index.ts b/packages/editor/src/index.ts index ea2aca05..c6c7a894 100644 --- a/packages/editor/src/index.ts +++ b/packages/editor/src/index.ts @@ -28,5 +28,6 @@ export * from './ui-tools/tool-bar/button'; export * from './ui-tools/tool-bar/tool-bar'; export * from './ui-tools/viewport/viewport-commands'; export * from './ui-tools/viewport/viewport-bar'; +export * from './utils/ivy-ui-extension'; export * from './ivy-glsp-jsonrpc-client'; diff --git a/packages/editor/src/jump/jump-out-ui.ts b/packages/editor/src/jump/jump-out-ui.ts index 18ba2101..47710155 100644 --- a/packages/editor/src/jump/jump-out-ui.ts +++ b/packages/editor/src/jump/jump-out-ui.ts @@ -1,5 +1,4 @@ import { - AbstractUIExtension, Action, CommandExecutionContext, CommandReturn, @@ -16,9 +15,10 @@ import { inject, injectable, postConstruct } from 'inversify'; import { IvyIcons } from '@axonivy/ui-icons'; import { createElement, createIcon } from '../utils/ui-utils'; import { JumpAction } from '@axonivy/process-editor-protocol'; +import { IvyUIExtension } from '../utils/ivy-ui-extension'; @injectable() -export class JumpOutUi extends AbstractUIExtension { +export class JumpOutUi extends IvyUIExtension { static readonly ID = 'jumpOutUi'; @inject(TYPES.IActionDispatcher) protected readonly actionDispatcher: IActionDispatcher; @@ -26,16 +26,16 @@ export class JumpOutUi extends AbstractUIExtension { @inject(SelectionService) protected selectionService: SelectionService; @inject(EditorContextService) protected readonly editorContext: EditorContextService; - public id(): string { + id(): string { return JumpOutUi.ID; } - public containerClass(): string { + containerClass(): string { return 'jump-out-container'; } @postConstruct() - postConstruct(): void { + protected postConstruct(): void { this.feedbackDispatcher.registerFeedback(this, [JumpOutFeedbackAction.create()]); } diff --git a/packages/editor/src/tools/change-bounds-tool.ts b/packages/editor/src/tools/change-bounds-tool.ts index ba638eac..687090cd 100644 --- a/packages/editor/src/tools/change-bounds-tool.ts +++ b/packages/editor/src/tools/change-bounds-tool.ts @@ -111,12 +111,21 @@ export class IvyFeedbackMoveMouseListener extends FeedbackMoveMouseListener { mouseMove(target: GModelElement, event: MouseEvent): Action[] { this.lastMove = undefined; - const actions = super.mouseMove(target, event); - if (event.buttons !== 0 && this._isMouseDrag && this.hasRealMoved()) { - this.tool.quickActionUi.hideUi(); - addNegativeArea(target); + if (event.buttons === 0) { + return this.mouseUp(target, event); } - return actions; + if (this._isMouseDown) { + this._isMouseDrag = true; + } + if (this._isMouseDrag) { + const actions = super.mouseMove(target, event); + if (this.hasRealMoved()) { + this.tool.quickActionUi.hideUi(); + addNegativeArea(target); + } + return actions; + } + return []; } draggingMouseUp(target: GModelElement, event: MouseEvent): Action[] { diff --git a/packages/editor/src/ui-tools/quick-action/quick-action-ui.ts b/packages/editor/src/ui-tools/quick-action/quick-action-ui.ts index 8993fceb..c75fd67c 100644 --- a/packages/editor/src/ui-tools/quick-action/quick-action-ui.ts +++ b/packages/editor/src/ui-tools/quick-action/quick-action-ui.ts @@ -1,5 +1,4 @@ import { - AbstractUIExtension, Action, Bounds, BoundsAware, @@ -33,9 +32,10 @@ import { Menu } from '../menu/menu'; import { isQuickActionAware } from './model'; import { QuickAction, QuickActionLocation, QuickActionProvider } from './quick-action'; import { InfoQuickActionMenu, QuickActionMenu, ShowInfoQuickActionMenuAction, ShowQuickActionMenuAction } from './quick-action-menu-ui'; +import { IvyUIExtension } from '../../utils/ivy-ui-extension'; @injectable() -export class QuickActionUI extends AbstractUIExtension implements IActionHandler, ISelectionListener { +export class QuickActionUI extends IvyUIExtension implements IActionHandler, ISelectionListener { static readonly ID = 'quickActionsUi'; private activeQuickActions: QuickAction[] = []; private activeQuickActionBtn?: HTMLElement; @@ -145,10 +145,6 @@ export class QuickActionUI extends AbstractUIExtension implements IActionHandler ); } - hide(): void { - super.hide(); - } - protected onBeforeShow(containerElement: HTMLElement, root: Readonly, ...contextElementIds: string[]): void { containerElement.innerHTML = ''; const elements = getElements(contextElementIds, root); diff --git a/packages/editor/src/ui-tools/tool-bar/tool-bar.css b/packages/editor/src/ui-tools/tool-bar/tool-bar.css index c9624c36..2bc9cc5b 100644 --- a/packages/editor/src/ui-tools/tool-bar/tool-bar.css +++ b/packages/editor/src/ui-tools/tool-bar/tool-bar.css @@ -1,5 +1,4 @@ .ivy-tool-bar { - position: absolute; inset: 0; width: 100%; height: 48px; diff --git a/packages/editor/src/ui-tools/tool-bar/tool-bar.ts b/packages/editor/src/ui-tools/tool-bar/tool-bar.ts index 3907a655..a71b3955 100644 --- a/packages/editor/src/ui-tools/tool-bar/tool-bar.ts +++ b/packages/editor/src/ui-tools/tool-bar/tool-bar.ts @@ -1,5 +1,4 @@ import { - AbstractUIExtension, Action, DisposableCollection, EditorContextService, @@ -37,11 +36,12 @@ import { import { ShowToolBarOptionsMenuAction } from './options/action'; import { ToolBarOptionsMenu } from './options/options-menu-ui'; import { ShowToolBarMenuAction, ToolBarMenu } from './tool-bar-menu'; +import { IvyUIExtension } from '../../utils/ivy-ui-extension'; const CLICKED_CSS_CLASS = 'clicked'; @injectable() -export class ToolBar extends AbstractUIExtension implements IActionHandler, IEditModeListener, ISelectionListener { +export class ToolBar extends IvyUIExtension implements IActionHandler, IEditModeListener, ISelectionListener { static readonly ID = 'ivy-tool-bar'; @inject(TYPES.IActionDispatcher) protected readonly actionDispatcher: GLSPActionDispatcher; diff --git a/packages/editor/src/ui-tools/viewport/viewport-bar.ts b/packages/editor/src/ui-tools/viewport/viewport-bar.ts index 04fe75d6..fd1cc637 100644 --- a/packages/editor/src/ui-tools/viewport/viewport-bar.ts +++ b/packages/editor/src/ui-tools/viewport/viewport-bar.ts @@ -1,6 +1,5 @@ import { Action, - AbstractUIExtension, EditorContextService, GLSPActionDispatcher, IActionHandler, @@ -9,17 +8,19 @@ import { IToolManager, SetUIExtensionVisibilityAction, SetViewportAction, - TYPES - , SelectionService } from '@eclipse-glsp/client'; + TYPES, + SelectionService +} from '@eclipse-glsp/client'; import { inject, injectable } from 'inversify'; import { CenterButton, FitToScreenButton, OriginScreenButton, ViewportBarButton } from './button'; import { createElement, createIcon } from '../../utils/ui-utils'; import { QuickActionUI } from '../quick-action/quick-action-ui'; import { EnableViewportAction, SetViewportZoomAction } from '@axonivy/process-editor-protocol'; +import { IvyUIExtension } from '../../utils/ivy-ui-extension'; @injectable() -export class ViewportBar extends AbstractUIExtension implements IActionHandler { +export class ViewportBar extends IvyUIExtension implements IActionHandler { static readonly ID = 'ivy-viewport-bar'; @inject(TYPES.IActionDispatcher) protected readonly actionDispatcher: GLSPActionDispatcher; diff --git a/packages/editor/src/ui-tools/viewport/viewport-commands.ts b/packages/editor/src/ui-tools/viewport/viewport-commands.ts index 357b6022..7dae2ae0 100644 --- a/packages/editor/src/ui-tools/viewport/viewport-commands.ts +++ b/packages/editor/src/ui-tools/viewport/viewport-commands.ts @@ -53,7 +53,7 @@ export class OriginViewportCommand extends BoundsAwareViewportCommand { } getNewViewport(_bounds: Bounds, _model: GModelRoot): Viewport | undefined { - return { zoom: 1, scroll: { x: 0, y: -48 } }; + return { zoom: 1, scroll: { x: 0, y: 0 } }; } } diff --git a/packages/editor/src/utils/ivy-ui-extension.ts b/packages/editor/src/utils/ivy-ui-extension.ts new file mode 100644 index 00000000..5fca25aa --- /dev/null +++ b/packages/editor/src/utils/ivy-ui-extension.ts @@ -0,0 +1,94 @@ +import { AbstractUIExtension } from '@eclipse-glsp/client'; +import { injectable } from 'inversify'; + +@injectable() +export abstract class IvyUIExtension extends AbstractUIExtension { + static UI_EXTENSION_CLASS = 'ui-extension'; + + protected get diagramContainerId(): string { + return this.options.baseDiv; + } + + protected get parentContainerSelector(): string { + return '#' + this.diagramContainerId; + } + + protected get containerSelector(): string { + return '#' + this.id(); + } + + protected get initialized(): boolean { + return !!this.containerElement; + } + + protected initialize(): boolean { + if (this.initialized) { + return true; + } + try { + this.containerElement = this.getOrCreateContainer(); + this.initializeContainer(this.containerElement); + this.initializeContents(this.containerElement); + } catch (error) { + const msg = error instanceof Error ? error.message : `Could not retrieve container element for UI extension ${this.id}`; + this.logger.error(this, msg); + return false; + } + return true; + } + + protected getOrCreateContainer(): HTMLElement { + if (this.containerElement) { + return this.containerElement; + } + const existingContainer = this.getContainer(); + if (existingContainer) { + return existingContainer; + } + const parent = this.getParentContainer(); + if (!parent || !parent.isConnected) { + throw new Error(`Could not obtain attached parent for initializing UI extension ${this.id}`); + } + const container = this.createContainer(parent); + this.insertContainerIntoParent(container, parent); + return container; + } + + protected getContainer(): HTMLElement | null { + return document.querySelector(this.containerSelector); + } + + protected createContainer(parent: HTMLElement): HTMLElement { + const container = document.createElement('div'); + container.id = parent.id + '_' + this.id(); + return container; + } + + protected initializeContainer(container: HTMLElement): void { + container.classList.add(IvyUIExtension.UI_EXTENSION_CLASS, this.containerClass()); + } + + protected getParentContainer(): HTMLElement { + return document.querySelector(this.parentContainerSelector)!; + } + + protected insertContainerIntoParent(container: HTMLElement, parent: HTMLElement): void { + parent.insertBefore(container, parent.firstChild); + } + + protected setContainerVisible(visible: boolean): void { + if (visible) { + this.containerElement?.classList.remove('hidden'); + } else { + this.containerElement?.classList.add('hidden'); + } + } + + protected isContainerVisible(): boolean { + return this.containerElement && !this.containerElement.classList.contains('hidden'); + } + + protected toggleContainerVisible(): void { + this.setContainerVisible(!this.isContainerVisible()); + } +} diff --git a/packages/inscription/src/inscription/inscription-ui.tsx b/packages/inscription/src/inscription/inscription-ui.tsx index c41739cb..b4c81658 100644 --- a/packages/inscription/src/inscription/inscription-ui.tsx +++ b/packages/inscription/src/inscription/inscription-ui.tsx @@ -1,17 +1,15 @@ import { InscriptionClientJsonRpc, IvyScriptLanguage } from '@axonivy/inscription-core'; import { ClientContextProvider, MonacoEditorUtil, ThemeContextProvider, initQueryClient } from '@axonivy/inscription-editor'; import { InscriptionClient, InscriptionContext } from '@axonivy/inscription-protocol'; -import { SwitchThemeActionHandler } from '@axonivy/process-editor'; +import { SwitchThemeActionHandler, IvyUIExtension } from '@axonivy/process-editor'; import { SwitchThemeAction } from '@axonivy/process-editor-protocol'; import { - AbstractUIExtension, Action, GArgument, GModelRoot, IActionHandler, ISelectionListener, SelectionService, - SetUIExtensionVisibilityAction, isNotUndefined, isOpenable } from '@eclipse-glsp/client'; @@ -26,8 +24,8 @@ import { EnableInscriptionAction, ToggleInscriptionAction } from './action'; const JSX = { createElement: React.createElement }; @injectable() -export class InscriptionUi extends AbstractUIExtension implements IActionHandler, ISelectionListener { - static readonly ID = 'inscriptionUi'; +export class InscriptionUi extends IvyUIExtension implements IActionHandler, ISelectionListener { + static readonly ID = 'inscription-ui'; @inject(SelectionService) protected readonly selectionService: SelectionService; @inject(SwitchThemeActionHandler) @optional() protected switchThemeHandler?: SwitchThemeActionHandler; @@ -52,20 +50,6 @@ export class InscriptionUi extends AbstractUIExtension implements IActionHandler return 'inscription-ui-container'; } - protected initialize() { - const baseDiv = document.getElementById('inscription'); - if (!baseDiv) { - this.logger.warn(this, `Could not obtain inscription base container for initializing UI extension ${this.id}`, this); - return false; - } - this.containerElement = this.getOrCreateContainer(baseDiv.id); - this.initializeContents(this.containerElement); - if (baseDiv) { - baseDiv.insertBefore(this.containerElement, baseDiv.firstChild); - } - return true; - } - protected initializeContents(containerElement: HTMLElement) { this.changeUiVisiblitiy(false); this.inscriptionContext = this.initInscriptionContext(); @@ -115,7 +99,7 @@ export class InscriptionUi extends AbstractUIExtension implements IActionHandler handle(action: Action) { if (EnableInscriptionAction.is(action)) { this.action = action; - return SetUIExtensionVisibilityAction.create({ extensionId: InscriptionUi.ID, visible: true }); + this.initialize(); } if (ToggleInscriptionAction.is(action)) { if (!this.inscriptionElement) { @@ -146,18 +130,10 @@ export class InscriptionUi extends AbstractUIExtension implements IActionHandler } private changeUiVisiblitiy(force?: boolean) { - const baseDiv = document.getElementById('inscription'); - if (!baseDiv) { - return; - } if (force !== undefined) { - if (force) { - baseDiv.classList.remove('hidden'); - } else { - baseDiv.classList.add('hidden'); - } + this.setContainerVisible(force); } else { - baseDiv.classList.toggle('hidden'); + this.toggleContainerVisible(); } window.dispatchEvent(new CustomEvent('resize')); } diff --git a/playwright/tests/page-objects/inscription.ts b/playwright/tests/page-objects/inscription.ts index 19367479..88bc87c5 100644 --- a/playwright/tests/page-objects/inscription.ts +++ b/playwright/tests/page-objects/inscription.ts @@ -6,7 +6,7 @@ export class Inscription { constructor(page: Page) { this.page = page; - this.view = this.page.locator('#inscription'); + this.view = this.page.locator('#inscription-ui'); } locator() { diff --git a/playwright/tests/page-objects/toolbar.ts b/playwright/tests/page-objects/toolbar.ts index bfe7861d..b71dc953 100644 --- a/playwright/tests/page-objects/toolbar.ts +++ b/playwright/tests/page-objects/toolbar.ts @@ -16,7 +16,7 @@ export class Toolbar { constructor(page: Page) { this.page = page; - this.toolbar = this.page.locator('#sprotty_ivy-tool-bar'); + this.toolbar = this.page.locator('#ivy-tool-bar'); this.toolbarMenu = new Menu(page, this.toolbar.locator('.tool-bar-menu')); this.optionsMenu = new OptionsMenu(page, this.toolbar.locator('.tool-bar-options-menu')); this.defaultTool = this.toolbar.locator('#btn_default_tools'); diff --git a/playwright/tests/page-objects/viewport.ts b/playwright/tests/page-objects/viewport.ts index 8705e159..1cf7a4cc 100644 --- a/playwright/tests/page-objects/viewport.ts +++ b/playwright/tests/page-objects/viewport.ts @@ -2,7 +2,7 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { Point } from './types'; import { graphLocator } from './graph'; -export const ORIGIN_VIEWPORT = 'scale(1) translate(0,48)' as const; +export const ORIGIN_VIEWPORT = 'scale(1) translate(0,0)' as const; export class ViewportBar { protected readonly page: Page;