From 0ef17f92a61b406e13b67af7752af7d2a146a270 Mon Sep 17 00:00:00 2001 From: Peter Pisljar Date: Tue, 11 Aug 2020 17:58:47 +0200 Subject: [PATCH] theme function (#73451) --- .../common/expression_functions/specs/font.ts | 15 +-- .../expression_functions/specs/index.ts | 3 + .../specs/tests/font.test.ts | 97 ++++++------------- .../specs/tests/theme.test.ts | 63 ++++++++++++ .../expression_functions/specs/theme.ts | 65 +++++++++++++ .../common/expression_functions/types.ts | 2 + 6 files changed, 172 insertions(+), 73 deletions(-) create mode 100644 src/plugins/expressions/common/expression_functions/specs/tests/theme.test.ts create mode 100644 src/plugins/expressions/common/expression_functions/specs/theme.ts diff --git a/src/plugins/expressions/common/expression_functions/specs/font.ts b/src/plugins/expressions/common/expression_functions/specs/font.ts index c46ce0adadef..52692f72cebb 100644 --- a/src/plugins/expressions/common/expression_functions/specs/font.ts +++ b/src/plugins/expressions/common/expression_functions/specs/font.ts @@ -64,7 +64,7 @@ export const font: ExpressionFunctionFont = { inputTypes: ['null'], args: { align: { - default: 'left', + default: '{ theme "font.align" default="left" }', help: i18n.translate('expressions.functions.font.args.alignHelpText', { defaultMessage: 'The horizontal text alignment.', }), @@ -72,13 +72,14 @@ export const font: ExpressionFunctionFont = { types: ['string'], }, color: { + default: `{ theme "font.color" }`, help: i18n.translate('expressions.functions.font.args.colorHelpText', { defaultMessage: 'The text color.', }), types: ['string'], }, family: { - default: `"${openSans.value}"`, + default: `{ theme "font.family" default="${openSans.value}" }`, help: i18n.translate('expressions.functions.font.args.familyHelpText', { defaultMessage: 'An acceptable {css} web font string', values: { @@ -88,7 +89,7 @@ export const font: ExpressionFunctionFont = { types: ['string'], }, italic: { - default: false, + default: `{ theme "font.italic" default=false }`, help: i18n.translate('expressions.functions.font.args.italicHelpText', { defaultMessage: 'Italicize the text?', }), @@ -96,7 +97,7 @@ export const font: ExpressionFunctionFont = { types: ['boolean'], }, lHeight: { - default: null, + default: `{ theme "font.lHeight" }`, aliases: ['lineHeight'], help: i18n.translate('expressions.functions.font.args.lHeightHelpText', { defaultMessage: 'The line height in pixels', @@ -104,14 +105,14 @@ export const font: ExpressionFunctionFont = { types: ['number', 'null'], }, size: { - default: 14, + default: `{ theme "font.size" default=14 }`, help: i18n.translate('expressions.functions.font.args.sizeHelpText', { defaultMessage: 'The font size in pixels', }), types: ['number'], }, underline: { - default: false, + default: `{ theme "font.underline" default=false }`, help: i18n.translate('expressions.functions.font.args.underlineHelpText', { defaultMessage: 'Underline the text?', }), @@ -119,7 +120,7 @@ export const font: ExpressionFunctionFont = { types: ['boolean'], }, weight: { - default: 'normal', + default: `{ theme "font.weight" default="normal" }`, help: i18n.translate('expressions.functions.font.args.weightHelpText', { defaultMessage: 'The font weight. For example, {list}, or {end}.', values: { diff --git a/src/plugins/expressions/common/expression_functions/specs/index.ts b/src/plugins/expressions/common/expression_functions/specs/index.ts index f7471a8fd9d7..5b9562dae5f2 100644 --- a/src/plugins/expressions/common/expression_functions/specs/index.ts +++ b/src/plugins/expressions/common/expression_functions/specs/index.ts @@ -24,6 +24,7 @@ import { kibanaContextFunction } from './kibana_context'; import { variableSet } from './var_set'; import { variable } from './var'; import { AnyExpressionFunctionDefinition } from '../types'; +import { theme } from './theme'; export const functionSpecs: AnyExpressionFunctionDefinition[] = [ clog, @@ -32,6 +33,7 @@ export const functionSpecs: AnyExpressionFunctionDefinition[] = [ kibanaContextFunction, variableSet, variable, + theme, ]; export * from './clog'; @@ -40,3 +42,4 @@ export * from './kibana'; export * from './kibana_context'; export * from './var_set'; export * from './var'; +export * from './theme'; diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/font.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/font.test.ts index 62e5fd4e0b66..ca4570e9589c 100644 --- a/src/plugins/expressions/common/expression_functions/specs/tests/font.test.ts +++ b/src/plugins/expressions/common/expression_functions/specs/tests/font.test.ts @@ -24,8 +24,19 @@ import { functionWrapper } from './utils'; describe('font', () => { const fn = functionWrapper(font); + const args = { + align: 'left', + color: null, + family: openSans.value, + italic: false, + lHeight: null, + size: 14, + underline: false, + weight: 'normal', + }; + describe('default output', () => { - const result = fn(null); + const result = fn(null, args); it('returns a style', () => { expect(result).toMatchObject({ @@ -39,7 +50,7 @@ describe('font', () => { describe('args', () => { describe('size', () => { it('sets font size', () => { - const result = fn(null, { size: 20 }); + const result = fn(null, { ...args, size: 20 }); expect(result).toMatchObject({ spec: { fontSize: '20px', @@ -47,21 +58,11 @@ describe('font', () => { }); expect(result.css).toContain('font-size:20px'); }); - - it('defaults to 14px', () => { - const result = fn(null); - expect(result).toMatchObject({ - spec: { - fontSize: '14px', - }, - }); - expect(result.css).toContain('font-size:14px'); - }); }); describe('lHeight', () => { it('sets line height', () => { - const result = fn(null, { lHeight: 30 }); + const result = fn(null, { ...args, lHeight: 30 }); expect(result).toMatchObject({ spec: { lineHeight: '30px', @@ -69,31 +70,19 @@ describe('font', () => { }); expect(result.css).toContain('line-height:30px'); }); - - it('defaults to 1', () => { - const result = fn(null); - expect(result.spec.lineHeight).toBe('1'); - expect(result.css).toContain('line-height:1'); - }); }); describe('family', () => { it('sets font family', () => { - const result = fn(null, { family: 'Optima, serif' }); + const result = fn(null, { ...args, family: 'Optima, serif' }); expect(result.spec.fontFamily).toBe('Optima, serif'); expect(result.css).toContain('font-family:Optima, serif'); }); - - it(`defaults to "${openSans.value}"`, () => { - const result = fn(null); - expect(result.spec.fontFamily).toBe(`"${openSans.value}"`); - expect(result.css).toContain(`font-family:"${openSans.value}"`); - }); }); describe('color', () => { it('sets font color', () => { - const result = fn(null, { color: 'blue' }); + const result = fn(null, { ...args, color: 'blue' }); expect(result.spec.color).toBe('blue'); expect(result.css).toContain('color:blue'); }); @@ -101,51 +90,39 @@ describe('font', () => { describe('weight', () => { it('sets font weight', () => { - let result = fn(null, { weight: 'normal' }); + let result = fn(null, { ...args, weight: 'normal' }); expect(result.spec.fontWeight).toBe('normal'); expect(result.css).toContain('font-weight:normal'); - result = fn(null, { weight: 'bold' }); + result = fn(null, { ...args, weight: 'bold' }); expect(result.spec.fontWeight).toBe('bold'); expect(result.css).toContain('font-weight:bold'); - result = fn(null, { weight: 'bolder' }); + result = fn(null, { ...args, weight: 'bolder' }); expect(result.spec.fontWeight).toBe('bolder'); expect(result.css).toContain('font-weight:bolder'); - result = fn(null, { weight: 'lighter' }); + result = fn(null, { ...args, weight: 'lighter' }); expect(result.spec.fontWeight).toBe('lighter'); expect(result.css).toContain('font-weight:lighter'); - result = fn(null, { weight: '400' }); + result = fn(null, { ...args, weight: '400' }); expect(result.spec.fontWeight).toBe('400'); expect(result.css).toContain('font-weight:400'); }); - it("defaults to 'normal'", () => { - const result = fn(null); - expect(result.spec.fontWeight).toBe('normal'); - expect(result.css).toContain('font-weight:normal'); - }); - it('throws when provided an invalid weight', () => { - expect(() => fn(null, { weight: 'foo' })).toThrow(); + expect(() => fn(null, { ...args, weight: 'foo' })).toThrow(); }); }); describe('underline', () => { it('sets text underline', () => { - let result = fn(null, { underline: true }); + let result = fn(null, { ...args, underline: true }); expect(result.spec.textDecoration).toBe('underline'); expect(result.css).toContain('text-decoration:underline'); - result = fn(null, { underline: false }); - expect(result.spec.textDecoration).toBe('none'); - expect(result.css).toContain('text-decoration:none'); - }); - - it('defaults to false', () => { - const result = fn(null); + result = fn(null, { ...args, underline: false }); expect(result.spec.textDecoration).toBe('none'); expect(result.css).toContain('text-decoration:none'); }); @@ -153,17 +130,11 @@ describe('font', () => { describe('italic', () => { it('sets italic', () => { - let result = fn(null, { italic: true }); + let result = fn(null, { ...args, italic: true }); expect(result.spec.fontStyle).toBe('italic'); expect(result.css).toContain('font-style:italic'); - result = fn(null, { italic: false }); - expect(result.spec.fontStyle).toBe('normal'); - expect(result.css).toContain('font-style:normal'); - }); - - it('defaults to false', () => { - const result = fn(null); + result = fn(null, { ...args, italic: false }); expect(result.spec.fontStyle).toBe('normal'); expect(result.css).toContain('font-style:normal'); }); @@ -171,31 +142,25 @@ describe('font', () => { describe('align', () => { it('sets text alignment', () => { - let result = fn(null, { align: 'left' }); + let result = fn(null, { ...args, align: 'left' }); expect(result.spec.textAlign).toBe('left'); expect(result.css).toContain('text-align:left'); - result = fn(null, { align: 'center' }); + result = fn(null, { ...args, align: 'center' }); expect(result.spec.textAlign).toBe('center'); expect(result.css).toContain('text-align:center'); - result = fn(null, { align: 'right' }); + result = fn(null, { ...args, align: 'right' }); expect(result.spec.textAlign).toBe('right'); expect(result.css).toContain('text-align:right'); - result = fn(null, { align: 'justify' }); + result = fn(null, { ...args, align: 'justify' }); expect(result.spec.textAlign).toBe('justify'); expect(result.css).toContain('text-align:justify'); }); - it(`defaults to 'left'`, () => { - const result = fn(null); - expect(result.spec.textAlign).toBe('left'); - expect(result.css).toContain('text-align:left'); - }); - it('throws when provided an invalid alignment', () => { - expect(() => fn(null, { align: 'foo' })).toThrow(); + expect(() => fn(null, { ...args, align: 'foo' })).toThrow(); }); }); }); diff --git a/src/plugins/expressions/common/expression_functions/specs/tests/theme.test.ts b/src/plugins/expressions/common/expression_functions/specs/tests/theme.test.ts new file mode 100644 index 000000000000..263409f0caca --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/tests/theme.test.ts @@ -0,0 +1,63 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { functionWrapper } from './utils'; +import { theme } from '../theme'; +import { ExecutionContext } from '../../../execution/types'; + +describe('expression_functions', () => { + describe('theme', () => { + const fn = functionWrapper(theme); + let context: ExecutionContext; + + let themeProps; + + beforeEach(() => { + themeProps = { + font: { + family: 'Arial', + size: 14, + }, + }; + + context = { + getInitialInput: () => {}, + types: {}, + variables: { theme: themeProps }, + abortSignal: {} as any, + inspectorAdapters: {} as any, + }; + }); + + it('returns the selected variable', () => { + const actual = fn(null, { variable: 'font.family' }, context); + expect(actual).toEqual('Arial'); + }); + + it('returns undefined if variable does not exist', () => { + const actual = fn(null, { variable: 'font.weight' }, context); + expect(actual).toEqual(undefined); + }); + + it('returns default if variable does not exist and default is provided', () => { + const actual = fn(null, { variable: 'font.weight', default: 'normal' }, context); + expect(actual).toEqual('normal'); + }); + }); +}); diff --git a/src/plugins/expressions/common/expression_functions/specs/theme.ts b/src/plugins/expressions/common/expression_functions/specs/theme.ts new file mode 100644 index 000000000000..e27b01674cb5 --- /dev/null +++ b/src/plugins/expressions/common/expression_functions/specs/theme.ts @@ -0,0 +1,65 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { get } from 'lodash'; +import { ExpressionFunctionDefinition } from '../types'; + +interface Arguments { + variable: string; + default: string | number | boolean; +} + +type Output = any; + +export type ExpressionFunctionTheme = ExpressionFunctionDefinition< + 'theme', + null, + Arguments, + Output +>; + +export const theme: ExpressionFunctionTheme = { + name: 'theme', + aliases: [], + help: i18n.translate('expressions.functions.themeHelpText', { + defaultMessage: 'Reads a theme setting.', + }), + inputTypes: ['null'], + args: { + variable: { + aliases: ['_'], + help: i18n.translate('expressions.functions.theme.args.variableHelpText', { + defaultMessage: 'Name of the theme variable to read.', + }), + required: true, + types: ['string'], + }, + default: { + help: i18n.translate('expressions.functions.theme.args.defaultHelpText', { + defaultMessage: 'default value in case theming info is not available.', + }), + }, + }, + fn: (input, args, handlers) => { + // currently we use variable `theme`, but external theme service would be preferable + const vars = handlers.variables.theme || {}; + return get(vars, args.variable, args.default); + }, +}; diff --git a/src/plugins/expressions/common/expression_functions/types.ts b/src/plugins/expressions/common/expression_functions/types.ts index 5979bcffb317..d58d872aff72 100644 --- a/src/plugins/expressions/common/expression_functions/types.ts +++ b/src/plugins/expressions/common/expression_functions/types.ts @@ -28,6 +28,7 @@ import { ExpressionFunctionKibana, ExpressionFunctionVarSet, ExpressionFunctionVar, + ExpressionFunctionTheme, } from './specs'; /** @@ -122,4 +123,5 @@ export interface ExpressionFunctionDefinitions { kibana: ExpressionFunctionKibana; var_set: ExpressionFunctionVarSet; var: ExpressionFunctionVar; + theme: ExpressionFunctionTheme; }