diff --git a/.i18nrc.json b/.i18nrc.json index bdfe444bb99b..36b3777fd1f5 100644 --- a/.i18nrc.json +++ b/.i18nrc.json @@ -18,6 +18,7 @@ "devTools": "src/plugins/dev_tools", "expressions": "src/plugins/expressions", "expressionError": "src/plugins/expression_error", + "expressionImage": "src/plugins/expression_image", "expressionRepeatImage": "src/plugins/expression_repeat_image", "expressionRevealImage": "src/plugins/expression_reveal_image", "expressionShape": "src/plugins/expression_shape", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 2db404c75698..d389c7e29211 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -74,6 +74,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a |Expression Error plugin adds an error renderer to the expression plugin. The renderer will display the error image. +|{kib-repo}blob/{branch}/src/plugins/expression_image/README.md[expressionImage] +|Expression Image plugin adds an image renderer to the expression plugin. The renderer will display the given image. + + |{kib-repo}blob/{branch}/src/plugins/expression_repeat_image/README.md[expressionRepeatImage] |Expression Repeat Image plugin adds a repeatImage function to the expression plugin and an associated renderer. The renderer will display the given image in mutliple instances. diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 63f3c29fa4a3..9b36e3ad796e 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -114,4 +114,5 @@ pageLoadAssetSize: cases: 144442 expressionError: 22127 expressionRepeatImage: 22341 + expressionImage: 19288 expressionShape: 30033 diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 7aca25d2013d..b27ff8663172 100644 --- a/src/dev/storybook/aliases.ts +++ b/src/dev/storybook/aliases.ts @@ -18,6 +18,7 @@ export const storybookAliases = { data_enhanced: 'x-pack/plugins/data_enhanced/.storybook', embeddable: 'src/plugins/embeddable/.storybook', expression_error: 'src/plugins/expression_error/.storybook', + expression_image: 'src/plugins/expression_image/.storybook', expression_repeat_image: 'src/plugins/expression_repeat_image/.storybook', expression_reveal_image: 'src/plugins/expression_reveal_image/.storybook', expression_shape: 'src/plugins/expression_shape/.storybook', diff --git a/src/plugins/expression_image/.storybook/main.js b/src/plugins/expression_image/.storybook/main.js new file mode 100644 index 000000000000..742239e638b8 --- /dev/null +++ b/src/plugins/expression_image/.storybook/main.js @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +// eslint-disable-next-line import/no-commonjs +module.exports = require('@kbn/storybook').defaultConfig; diff --git a/src/plugins/expression_image/README.md b/src/plugins/expression_image/README.md new file mode 100755 index 000000000000..b02c9fd39b3d --- /dev/null +++ b/src/plugins/expression_image/README.md @@ -0,0 +1,9 @@ +# expressionRevealImage + +Expression Image plugin adds an `image` renderer to the expression plugin. The renderer will display the given image. + +--- + +## Development + +See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment. diff --git a/src/plugins/expression_image/__fixtures__/index.ts b/src/plugins/expression_image/__fixtures__/index.ts new file mode 100644 index 000000000000..279e8d87446b --- /dev/null +++ b/src/plugins/expression_image/__fixtures__/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { imageFunction } from '../common/expression_functions'; diff --git a/src/plugins/expression_image/common/constants.ts b/src/plugins/expression_image/common/constants.ts new file mode 100644 index 000000000000..0ac8bec2e1f7 --- /dev/null +++ b/src/plugins/expression_image/common/constants.ts @@ -0,0 +1,14 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export const PLUGIN_ID = 'expressionImage'; +export const PLUGIN_NAME = 'expressionImage'; + +export const CONTEXT = '_context_'; +export const BASE64 = '`base64`'; +export const URL = 'URL'; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.test.js b/src/plugins/expression_image/common/expression_functions/image_function.test.ts similarity index 59% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.test.js rename to src/plugins/expression_image/common/expression_functions/image_function.test.ts index 862560e5643d..7deaeb90b411 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.test.js +++ b/src/plugins/expression_image/common/expression_functions/image_function.test.ts @@ -1,72 +1,78 @@ /* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. */ import expect from '@kbn/expect'; +import { ExecutionContext } from 'src/plugins/expressions'; import { + functionWrapper, getElasticLogo, getElasticOutline, - functionWrapper, -} from '../../../../../../src/plugins/presentation_util/common/lib'; -import { image } from './image'; +} from '../../../presentation_util/common/lib'; +import { imageFunction as image } from './image_function'; -// TODO: the test was not running and is not up to date describe('image', () => { const fn = functionWrapper(image); - let elasticLogo; - let elasticOutline; + let elasticLogo: string; + let elasticOutline: string; + beforeEach(async () => { - elasticLogo = (await getElasticLogo()).elasticLogo; - elasticOutline = (await getElasticOutline()).elasticOutline; + elasticLogo = (await getElasticLogo())?.elasticLogo; + elasticOutline = (await getElasticOutline())?.elasticOutline; }); it('returns an image object using a dataUrl', async () => { - const result = await fn(null, { dataurl: elasticOutline, mode: 'cover' }); + const result = await fn( + null, + { dataurl: elasticOutline, mode: 'cover' }, + {} as ExecutionContext + ); expect(result).to.have.property('type', 'image'); }); describe('args', () => { describe('dataurl', () => { it('sets the source of the image using dataurl', async () => { - const result = await fn(null, { dataurl: elasticOutline }); + const result = await fn(null, { dataurl: elasticOutline }, {} as ExecutionContext); expect(result).to.have.property('dataurl', elasticOutline); }); it.skip('sets the source of the image using url', async () => { // This is skipped because functionWrapper doesn't use the actual // interpreter and doesn't resolve aliases - const result = await fn(null, { url: elasticOutline }); + const result = await fn(null, { url: elasticOutline }, {} as ExecutionContext); expect(result).to.have.property('dataurl', elasticOutline); }); it('defaults to the elasticLogo if not provided', async () => { - const result = await fn(null); + const result = await fn(null, {}, {} as ExecutionContext); expect(result).to.have.property('dataurl', elasticLogo); }); }); describe('sets the mode', () => { it('to contain', async () => { - const result = await fn(null, { mode: 'contain' }); + const result = await fn(null, { mode: 'contain' }, {} as ExecutionContext); expect(result).to.have.property('mode', 'contain'); }); it('to cover', async () => { - const result = await fn(null, { mode: 'cover' }); + const result = await fn(null, { mode: 'cover' }, {} as ExecutionContext); expect(result).to.have.property('mode', 'cover'); }); it('to stretch', async () => { - const result = await fn(null, { mode: 'stretch' }); + const result = await fn(null, { mode: 'stretch' }, {} as ExecutionContext); expect(result).to.have.property('mode', '100% 100%'); }); it("defaults to 'contain' if not provided", async () => { - const result = await fn(null); + const result = await fn(null, {}, {} as ExecutionContext); expect(result).to.have.property('mode', 'contain'); }); }); diff --git a/src/plugins/expression_image/common/expression_functions/image_function.ts b/src/plugins/expression_image/common/expression_functions/image_function.ts new file mode 100644 index 000000000000..8394681e1b10 --- /dev/null +++ b/src/plugins/expression_image/common/expression_functions/image_function.ts @@ -0,0 +1,96 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { i18n } from '@kbn/i18n'; +import { getElasticLogo, resolveWithMissingImage } from '../../../presentation_util/common/lib'; +import { BASE64, URL } from '../constants'; +import { ExpressionImageFunction, ImageMode } from '../types'; + +export const strings = { + help: i18n.translate('expressionImage.functions.imageHelpText', { + defaultMessage: + 'Displays an image. Provide an image asset as a {BASE64} data {URL}, or pass in a sub-expression.', + values: { + BASE64, + URL, + }, + }), + args: { + dataurl: i18n.translate('expressionImage.functions.image.args.dataurlHelpText', { + defaultMessage: 'The {https} {URL} or {BASE64} data {URL} of an image.', + values: { + BASE64, + https: 'HTTP(S)', + URL, + }, + }), + mode: i18n.translate('expressionImage.functions.image.args.modeHelpText', { + defaultMessage: + '{contain} shows the entire image, scaled to fit. ' + + '{cover} fills the container with the image, cropping from the sides or bottom as needed. ' + + '{stretch} resizes the height and width of the image to 100% of the container.', + values: { + contain: `\`"${ImageMode.CONTAIN}"\``, + cover: `\`"${ImageMode.COVER}"\``, + stretch: `\`"${ImageMode.STRETCH}"\``, + }, + }), + }, +}; + +const errors = { + invalidImageMode: () => + i18n.translate('expressionImage.functions.image.invalidImageModeErrorMessage', { + defaultMessage: '"mode" must be "{contain}", "{cover}", or "{stretch}"', + values: { + contain: ImageMode.CONTAIN, + cover: ImageMode.COVER, + stretch: ImageMode.STRETCH, + }, + }), +}; + +export const imageFunction: ExpressionImageFunction = () => { + const { help, args: argHelp } = strings; + + return { + name: 'image', + aliases: [], + type: 'image', + inputTypes: ['null'], + help, + args: { + dataurl: { + // This was accepting dataurl, but there was no facility in fn for checking type and handling a dataurl type. + types: ['string', 'null'], + help: argHelp.dataurl, + aliases: ['_', 'url'], + default: null, + }, + mode: { + types: ['string'], + help: argHelp.mode, + default: 'contain', + options: Object.values(ImageMode), + }, + }, + fn: async (input, { dataurl, mode }) => { + if (!mode || !Object.values(ImageMode).includes(mode)) { + throw new Error(errors.invalidImageMode()); + } + + const modeStyle = mode === 'stretch' ? '100% 100%' : mode; + const { elasticLogo } = await getElasticLogo(); + return { + type: 'image', + mode: modeStyle, + dataurl: resolveWithMissingImage(dataurl, elasticLogo) as string, + }; + }, + }; +}; diff --git a/src/plugins/expression_image/common/expression_functions/index.ts b/src/plugins/expression_image/common/expression_functions/index.ts new file mode 100644 index 000000000000..5274069d3d17 --- /dev/null +++ b/src/plugins/expression_image/common/expression_functions/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { imageFunction } from './image_function'; + +export const functions = [imageFunction]; + +export { imageFunction }; diff --git a/src/plugins/expression_image/common/index.ts b/src/plugins/expression_image/common/index.ts new file mode 100755 index 000000000000..f251b9cf01cb --- /dev/null +++ b/src/plugins/expression_image/common/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export * from './constants'; +export * from './types'; diff --git a/src/plugins/expression_image/common/types/expression_functions.ts b/src/plugins/expression_image/common/types/expression_functions.ts new file mode 100644 index 000000000000..5ee9ed93cc87 --- /dev/null +++ b/src/plugins/expression_image/common/types/expression_functions.ts @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import { ExpressionFunctionDefinition } from '../../../expressions'; + +export enum ImageMode { + CONTAIN = 'contain', + COVER = 'cover', + STRETCH = 'stretch', +} + +interface Arguments { + dataurl: string | null; + mode: ImageMode | null; +} + +export interface Return { + type: 'image'; + mode: string; + dataurl: string; +} + +export type ExpressionImageFunction = () => ExpressionFunctionDefinition< + 'image', + null, + Arguments, + Promise +>; diff --git a/src/plugins/expression_image/common/types/expression_renderers.ts b/src/plugins/expression_image/common/types/expression_renderers.ts new file mode 100644 index 000000000000..c4ff7a7f18ae --- /dev/null +++ b/src/plugins/expression_image/common/types/expression_renderers.ts @@ -0,0 +1,21 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ImageMode } from './expression_functions'; + +export type OriginString = 'bottom' | 'left' | 'top' | 'right'; + +export interface ImageRendererConfig { + dataurl: string | null; + mode: ImageMode | null; +} + +export interface NodeDimensions { + width: number; + height: number; +} diff --git a/src/plugins/expression_image/common/types/index.ts b/src/plugins/expression_image/common/types/index.ts new file mode 100644 index 000000000000..ec934e7affe8 --- /dev/null +++ b/src/plugins/expression_image/common/types/index.ts @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +export * from './expression_functions'; +export * from './expression_renderers'; diff --git a/src/plugins/expression_image/jest.config.js b/src/plugins/expression_image/jest.config.js new file mode 100644 index 000000000000..3d5bc9f184c6 --- /dev/null +++ b/src/plugins/expression_image/jest.config.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +module.exports = { + preset: '@kbn/test', + rootDir: '../../..', + roots: ['/src/plugins/expression_image'], +}; diff --git a/src/plugins/expression_image/kibana.json b/src/plugins/expression_image/kibana.json new file mode 100755 index 000000000000..13b4e989b8f7 --- /dev/null +++ b/src/plugins/expression_image/kibana.json @@ -0,0 +1,9 @@ +{ + "id": "expressionImage", + "version": "1.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": true, + "requiredPlugins": ["expressions", "presentationUtil"], + "optionalPlugins": [] +} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/__snapshots__/image.stories.storyshot b/src/plugins/expression_image/public/expression_renderers/__stories__/__snapshots__/image.stories.storyshot similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/__snapshots__/image.stories.storyshot rename to src/plugins/expression_image/public/expression_renderers/__stories__/__snapshots__/image.stories.storyshot diff --git a/src/plugins/expression_image/public/expression_renderers/__stories__/image_renderer.stories.tsx b/src/plugins/expression_image/public/expression_renderers/__stories__/image_renderer.stories.tsx new file mode 100644 index 000000000000..d75aa1a4263e --- /dev/null +++ b/src/plugins/expression_image/public/expression_renderers/__stories__/image_renderer.stories.tsx @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import React from 'react'; +import { storiesOf } from '@storybook/react'; +import { Render, waitFor } from '../../../../presentation_util/public/__stories__'; +import { imageRenderer } from '../image_renderer'; +import { getElasticLogo } from '../../../../../../src/plugins/presentation_util/common/lib'; +import { ImageMode } from '../../../common'; + +const Renderer = ({ elasticLogo }: { elasticLogo: string }) => { + const config = { + dataurl: elasticLogo, + mode: ImageMode.COVER, + }; + + return ; +}; + +storiesOf('renderers/image', module).add( + 'default', + (_, props) => { + return ; + }, + { decorators: [waitFor(getElasticLogo())] } +); diff --git a/src/plugins/expression_image/public/expression_renderers/image_renderer.tsx b/src/plugins/expression_image/public/expression_renderers/image_renderer.tsx new file mode 100644 index 000000000000..3d542a9978a8 --- /dev/null +++ b/src/plugins/expression_image/public/expression_renderers/image_renderer.tsx @@ -0,0 +1,53 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ +import React from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions'; +import { i18n } from '@kbn/i18n'; +import { getElasticLogo, isValidUrl } from '../../../presentation_util/public'; +import { ImageRendererConfig } from '../../common/types'; + +const strings = { + getDisplayName: () => + i18n.translate('expressionImage.renderer.image.displayName', { + defaultMessage: 'Image', + }), + getHelpDescription: () => + i18n.translate('expressionImage.renderer.image.helpDescription', { + defaultMessage: 'Render an image', + }), +}; + +export const imageRenderer = (): ExpressionRenderDefinition => ({ + name: 'image', + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, + render: async ( + domNode: HTMLElement, + config: ImageRendererConfig, + handlers: IInterpreterRenderHandlers + ) => { + const { elasticLogo } = await getElasticLogo(); + const dataurl = isValidUrl(config.dataurl ?? '') ? config.dataurl : elasticLogo; + + const style = { + height: '100%', + backgroundImage: `url(${dataurl})`, + backgroundRepeat: 'no-repeat', + backgroundPosition: 'center center', + backgroundSize: config.mode as string, + }; + + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + + render(
, domNode, () => handlers.done()); + }, +}); diff --git a/src/plugins/expression_image/public/expression_renderers/index.ts b/src/plugins/expression_image/public/expression_renderers/index.ts new file mode 100644 index 000000000000..96c274f05a7a --- /dev/null +++ b/src/plugins/expression_image/public/expression_renderers/index.ts @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { imageRenderer } from './image_renderer'; + +export const renderers = [imageRenderer]; + +export { imageRenderer }; diff --git a/src/plugins/expression_image/public/index.ts b/src/plugins/expression_image/public/index.ts new file mode 100755 index 000000000000..522418640bd1 --- /dev/null +++ b/src/plugins/expression_image/public/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ExpressionImagePlugin } from './plugin'; + +export type { ExpressionImagePluginSetup, ExpressionImagePluginStart } from './plugin'; + +export function plugin() { + return new ExpressionImagePlugin(); +} + +export * from './expression_renderers'; diff --git a/src/plugins/expression_image/public/plugin.ts b/src/plugins/expression_image/public/plugin.ts new file mode 100755 index 000000000000..44feea412163 --- /dev/null +++ b/src/plugins/expression_image/public/plugin.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { ExpressionsStart, ExpressionsSetup } from '../../expressions/public'; +import { imageRenderer } from './expression_renderers'; +import { imageFunction } from '../common/expression_functions'; + +interface SetupDeps { + expressions: ExpressionsSetup; +} + +interface StartDeps { + expression: ExpressionsStart; +} + +export type ExpressionImagePluginSetup = void; +export type ExpressionImagePluginStart = void; + +export class ExpressionImagePlugin + implements Plugin { + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionImagePluginSetup { + expressions.registerFunction(imageFunction); + expressions.registerRenderer(imageRenderer); + } + + public start(core: CoreStart): ExpressionImagePluginStart {} + + public stop() {} +} diff --git a/src/plugins/expression_image/server/index.ts b/src/plugins/expression_image/server/index.ts new file mode 100755 index 000000000000..a4c6ee888d08 --- /dev/null +++ b/src/plugins/expression_image/server/index.ts @@ -0,0 +1,15 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { ExpressionImagePlugin } from './plugin'; + +export type { ExpressionImagePluginSetup, ExpressionImagePluginStart } from './plugin'; + +export function plugin() { + return new ExpressionImagePlugin(); +} diff --git a/src/plugins/expression_image/server/plugin.ts b/src/plugins/expression_image/server/plugin.ts new file mode 100755 index 000000000000..d3259d45107e --- /dev/null +++ b/src/plugins/expression_image/server/plugin.ts @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; +import { ExpressionsServerStart, ExpressionsServerSetup } from '../../expressions/server'; +import { imageFunction } from '../common/expression_functions'; + +interface SetupDeps { + expressions: ExpressionsServerSetup; +} + +interface StartDeps { + expression: ExpressionsServerStart; +} + +export type ExpressionImagePluginSetup = void; +export type ExpressionImagePluginStart = void; + +export class ExpressionImagePlugin + implements Plugin { + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionImagePluginSetup { + expressions.registerFunction(imageFunction); + } + + public start(core: CoreStart): ExpressionImagePluginStart {} + + public stop() {} +} diff --git a/src/plugins/expression_image/tsconfig.json b/src/plugins/expression_image/tsconfig.json new file mode 100644 index 000000000000..5fab51496c97 --- /dev/null +++ b/src/plugins/expression_image/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "isolatedModules": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + "__fixtures__/**/*", + ], + "references": [ + { "path": "../../core/tsconfig.json" }, + { "path": "../presentation_util/tsconfig.json" }, + { "path": "../expressions/tsconfig.json" }, + ] +} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.ts deleted file mode 100644 index e661a15cea3a..000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/image.ts +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { ExpressionFunctionDefinition } from 'src/plugins/expressions/common'; -import { getFunctionHelp, getFunctionErrors } from '../../../i18n'; - -import { - getElasticLogo, - resolveWithMissingImage, -} from '../../../../../../src/plugins/presentation_util/common/lib'; - -export enum ImageMode { - CONTAIN = 'contain', - COVER = 'cover', - STRETCH = 'stretch', -} - -interface Arguments { - dataurl: string | null; - mode: ImageMode | null; -} - -export interface Return { - type: 'image'; - mode: string; - dataurl: string; -} - -export function image(): ExpressionFunctionDefinition<'image', null, Arguments, Promise> { - const { help, args: argHelp } = getFunctionHelp().image; - const errors = getFunctionErrors().image; - return { - name: 'image', - aliases: [], - type: 'image', - inputTypes: ['null'], - help, - args: { - dataurl: { - // This was accepting dataurl, but there was no facility in fn for checking type and handling a dataurl type. - types: ['string', 'null'], - help: argHelp.dataurl, - aliases: ['_', 'url'], - default: null, - }, - mode: { - types: ['string'], - help: argHelp.mode, - default: 'contain', - options: Object.values(ImageMode), - }, - }, - fn: async (input, { dataurl, mode }) => { - if (!mode || !Object.values(ImageMode).includes(mode)) { - throw errors.invalidImageMode(); - } - const { elasticLogo } = await getElasticLogo(); - - if (dataurl === null) { - dataurl = elasticLogo; - } - - const modeStyle = mode === 'stretch' ? '100% 100%' : mode; - return { - type: 'image', - mode: modeStyle, - dataurl: resolveWithMissingImage(dataurl, elasticLogo) as string, - }; - }, - }; -} diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts index 6ab7abac985c..3de3275ebb23 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts @@ -29,7 +29,6 @@ import { gt } from './gt'; import { gte } from './gte'; import { head } from './head'; import { ifFn } from './if'; -import { image } from './image'; import { joinRows } from './join_rows'; import { lt } from './lt'; import { lte } from './lte'; @@ -79,7 +78,6 @@ export const functions = [ gte, head, ifFn, - image, lt, lte, joinRows, diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/image.stories.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/image.stories.tsx deleted file mode 100644 index a8ac14768bfa..000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/image.stories.tsx +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { storiesOf } from '@storybook/react'; -import { image } from '../image'; -import { getElasticLogo } from '../../../../../../src/plugins/presentation_util/common/lib'; -import { waitFor } from '../../../../../../src/plugins/presentation_util/public/__stories__'; -import { Render } from './render'; - -const Renderer = ({ elasticLogo }: { elasticLogo: string }) => { - const config = { - type: 'image' as 'image', - mode: 'cover', - dataurl: elasticLogo, - }; - - return ; -}; - -storiesOf('renderers/image', module).add( - 'default', - (_, props) => , - { decorators: [waitFor(getElasticLogo())] } -); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts index 8eabae4c661d..80ca5e68860b 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts @@ -5,7 +5,6 @@ * 2.0. */ -import { image } from './image'; import { markdown } from './markdown'; import { metric } from './metric'; import { pie } from './pie'; @@ -14,6 +13,6 @@ import { progress } from './progress'; import { text } from './text'; import { table } from './table'; -export const renderFunctions = [image, markdown, metric, pie, plot, progress, table, text]; +export const renderFunctions = [markdown, metric, pie, plot, progress, table, text]; export const renderFunctionFactories = []; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts index 0c824fb3dd25..eab3b88b0fe2 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts @@ -5,6 +5,7 @@ * 2.0. */ +import { imageRenderer } from '../../../../../src/plugins/expression_image/public'; import { errorRenderer, debugRenderer } from '../../../../../src/plugins/expression_error/public'; import { repeatImageRenderer } from '../../../../../src/plugins/expression_repeat_image/public'; import { revealImageRenderer } from '../../../../../src/plugins/expression_reveal_image/public'; @@ -14,6 +15,7 @@ export const renderFunctions = [ revealImageRenderer, debugRenderer, errorRenderer, + imageRenderer, shapeRenderer, repeatImageRenderer, ]; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/image.tsx b/x-pack/plugins/canvas/canvas_plugin_src/renderers/image.tsx deleted file mode 100644 index 78e3ecb7a4c9..000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/image.tsx +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import ReactDOM from 'react-dom'; -import React from 'react'; -import { - getElasticLogo, - isValidUrl, -} from '../../../../../src/plugins/presentation_util/common/lib'; -import { Return as Arguments } from '../functions/common/image'; -import { RendererStrings } from '../../i18n'; -import { RendererFactory } from '../../types'; - -const { image: strings } = RendererStrings; - -export const image: RendererFactory = () => ({ - name: 'image', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, - render: async (domNode, config, handlers) => { - const { elasticLogo } = await getElasticLogo(); - const dataurl = isValidUrl(config.dataurl) ? config.dataurl : elasticLogo; - - const style = { - height: '100%', - backgroundImage: `url(${dataurl})`, - backgroundRepeat: 'no-repeat', - backgroundPosition: 'center center', - backgroundSize: config.mode, - }; - - ReactDOM.render(
, domNode, () => handlers.done()); - - handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode)); - }, -}); diff --git a/x-pack/plugins/canvas/i18n/functions/dict/image.ts b/x-pack/plugins/canvas/i18n/functions/dict/image.ts deleted file mode 100644 index b619d550f9ef..000000000000 --- a/x-pack/plugins/canvas/i18n/functions/dict/image.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { i18n } from '@kbn/i18n'; -import { image } from '../../../canvas_plugin_src/functions/common/image'; -import { FunctionHelp } from '../function_help'; -import { FunctionFactory } from '../../../types'; -import { - URL, - BASE64, - IMAGE_MODE_CONTAIN, - IMAGE_MODE_COVER, - IMAGE_MODE_STRETCH, -} from '../../constants'; - -export const help: FunctionHelp> = { - help: i18n.translate('xpack.canvas.functions.imageHelpText', { - defaultMessage: - 'Displays an image. Provide an image asset as a {BASE64} data {URL}, or pass in a sub-expression.', - values: { - BASE64, - URL, - }, - }), - args: { - dataurl: i18n.translate('xpack.canvas.functions.image.args.dataurlHelpText', { - defaultMessage: 'The {https} {URL} or {BASE64} data {URL} of an image.', - values: { - BASE64, - https: 'HTTP(S)', - URL, - }, - }), - mode: i18n.translate('xpack.canvas.functions.image.args.modeHelpText', { - defaultMessage: - '{contain} shows the entire image, scaled to fit. ' + - '{cover} fills the container with the image, cropping from the sides or bottom as needed. ' + - '{stretch} resizes the height and width of the image to 100% of the container.', - values: { - contain: `\`"${IMAGE_MODE_CONTAIN}"\``, - cover: `\`"${IMAGE_MODE_COVER}"\``, - stretch: `\`"${IMAGE_MODE_STRETCH}"\``, - }, - }), - }, -}; - -export const errors = { - invalidImageMode: () => - new Error( - i18n.translate('xpack.canvas.functions.image.invalidImageModeErrorMessage', { - defaultMessage: '"mode" must be "{contain}", "{cover}", or "{stretch}"', - values: { - contain: IMAGE_MODE_CONTAIN, - cover: IMAGE_MODE_COVER, - stretch: IMAGE_MODE_STRETCH, - }, - }) - ), -}; diff --git a/x-pack/plugins/canvas/i18n/functions/function_errors.ts b/x-pack/plugins/canvas/i18n/functions/function_errors.ts index a01cb09a3834..1e515ece6356 100644 --- a/x-pack/plugins/canvas/i18n/functions/function_errors.ts +++ b/x-pack/plugins/canvas/i18n/functions/function_errors.ts @@ -14,7 +14,6 @@ import { errors as csv } from './dict/csv'; import { errors as date } from './dict/date'; import { errors as demodata } from './dict/demodata'; import { errors as getCell } from './dict/get_cell'; -import { errors as image } from './dict/image'; import { errors as joinRows } from './dict/join_rows'; import { errors as ply } from './dict/ply'; import { errors as pointseries } from './dict/pointseries'; @@ -32,7 +31,6 @@ export const getFunctionErrors = () => ({ date, demodata, getCell, - image, joinRows, ply, pointseries, diff --git a/x-pack/plugins/canvas/i18n/functions/function_help.ts b/x-pack/plugins/canvas/i18n/functions/function_help.ts index 85063b9dbfd6..b0389f6b93b1 100644 --- a/x-pack/plugins/canvas/i18n/functions/function_help.ts +++ b/x-pack/plugins/canvas/i18n/functions/function_help.ts @@ -40,7 +40,6 @@ import { help as gt } from './dict/gt'; import { help as gte } from './dict/gte'; import { help as head } from './dict/head'; import { help as ifFn } from './dict/if'; -import { help as image } from './dict/image'; import { help as joinRows } from './dict/join_rows'; import { help as location } from './dict/location'; import { help as lt } from './dict/lt'; @@ -199,7 +198,6 @@ export const getFunctionHelp = (): FunctionHelpDict => ({ head, if: ifFn, joinRows, - image, location, lt, lte, diff --git a/x-pack/plugins/canvas/i18n/renderers.ts b/x-pack/plugins/canvas/i18n/renderers.ts index 80f1a5aecc89..faa43fe03817 100644 --- a/x-pack/plugins/canvas/i18n/renderers.ts +++ b/x-pack/plugins/canvas/i18n/renderers.ts @@ -55,16 +55,6 @@ export const RendererStrings = { defaultMessage: 'Renders an embeddable Saved Object from other parts of Kibana', }), }, - image: { - getDisplayName: () => - i18n.translate('xpack.canvas.renderer.image.displayName', { - defaultMessage: 'Image', - }), - getHelpDescription: () => - i18n.translate('xpack.canvas.renderer.image.helpDescription', { - defaultMessage: 'Render an image', - }), - }, markdown: { getDisplayName: () => i18n.translate('xpack.canvas.renderer.markdown.displayName', { diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index 1692d90884a6..acede1c9c2aa 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -11,6 +11,7 @@ "data", "embeddable", "expressionError", + "expressionImage", "expressionRepeatImage", "expressionRevealImage", "expressionShape", diff --git a/x-pack/plugins/canvas/shareable_runtime/components/__stories__/rendered_element.stories.tsx b/x-pack/plugins/canvas/shareable_runtime/components/__stories__/rendered_element.stories.tsx index db74dd7514ee..ee609f42f1cf 100644 --- a/x-pack/plugins/canvas/shareable_runtime/components/__stories__/rendered_element.stories.tsx +++ b/x-pack/plugins/canvas/shareable_runtime/components/__stories__/rendered_element.stories.tsx @@ -9,7 +9,7 @@ import { storiesOf } from '@storybook/react'; import React from 'react'; import { ExampleContext } from '../../test/context_example'; -import { image } from '../../../canvas_plugin_src/renderers/image'; +import { imageFunction } from '../../../../../../src/plugins/expression_image/__fixtures__'; import { sharedWorkpads } from '../../test'; import { RenderedElement, RenderedElementComponent } from '../rendered_element'; @@ -30,7 +30,7 @@ storiesOf('shareables/RenderedElement', module) ; } /** @@ -64,7 +65,7 @@ export class RenderedElementComponent extends PureComponent { try { fn.render(this.ref.current, value.value, createHandlers()); - } catch (e) { + } catch (e: any) { // eslint-disable-next-line no-console console.log(as, e.message); } diff --git a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js index d5f0a2196814..9b86ebddbd9b 100644 --- a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js +++ b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js @@ -5,7 +5,6 @@ * 2.0. */ -import { image } from '../canvas_plugin_src/renderers/image'; import { markdown } from '../canvas_plugin_src/renderers/markdown'; import { metric } from '../canvas_plugin_src/renderers/metric'; import { pie } from '../canvas_plugin_src/renderers/pie'; @@ -13,6 +12,7 @@ import { plot } from '../canvas_plugin_src/renderers/plot'; import { progress } from '../canvas_plugin_src/renderers/progress'; import { table } from '../canvas_plugin_src/renderers/table'; import { text } from '../canvas_plugin_src/renderers/text'; +import { imageRenderer as image } from '../../../../src/plugins/expression_image/public'; import { errorRenderer as error, debugRenderer as debug, diff --git a/x-pack/plugins/canvas/tsconfig.json b/x-pack/plugins/canvas/tsconfig.json index 6181df5abe46..6d57aee565c8 100644 --- a/x-pack/plugins/canvas/tsconfig.json +++ b/x-pack/plugins/canvas/tsconfig.json @@ -32,6 +32,7 @@ { "path": "../../../src/plugins/embeddable/tsconfig.json" }, { "path": "../../../src/plugins/expressions/tsconfig.json" }, { "path": "../../../src/plugins/expression_error/tsconfig.json" }, + { "path": "../../../src/plugins/expression_image/tsconfig.json" }, { "path": "../../../src/plugins/expression_repeat_image/tsconfig.json" }, { "path": "../../../src/plugins/expression_reveal_image/tsconfig.json" }, { "path": "../../../src/plugins/expression_shape/tsconfig.json" }, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index f0356a99fe59..6e1f42a438f6 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6706,10 +6706,10 @@ "xpack.canvas.functions.if.args.elseHelpText": "条件が {BOOLEAN_FALSE} の場合の戻り値です。指定されておらず、条件が満たされていない場合は、元の {CONTEXT} が戻されます。", "xpack.canvas.functions.if.args.thenHelpText": "条件が {BOOLEAN_TRUE} の場合の戻り値です。指定されておらず、条件が満たされている場合は、元の {CONTEXT} が戻されます。", "xpack.canvas.functions.ifHelpText": "条件付きロジックを実行します。", - "xpack.canvas.functions.image.args.dataurlHelpText": "画像の {https} {URL} または {BASE64} データ {URL} です。", - "xpack.canvas.functions.image.args.modeHelpText": "{contain} はサイズに合わせて拡大・縮小して画像全体を表示し、{cover} はコンテナーを画像で埋め、必要に応じて両端や下をクロップします。{stretch} は画像の高さと幅をコンテナーの 100% になるよう変更します。", - "xpack.canvas.functions.image.invalidImageModeErrorMessage": "「mode」は「{contain}」、「{cover}」、または「{stretch}」でなければなりません", - "xpack.canvas.functions.imageHelpText": "画像を表示します。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。", + "expressionImage.functions.image.args.dataurlHelpText": "画像の {https} {URL} または {BASE64} データ {URL} です。", + "expressionImage.functions.image.args.modeHelpText": "{contain} はサイズに合わせて拡大・縮小して画像全体を表示し、{cover} はコンテナーを画像で埋め、必要に応じて両端や下をクロップします。{stretch} は画像の高さと幅をコンテナーの 100% になるよう変更します。", + "expressionImage.functions.image.invalidImageModeErrorMessage": "「mode」は「{contain}」、「{cover}」、または「{stretch}」でなければなりません", + "expressionImage.functions.imageHelpText": "画像を表示します。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。", "xpack.canvas.functions.joinRows.args.columnHelpText": "値を抽出する列またはフィールド。", "xpack.canvas.functions.joinRows.args.distinctHelpText": "一意の値のみを抽出しますか?", "xpack.canvas.functions.joinRows.args.quoteHelpText": "各抽出された値を囲む引用符文字。", @@ -6957,8 +6957,10 @@ "xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel": "すべて", "xpack.canvas.renderer.embeddable.displayName": "埋め込み可能", "xpack.canvas.renderer.embeddable.helpDescription": "Kibana の他の部分から埋め込み可能な保存済みオブジェクトをレンダリングします", - "xpack.canvas.renderer.image.displayName": "画像", - "xpack.canvas.renderer.image.helpDescription": "画像をレンダリングします", + "expressionError.renderer.error.displayName": "エラー情報", + "expressionError.renderer.error.helpDescription": "エラーデータをユーザーにわかるようにレンダリングします", + "expressionImage.renderer.image.displayName": "画像", + "expressionImage.renderer.image.helpDescription": "画像をレンダリングします", "xpack.canvas.renderer.markdown.displayName": "マークダウン", "xpack.canvas.renderer.markdown.helpDescription": "{MARKDOWN} インプットを使用して {HTML} を表示", "xpack.canvas.renderer.metric.displayName": "メトリック", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 071ecf55552a..85d73a1d2f46 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6747,10 +6747,10 @@ "xpack.canvas.functions.if.args.elseHelpText": "条件为 {BOOLEAN_FALSE} 时的返回值。未指定且条件未满足时,将返回原始 {CONTEXT}。", "xpack.canvas.functions.if.args.thenHelpText": "条件为 {BOOLEAN_TRUE} 时的返回值。未指定且条件满足时,将返回原始 {CONTEXT}。", "xpack.canvas.functions.ifHelpText": "执行条件逻辑。", - "xpack.canvas.functions.image.args.dataurlHelpText": "图像的 {https} {URL} 或 {BASE64} 数据 {URL}。", - "xpack.canvas.functions.image.args.modeHelpText": "{contain} 将显示整个图像,图像缩放至适合大小。{cover} 将使用该图像填充容器,根据需要在两边或底部裁剪图像。{stretch} 将图像的高和宽调整为容器的 100%。", - "xpack.canvas.functions.image.invalidImageModeErrorMessage": "“mode”必须为“{contain}”、“{cover}”或“{stretch}”", - "xpack.canvas.functions.imageHelpText": "显示图像。以 {BASE64} 数据 {URL} 的形式提供图像资产或传入子表达式。", + "expressionImage.functions.image.args.dataurlHelpText": "图像的 {https} {URL} 或 {BASE64} 数据 {URL}。", + "expressionImage.functions.image.args.modeHelpText": "{contain} 将显示整个图像,图像缩放至适合大小。{cover} 将使用该图像填充容器,根据需要在两边或底部裁剪图像。{stretch} 将图像的高和宽调整为容器的 100%。", + "expressionImage.functions.image.invalidImageModeErrorMessage": "“mode”必须为“{contain}”、“{cover}”或“{stretch}”", + "expressionImage.functions.imageHelpText": "显示图像。以 {BASE64} 数据 {URL} 的形式提供图像资产或传入子表达式。", "xpack.canvas.functions.joinRows.args.columnHelpText": "从其中提取值的列或字段。", "xpack.canvas.functions.joinRows.args.distinctHelpText": "仅提取唯一值?", "xpack.canvas.functions.joinRows.args.quoteHelpText": "要将每个提取的值引起来的引号字符。", @@ -6998,8 +6998,10 @@ "xpack.canvas.renderer.dropdownFilter.matchAllOptionLabel": "任意", "xpack.canvas.renderer.embeddable.displayName": "可嵌入", "xpack.canvas.renderer.embeddable.helpDescription": "从 Kibana 的其他部分呈现可嵌入的已保存对象", - "xpack.canvas.renderer.image.displayName": "图像", - "xpack.canvas.renderer.image.helpDescription": "呈现图像", + "expressionError.renderer.error.displayName": "错误信息", + "expressionError.renderer.error.helpDescription": "以用户友好的方式呈现错误数据", + "expressionImage.renderer.image.displayName": "图像", + "expressionImage.renderer.image.helpDescription": "呈现图像", "xpack.canvas.renderer.markdown.displayName": "Markdown", "xpack.canvas.renderer.markdown.helpDescription": "使用 {MARKDOWN} 输入呈现 {HTML}", "xpack.canvas.renderer.metric.displayName": "指标",