From 8df3b18903c340a0841652739882acbfbcf13267 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Thu, 2 Nov 2023 13:16:18 +0100 Subject: [PATCH 1/5] fix: Allow overriding theme from query params --- packages/editor-ui/src/stores/ui.store.ts | 33 +++++++---------------- packages/editor-ui/src/stores/ui.utils.ts | 31 +++++++++++++++++++++ 2 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 packages/editor-ui/src/stores/ui.utils.ts diff --git a/packages/editor-ui/src/stores/ui.store.ts b/packages/editor-ui/src/stores/ui.store.ts index c8038fbd9d94b..48a4744587f93 100644 --- a/packages/editor-ui/src/stores/ui.store.ts +++ b/packages/editor-ui/src/stores/ui.store.ts @@ -64,37 +64,24 @@ import { useCloudPlanStore } from '@/stores/cloudPlan.store'; import { useTelemetryStore } from '@/stores/telemetry.store'; import { dismissBannerPermanently } from '@/api/ui'; import type { BannerName } from 'n8n-workflow'; +import { + addThemeToBody, + getPreferredTheme, + getQueryParam, + isValidTheme, + updateTheme, +} from './ui.utils'; let savedTheme: ThemeOption = 'system'; try { - const value = localStorage.getItem(LOCAL_STORAGE_THEME) as AppliedThemeOption; - if (['light', 'dark'].includes(value)) { + // query param allows overriding theme for demo view in preview iframe without flickering + const value = getQueryParam('theme') || localStorage.getItem(LOCAL_STORAGE_THEME); + if (isValidTheme(value)) { savedTheme = value; addThemeToBody(value); } } catch (e) {} -function addThemeToBody(theme: AppliedThemeOption) { - window.document.body.setAttribute('data-theme', theme); -} - -function updateTheme(theme: ThemeOption) { - if (theme === 'system') { - window.document.body.removeAttribute('data-theme'); - localStorage.removeItem(LOCAL_STORAGE_THEME); - } else { - addThemeToBody(theme); - localStorage.setItem(LOCAL_STORAGE_THEME, theme); - } -} - -function getPreferredTheme(): AppliedThemeOption { - const isDarkMode = - !!window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)')?.matches; - - return isDarkMode ? 'dark' : 'light'; -} - export const useUIStore = defineStore(STORES.UI, { state: (): UIState => ({ activeActions: [], diff --git a/packages/editor-ui/src/stores/ui.utils.ts b/packages/editor-ui/src/stores/ui.utils.ts new file mode 100644 index 0000000000000..79bb89c0f281d --- /dev/null +++ b/packages/editor-ui/src/stores/ui.utils.ts @@ -0,0 +1,31 @@ +import type { AppliedThemeOption, ThemeOption } from '@/Interface'; +import { LOCAL_STORAGE_THEME } from '@/constants'; + +export function addThemeToBody(theme: AppliedThemeOption) { + window.document.body.setAttribute('data-theme', theme); +} + +export function isValidTheme(theme: string | null): theme is AppliedThemeOption { + return !!theme && ['light', 'dark'].includes(theme); +} + +export function getQueryParam(paramName: string): string | null { + return new URLSearchParams(window.location.search).get(paramName); +} + +export function updateTheme(theme: ThemeOption) { + if (theme === 'system') { + window.document.body.removeAttribute('data-theme'); + localStorage.removeItem(LOCAL_STORAGE_THEME); + } else { + addThemeToBody(theme); + localStorage.setItem(LOCAL_STORAGE_THEME, theme); + } +} + +export function getPreferredTheme(): AppliedThemeOption { + const isDarkMode = + !!window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)')?.matches; + + return isDarkMode ? 'dark' : 'light'; +} From 4fe73981f69af4e3883e93ad5dc3b815a3cf41b6 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Thu, 2 Nov 2023 13:49:37 +0100 Subject: [PATCH 2/5] test: add tests --- cypress/e2e/31-demo.cy.ts | 24 ++++++++++++++++++++++++ cypress/pages/demo.ts | 28 ++++++++++++++++++++++++++++ cypress/pages/templates.ts | 2 -- 3 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 cypress/e2e/31-demo.cy.ts create mode 100644 cypress/pages/demo.ts diff --git a/cypress/e2e/31-demo.cy.ts b/cypress/e2e/31-demo.cy.ts new file mode 100644 index 0000000000000..b8addbed27fc6 --- /dev/null +++ b/cypress/e2e/31-demo.cy.ts @@ -0,0 +1,24 @@ +import workflow from '../fixtures/Manual_wait_set.json'; +import { DemoPage } from '../pages/demo'; +import { WorkflowPage } from '../pages/workflow'; + +const demoPage = new DemoPage(); +const workflowPage = new WorkflowPage(); + +describe('Demo', () => { + it('can import template', () => { + demoPage.actions.visit(); + demoPage.actions.importWorkflow(workflow); + workflowPage.getters.canvasNodes().should('have.length', 3); + }); + + it('can override theme to dark', () => { + demoPage.actions.visit('dark'); + cy.get('body').should('have.attr', 'data-theme', 'dark'); + }); + + it('can override theme to light', () => { + demoPage.actions.visit('light'); + cy.get('body').should('have.attr', 'data-theme', 'light'); + }); +}); diff --git a/cypress/pages/demo.ts b/cypress/pages/demo.ts new file mode 100644 index 0000000000000..0bcc63b8d5b24 --- /dev/null +++ b/cypress/pages/demo.ts @@ -0,0 +1,28 @@ +import { BasePage } from './base'; + +export class DemoPage extends BasePage { + url = '/workflows/demo'; + + getters = { + }; + + actions = { + visit: (theme?: 'dark' | 'light') => { + const query = theme ? `?theme=${theme}` : ''; + cy.visit(this.url + query); + cy.waitForLoad(); + cy.window().then((win) => { + // @ts-ignore + win.preventNodeViewBeforeUnload = true; + }); + }, + importWorkflow(workflow: object) { + const OPEN_WORKFLOW = {command: 'openWorkflow', workflow}; + cy.window().then($window => { + const message = JSON.stringify(OPEN_WORKFLOW); + $window.postMessage(message, '*') + }); + } + } +} + diff --git a/cypress/pages/templates.ts b/cypress/pages/templates.ts index d49c086a790fa..1222bc0edc70f 100644 --- a/cypress/pages/templates.ts +++ b/cypress/pages/templates.ts @@ -1,7 +1,5 @@ import { BasePage } from './base'; -import { WorkflowPage } from './workflow'; -const workflowPage = new WorkflowPage(); export class TemplatesPage extends BasePage { url = '/templates'; From 1b2b0a4410585fe6e49f693780ee1533ac78173b Mon Sep 17 00:00:00 2001 From: Mutasem Date: Thu, 2 Nov 2023 14:05:18 +0100 Subject: [PATCH 3/5] test: refactor --- cypress/e2e/31-demo.cy.ts | 11 +++++----- cypress/pages/demo.ts | 43 ++++++++++++++++----------------------- 2 files changed, 23 insertions(+), 31 deletions(-) diff --git a/cypress/e2e/31-demo.cy.ts b/cypress/e2e/31-demo.cy.ts index b8addbed27fc6..d9397ace4eece 100644 --- a/cypress/e2e/31-demo.cy.ts +++ b/cypress/e2e/31-demo.cy.ts @@ -1,24 +1,23 @@ import workflow from '../fixtures/Manual_wait_set.json'; -import { DemoPage } from '../pages/demo'; +import { importWorkflow, vistDemoPage } from '../pages/demo'; import { WorkflowPage } from '../pages/workflow'; -const demoPage = new DemoPage(); const workflowPage = new WorkflowPage(); describe('Demo', () => { it('can import template', () => { - demoPage.actions.visit(); - demoPage.actions.importWorkflow(workflow); + vistDemoPage(); + importWorkflow(workflow); workflowPage.getters.canvasNodes().should('have.length', 3); }); it('can override theme to dark', () => { - demoPage.actions.visit('dark'); + vistDemoPage('dark'); cy.get('body').should('have.attr', 'data-theme', 'dark'); }); it('can override theme to light', () => { - demoPage.actions.visit('light'); + vistDemoPage('light'); cy.get('body').should('have.attr', 'data-theme', 'light'); }); }); diff --git a/cypress/pages/demo.ts b/cypress/pages/demo.ts index 0bcc63b8d5b24..906d7a8cca8b9 100644 --- a/cypress/pages/demo.ts +++ b/cypress/pages/demo.ts @@ -1,28 +1,21 @@ -import { BasePage } from './base'; +/** + * Actions + */ -export class DemoPage extends BasePage { - url = '/workflows/demo'; - - getters = { - }; - - actions = { - visit: (theme?: 'dark' | 'light') => { - const query = theme ? `?theme=${theme}` : ''; - cy.visit(this.url + query); - cy.waitForLoad(); - cy.window().then((win) => { - // @ts-ignore - win.preventNodeViewBeforeUnload = true; - }); - }, - importWorkflow(workflow: object) { - const OPEN_WORKFLOW = {command: 'openWorkflow', workflow}; - cy.window().then($window => { - const message = JSON.stringify(OPEN_WORKFLOW); - $window.postMessage(message, '*') - }); - } - } +export function vistDemoPage(theme?: 'dark' | 'light') { + const query = theme ? `?theme=${theme}` : ''; + cy.visit(this.url + query); + cy.waitForLoad(); + cy.window().then((win) => { + // @ts-ignore + win.preventNodeViewBeforeUnload = true; + }); } +export function importWorkflow(workflow: object) { + const OPEN_WORKFLOW = {command: 'openWorkflow', workflow}; + cy.window().then($window => { + const message = JSON.stringify(OPEN_WORKFLOW); + $window.postMessage(message, '*') + }); +} From 25fba3fa153857157745d78f1c2f00f14fbffdd0 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Thu, 2 Nov 2023 14:06:08 +0100 Subject: [PATCH 4/5] test: refactor --- cypress/pages/demo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/pages/demo.ts b/cypress/pages/demo.ts index 906d7a8cca8b9..0590fb8def03e 100644 --- a/cypress/pages/demo.ts +++ b/cypress/pages/demo.ts @@ -4,7 +4,7 @@ export function vistDemoPage(theme?: 'dark' | 'light') { const query = theme ? `?theme=${theme}` : ''; - cy.visit(this.url + query); + cy.visit('/workflows/demo' + query); cy.waitForLoad(); cy.window().then((win) => { // @ts-ignore From c9e4f7fa1bcf259c43d2545d5b55b02cacfeb4ec Mon Sep 17 00:00:00 2001 From: Mutasem Date: Thu, 2 Nov 2023 14:19:46 +0100 Subject: [PATCH 5/5] test: refactor --- packages/editor-ui/src/stores/ui.store.ts | 6 ++---- packages/editor-ui/src/stores/ui.utils.ts | 7 ++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/editor-ui/src/stores/ui.store.ts b/packages/editor-ui/src/stores/ui.store.ts index 48a4744587f93..9c6abeaf84a40 100644 --- a/packages/editor-ui/src/stores/ui.store.ts +++ b/packages/editor-ui/src/stores/ui.store.ts @@ -37,7 +37,6 @@ import { DEBUG_PAYWALL_MODAL_KEY, N8N_PRICING_PAGE_URL, WORKFLOW_HISTORY_VERSION_RESTORE, - LOCAL_STORAGE_THEME, } from '@/constants'; import type { CloudUpdateLinkSourceType, @@ -67,15 +66,14 @@ import type { BannerName } from 'n8n-workflow'; import { addThemeToBody, getPreferredTheme, - getQueryParam, + getThemeOverride, isValidTheme, updateTheme, } from './ui.utils'; let savedTheme: ThemeOption = 'system'; try { - // query param allows overriding theme for demo view in preview iframe without flickering - const value = getQueryParam('theme') || localStorage.getItem(LOCAL_STORAGE_THEME); + const value = getThemeOverride(); if (isValidTheme(value)) { savedTheme = value; addThemeToBody(value); diff --git a/packages/editor-ui/src/stores/ui.utils.ts b/packages/editor-ui/src/stores/ui.utils.ts index 79bb89c0f281d..4accdfba674d4 100644 --- a/packages/editor-ui/src/stores/ui.utils.ts +++ b/packages/editor-ui/src/stores/ui.utils.ts @@ -9,7 +9,12 @@ export function isValidTheme(theme: string | null): theme is AppliedThemeOption return !!theme && ['light', 'dark'].includes(theme); } -export function getQueryParam(paramName: string): string | null { +// query param allows overriding theme for demo view in preview iframe without flickering +export function getThemeOverride() { + return getQueryParam('theme') || localStorage.getItem(LOCAL_STORAGE_THEME); +} + +function getQueryParam(paramName: string): string | null { return new URLSearchParams(window.location.search).get(paramName); }