From 2854a0cf467258c6dacc15c2b200cf6480b6ecef Mon Sep 17 00:00:00 2001 From: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:06:33 +0100 Subject: [PATCH] fix(editor): Allow overriding theme from query params (#7591) Allow overriding theme through query params.. to be able to override it from preview iframe in webcomponent Github issue / Community forum post (link here to close automatically): --- cypress/e2e/31-demo.cy.ts | 23 +++++++++++++++ cypress/pages/demo.ts | 21 +++++++++++++ cypress/pages/templates.ts | 2 -- packages/editor-ui/src/stores/ui.store.ts | 33 ++++++--------------- packages/editor-ui/src/stores/ui.utils.ts | 36 +++++++++++++++++++++++ 5 files changed, 89 insertions(+), 26 deletions(-) create mode 100644 cypress/e2e/31-demo.cy.ts create mode 100644 cypress/pages/demo.ts create mode 100644 packages/editor-ui/src/stores/ui.utils.ts diff --git a/cypress/e2e/31-demo.cy.ts b/cypress/e2e/31-demo.cy.ts new file mode 100644 index 0000000000..d9397ace4e --- /dev/null +++ b/cypress/e2e/31-demo.cy.ts @@ -0,0 +1,23 @@ +import workflow from '../fixtures/Manual_wait_set.json'; +import { importWorkflow, vistDemoPage } from '../pages/demo'; +import { WorkflowPage } from '../pages/workflow'; + +const workflowPage = new WorkflowPage(); + +describe('Demo', () => { + it('can import template', () => { + vistDemoPage(); + importWorkflow(workflow); + workflowPage.getters.canvasNodes().should('have.length', 3); + }); + + it('can override theme to dark', () => { + vistDemoPage('dark'); + cy.get('body').should('have.attr', 'data-theme', 'dark'); + }); + + it('can override theme to light', () => { + vistDemoPage('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 0000000000..0590fb8def --- /dev/null +++ b/cypress/pages/demo.ts @@ -0,0 +1,21 @@ +/** + * Actions + */ + +export function vistDemoPage(theme?: 'dark' | 'light') { + const query = theme ? `?theme=${theme}` : ''; + cy.visit('/workflows/demo' + 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, '*') + }); +} diff --git a/cypress/pages/templates.ts b/cypress/pages/templates.ts index d49c086a79..1222bc0edc 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'; diff --git a/packages/editor-ui/src/stores/ui.store.ts b/packages/editor-ui/src/stores/ui.store.ts index c8038fbd9d..9c6abeaf84 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, @@ -64,37 +63,23 @@ 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, + getThemeOverride, + isValidTheme, + updateTheme, +} from './ui.utils'; let savedTheme: ThemeOption = 'system'; try { - const value = localStorage.getItem(LOCAL_STORAGE_THEME) as AppliedThemeOption; - if (['light', 'dark'].includes(value)) { + const value = getThemeOverride(); + 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 0000000000..4accdfba67 --- /dev/null +++ b/packages/editor-ui/src/stores/ui.utils.ts @@ -0,0 +1,36 @@ +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); +} + +// 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); +} + +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'; +}