diff --git a/cypress/e2e/31-demo.cy.ts b/cypress/e2e/31-demo.cy.ts new file mode 100644 index 0000000000000..d9397ace4eece --- /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 0000000000000..0590fb8def03e --- /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 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'; diff --git a/packages/editor-ui/src/stores/ui.store.ts b/packages/editor-ui/src/stores/ui.store.ts index c8038fbd9d94b..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, @@ -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 0000000000000..4accdfba674d4 --- /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'; +}