diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/palette.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/palette.ts index 4a83a2868c05e..c22f35b420f6c 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/palette.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/palette.ts @@ -7,9 +7,6 @@ import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; import { paulTor14 } from '../../../common/lib/palettes'; import { getFunctionHelp } from '../../../i18n'; -// @ts-expect-error untyped local -import { getState } from '../../../public/state/store'; -import { getWorkpadPalette } from '../../../public/state/selectors/workpad'; interface Arguments { color: string[]; @@ -52,7 +49,7 @@ export function palette(): ExpressionFunctionDefinition<'palette', null, Argumen options: [true, false], }, }, - fn: (input, args) => { + fn: (input, args, context) => { let colors: string[] = paulTor14.colors; const { color, reverse } = args; let { gradient } = args; @@ -60,7 +57,8 @@ export function palette(): ExpressionFunctionDefinition<'palette', null, Argumen if (color) { colors = color; } else { - const workpadPalette = getWorkpadPalette(getState()); + const theme = context.variables.theme; + const workpadPalette = theme?.palette; if (workpadPalette) { colors = workpadPalette.colors; diff --git a/x-pack/plugins/canvas/public/lib/run_interpreter.ts b/x-pack/plugins/canvas/public/lib/run_interpreter.ts index 12e07ed3535f6..fc539c9882016 100644 --- a/x-pack/plugins/canvas/public/lib/run_interpreter.ts +++ b/x-pack/plugins/canvas/public/lib/run_interpreter.ts @@ -7,19 +7,26 @@ import { fromExpression, getType } from '@kbn/interpreter/common'; import { ExpressionValue, ExpressionAstExpression } from 'src/plugins/expressions/public'; import { notifyService, expressionsService } from '../services'; +import { CanvasWorkpadTheme } from '../../types'; interface Options { castToRender?: boolean; } +interface ContextOptions { + variables: Record; + theme: CanvasWorkpadTheme; +} + /** * Meant to be a replacement for plugins/interpreter/interpretAST */ export async function interpretAst( ast: ExpressionAstExpression, - variables: Record + contextOptions: ContextOptions ): Promise { - const context = { variables }; + const { variables, theme = {} } = contextOptions; + const context = { variables: { ...variables, theme } }; return await expressionsService.getService().execute(ast, null, context).getData(); } @@ -28,7 +35,7 @@ export async function interpretAst( * * @param {object} ast - Executable AST * @param {any} input - Initial input for AST execution - * @param {object} variables - Variables to pass in to the intrepreter context + * @param {object} contextOptions - variables and theme info passed to the context * @param {object} options * @param {boolean} options.castToRender - try to cast to a type: render object? * @returns {promise} @@ -36,10 +43,11 @@ export async function interpretAst( export async function runInterpreter( ast: ExpressionAstExpression, input: ExpressionValue, - variables: Record, + contextOptions: ContextOptions, options: Options = {} ): Promise { - const context = { variables }; + const { variables, theme = {} } = contextOptions; + const context = { variables: { ...variables, theme } }; try { const renderable = await expressionsService.getService().execute(ast, input, context).getData(); @@ -49,7 +57,7 @@ export async function runInterpreter( } if (options.castToRender) { - return runInterpreter(fromExpression('render'), renderable, variables, { + return runInterpreter(fromExpression('render'), renderable, contextOptions, { castToRender: false, }); } diff --git a/x-pack/plugins/canvas/public/lib/workpad_service.ts b/x-pack/plugins/canvas/public/lib/workpad_service.ts index 07922ac33ff13..00806ac26882c 100644 --- a/x-pack/plugins/canvas/public/lib/workpad_service.ts +++ b/x-pack/plugins/canvas/public/lib/workpad_service.ts @@ -66,6 +66,7 @@ export const create = async (workpad: CanvasWorkpad) => ...sanitizeWorkpad({ ...workpad }), assets: workpad.assets || {}, variables: workpad.variables || [], + theme: workpad.theme || null, }); export async function createFromTemplate(templateId: string) { diff --git a/x-pack/plugins/canvas/public/state/actions/elements.js b/x-pack/plugins/canvas/public/state/actions/elements.js index 2ba011373c670..36ce7cb1cb6fe 100644 --- a/x-pack/plugins/canvas/public/state/actions/elements.js +++ b/x-pack/plugins/canvas/public/state/actions/elements.js @@ -12,6 +12,7 @@ import { createThunk } from '../../lib/create_thunk'; import { getPages, getWorkpadVariablesAsObject, + getWorkpadThemeAsObject, getNodeById, getNodes, getSelectedPageIndex, @@ -102,7 +103,9 @@ export const fetchContext = createThunk( return i < index; }); - const variables = getWorkpadVariablesAsObject(getState()); + const state = getState(); + const variables = getWorkpadVariablesAsObject(state); + const theme = getWorkpadThemeAsObject(state); // get context data from a partial AST return interpretAst( @@ -110,7 +113,10 @@ export const fetchContext = createThunk( ...element.ast, chain: astChain, }, - variables + { + variables, + theme, + } ).then((value) => { dispatch( args.setValue({ @@ -136,9 +142,11 @@ const fetchRenderableWithContextFn = ({ dispatch, getState }, element, ast, cont value: renderable, }); - const variables = getWorkpadVariablesAsObject(getState()); + const state = getState(); + const variables = getWorkpadVariablesAsObject(state); + const theme = getWorkpadThemeAsObject(state); - return runInterpreter(ast, context, variables, { castToRender: true }) + return runInterpreter(ast, context, { variables, theme }, { castToRender: true }) .then((renderable) => { dispatch(getAction(renderable)); }) @@ -182,9 +190,11 @@ export const fetchAllRenderables = createThunk( const ast = element.ast || safeElementFromExpression(element.expression); const argumentPath = [element.id, 'expressionRenderable']; - const variables = getWorkpadVariablesAsObject(getState()); + const state = getState(); + const variables = getWorkpadVariablesAsObject(state); + const theme = getWorkpadThemeAsObject(state); - return runInterpreter(ast, null, variables, { castToRender: true }) + return runInterpreter(ast, null, { variables, theme }, { castToRender: true }) .then((renderable) => ({ path: argumentPath, value: renderable })) .catch((err) => { services.notify.getService().error(err); diff --git a/x-pack/plugins/canvas/public/state/reducers/workpad.ts b/x-pack/plugins/canvas/public/state/reducers/workpad.ts index 1f29b5bf6156e..381fb87645ef4 100644 --- a/x-pack/plugins/canvas/public/state/reducers/workpad.ts +++ b/x-pack/plugins/canvas/public/state/reducers/workpad.ts @@ -52,27 +52,39 @@ export const workpadReducer = handleActions( }, [SET_WORKPAD_PALETTE]: (workpadState, { payload }: Action) => { - if (payload) { - return set(workpadState, 'theme.palette', payload); - } - unset(workpadState, 'theme.palette'); - return workpadState; + return { + ...workpadState, + theme: { + ...(workpadState.theme || {}), + palette: payload || undefined, + }, + }; }, [SET_WORKPAD_FONT_FAMILY]: (workpadState, { payload }: Action) => { - if (payload) { - return set(workpadState, 'theme.font.family', payload); - } - unset(workpadState, 'theme.font.family'); - return workpadState; + return { + ...workpadState, + theme: { + ...(workpadState.theme || {}), + font: { + ...(workpadState.theme?.font || {}), + family: payload || undefined, + }, + }, + }; }, [SET_WORKPAD_FONT_SIZE]: (workpadState, { payload }: Action) => { - if (payload) { - return set(workpadState, 'theme.font.size', payload); - } - unset(workpadState, 'theme.font.size'); - return workpadState; + return { + ...workpadState, + theme: { + ...(workpadState.theme || {}), + font: { + ...(workpadState.theme?.font || {}), + size: payload || undefined, + }, + }, + }; }, [SET_NAME]: (workpadState, { payload }: Action) => { diff --git a/x-pack/plugins/canvas/public/state/selectors/workpad.ts b/x-pack/plugins/canvas/public/state/selectors/workpad.ts index 2d60e27728e2b..25d726b7669ee 100644 --- a/x-pack/plugins/canvas/public/state/selectors/workpad.ts +++ b/x-pack/plugins/canvas/public/state/selectors/workpad.ts @@ -616,6 +616,21 @@ export const getWorkpadTheme = (state: State) => { return theme || null; }; +export const getWorkpadThemeAsObject = (state: State) => { + const theme = getWorkpadTheme(state) || {}; + + // TODO: Pretty ugly -- we should re-work this. + // The reason for this is the theme.font.family from canvas + // is an object + return { + ...theme, + font: { + ...(theme?.font || {}), + family: theme?.font?.family?.value || null, + }, + }; +}; + export const getWorkpadFontFamily = (state: State) => { const { font } = getWorkpadTheme(state) || {}; return font?.family || null; diff --git a/x-pack/plugins/canvas/server/routes/workpad/workpad_schema.ts b/x-pack/plugins/canvas/server/routes/workpad/workpad_schema.ts index 1beda7d50a546..7c648d68a18b9 100644 --- a/x-pack/plugins/canvas/server/routes/workpad/workpad_schema.ts +++ b/x-pack/plugins/canvas/server/routes/workpad/workpad_schema.ts @@ -97,5 +97,5 @@ export const WorkpadSchema = schema.object({ page: schema.number(), pages: schema.arrayOf(WorkpadPageSchema), width: schema.number(), - theme: schema.maybe(WorkpadThemeSchema), + theme: schema.maybe(schema.nullable(WorkpadThemeSchema)), });