diff --git a/.i18nrc.json b/.i18nrc.json index ad32edb67b83..bdfe444bb99b 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", + "expressionRepeatImage": "src/plugins/expression_repeat_image", "expressionRevealImage": "src/plugins/expression_reveal_image", "expressionShape": "src/plugins/expression_shape", "inputControl": "src/plugins/input_control_vis", diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 1466385bb694..2db404c75698 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_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. + + |{kib-repo}blob/{branch}/src/plugins/expression_reveal_image/README.md[expressionRevealImage] |Expression Reveal Image plugin adds a revealImage function to the expression plugin and an associated renderer. The renderer will display the given percentage of a given image. diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index e78bfa05305b..63f3c29fa4a3 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -113,4 +113,5 @@ pageLoadAssetSize: expressionRevealImage: 25675 cases: 144442 expressionError: 22127 + expressionRepeatImage: 22341 expressionShape: 30033 diff --git a/src/dev/storybook/aliases.ts b/src/dev/storybook/aliases.ts index 51ed25bfc69f..7aca25d2013d 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_repeat_image: 'src/plugins/expression_repeat_image/.storybook', expression_reveal_image: 'src/plugins/expression_reveal_image/.storybook', expression_shape: 'src/plugins/expression_shape/.storybook', infra: 'x-pack/plugins/infra/.storybook', diff --git a/src/plugins/expression_error/public/components/error/error.tsx b/src/plugins/expression_error/public/components/error/error.tsx index 99318357d860..637309448da2 100644 --- a/src/plugins/expression_error/public/components/error/error.tsx +++ b/src/plugins/expression_error/public/components/error/error.tsx @@ -12,6 +12,12 @@ import { i18n } from '@kbn/i18n'; import { get } from 'lodash'; import { ShowDebugging } from './show_debugging'; +export interface Props { + payload: { + error: Error; + }; +} + const strings = { getDescription: () => i18n.translate('expressionError.errorComponent.description', { @@ -23,12 +29,6 @@ const strings = { }), }; -export interface Props { - payload: { - error: Error; - }; -} - export const Error: FC = ({ payload }) => { const message = get(payload, 'error.message'); diff --git a/src/plugins/expression_repeat_image/.storybook/main.js b/src/plugins/expression_repeat_image/.storybook/main.js new file mode 100644 index 000000000000..742239e638b8 --- /dev/null +++ b/src/plugins/expression_repeat_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_repeat_image/README.md b/src/plugins/expression_repeat_image/README.md new file mode 100755 index 000000000000..11f4f9847c39 --- /dev/null +++ b/src/plugins/expression_repeat_image/README.md @@ -0,0 +1,9 @@ +# 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. + +--- + +## 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_repeat_image/common/constants.ts b/src/plugins/expression_repeat_image/common/constants.ts new file mode 100644 index 000000000000..878d5da74256 --- /dev/null +++ b/src/plugins/expression_repeat_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 = 'expressionRepeatImage'; +export const PLUGIN_NAME = 'expressionRepeatImage'; + +export const CONTEXT = '_context_'; +export const BASE64 = '`base64`'; +export const URL = 'URL'; diff --git a/src/plugins/expression_repeat_image/common/expression_functions/index.ts b/src/plugins/expression_repeat_image/common/expression_functions/index.ts new file mode 100644 index 000000000000..84695c58c7f2 --- /dev/null +++ b/src/plugins/expression_repeat_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 { repeatImageFunction } from './repeat_image_function'; + +export const functions = [repeatImageFunction]; + +export { repeatImageFunction }; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/repeat_image.test.js b/src/plugins/expression_repeat_image/common/expression_functions/repeat_image_function.test.ts similarity index 62% rename from x-pack/plugins/canvas/canvas_plugin_src/functions/common/repeat_image.test.js rename to src/plugins/expression_repeat_image/common/expression_functions/repeat_image_function.test.ts index 42569e26e426..4c7e4771a8e0 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/repeat_image.test.js +++ b/src/plugins/expression_repeat_image/common/expression_functions/repeat_image_function.test.ts @@ -1,29 +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; 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 { ExecutionContext } from 'src/plugins/expressions'; import { getElasticLogo, getElasticOutline, functionWrapper, -} from '../../../../../../src/plugins/presentation_util/common/lib'; -import { repeatImage } from './repeat_image'; +} from '../../../presentation_util/common/lib'; +import { repeatImageFunction } from './repeat_image_function'; describe('repeatImage', () => { - const fn = functionWrapper(repeatImage); + const fn = functionWrapper(repeatImageFunction); - let elasticLogo; - let elasticOutline; + let elasticLogo: string; + let elasticOutline: string; beforeEach(async () => { elasticLogo = await (await getElasticLogo()).elasticLogo; elasticOutline = await (await getElasticOutline()).elasticOutline; }); it('returns a render as repeatImage', async () => { - const result = await fn(10); + const result = await fn(10, {}, {} as ExecutionContext); expect(result).toHaveProperty('type', 'render'); expect(result).toHaveProperty('as', 'repeatImage'); }); @@ -31,46 +33,47 @@ describe('repeatImage', () => { describe('args', () => { describe('image', () => { it('sets the source of the repeated image', async () => { - const result = (await fn(10, { image: elasticLogo })).value; + const result = (await fn(10, { image: elasticLogo }, {} as ExecutionContext)).value; expect(result).toHaveProperty('image', elasticLogo); }); it('defaults to the Elastic outline logo', async () => { - const result = (await fn(100000)).value; + const result = (await fn(100000, {}, {} as ExecutionContext)).value; expect(result).toHaveProperty('image', elasticOutline); }); }); describe('size', () => { it('sets the size of the image', async () => { - const result = (await fn(-5, { size: 200 })).value; + const result = (await fn(-5, { size: 200 }, {} as ExecutionContext)).value; expect(result).toHaveProperty('size', 200); }); it('defaults to 100', async () => { - const result = (await fn(-5)).value; + const result = (await fn(-5, {}, {} as ExecutionContext)).value; expect(result).toHaveProperty('size', 100); }); }); describe('max', () => { it('sets the maximum number of a times the image is repeated', async () => { - const result = (await fn(100000, { max: 20 })).value; + const result = (await fn(100000, { max: 20 }, {} as ExecutionContext)).value; expect(result).toHaveProperty('max', 20); }); it('defaults to 1000', async () => { - const result = (await fn(100000)).value; + const result = (await fn(100000, {}, {} as ExecutionContext)).value; expect(result).toHaveProperty('max', 1000); }); }); describe('emptyImage', () => { it('returns repeatImage object with emptyImage as undefined', async () => { - const result = (await fn(100000, { emptyImage: elasticLogo })).value; + const result = (await fn(100000, { emptyImage: elasticLogo }, {} as ExecutionContext)) + .value; expect(result).toHaveProperty('emptyImage', elasticLogo); }); it('sets emptyImage to null', async () => { - const result = (await fn(100000)).value; + const result = (await fn(100000, {}, {} as ExecutionContext)).value; expect(result).toHaveProperty('emptyImage', null); }); }); diff --git a/src/plugins/expression_repeat_image/common/expression_functions/repeat_image_function.ts b/src/plugins/expression_repeat_image/common/expression_functions/repeat_image_function.ts new file mode 100644 index 000000000000..ebc72ab10af4 --- /dev/null +++ b/src/plugins/expression_repeat_image/common/expression_functions/repeat_image_function.ts @@ -0,0 +1,115 @@ +/* + * 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 { + getElasticOutline, + isValidUrl, + resolveWithMissingImage, +} from '../../../presentation_util/common/lib'; +import { CONTEXT, BASE64, URL } from '../constants'; +import { ExpressionRepeatImageFunction } from '../types'; + +export const strings = { + help: i18n.translate('expressionRepeatImage.functions.repeatImageHelpText', { + defaultMessage: 'Configures a repeating image element.', + }), + args: { + emptyImage: i18n.translate( + 'expressionRepeatImage.functions.repeatImage.args.emptyImageHelpText', + { + defaultMessage: + 'Fills the difference between the {CONTEXT} and {maxArg} parameter for the element with this image. ' + + 'Provide an image asset as a {BASE64} data {URL}, or pass in a sub-expression.', + values: { + BASE64, + CONTEXT, + maxArg: '`max`', + URL, + }, + } + ), + image: i18n.translate('expressionRepeatImage.functions.repeatImage.args.imageHelpText', { + defaultMessage: + 'The image to repeat. Provide an image asset as a {BASE64} data {URL}, or pass in a sub-expression.', + values: { + BASE64, + URL, + }, + }), + max: i18n.translate('expressionRepeatImage.functions.repeatImage.args.maxHelpText', { + defaultMessage: 'The maximum number of times the image can repeat.', + }), + size: i18n.translate('expressionRepeatImage.functions.repeatImage.args.sizeHelpText', { + defaultMessage: + 'The maximum height or width of the image, in pixels. ' + + 'When the image is taller than it is wide, this function limits the height.', + }), + }, +}; + +const errors = { + getMissingMaxArgumentErrorMessage: () => + i18n.translate('expressionRepeatImage.error.repeatImage.missingMaxArgument', { + defaultMessage: '{maxArgument} must be set if providing an {emptyImageArgument}', + values: { + maxArgument: '`max`', + emptyImageArgument: '`emptyImage`', + }, + }), +}; + +export const repeatImageFunction: ExpressionRepeatImageFunction = () => { + const { help, args: argHelp } = strings; + + return { + name: 'repeatImage', + aliases: [], + type: 'render', + inputTypes: ['number'], + help, + args: { + emptyImage: { + types: ['string', 'null'], + help: argHelp.emptyImage, + default: null, + }, + image: { + types: ['string', 'null'], + help: argHelp.image, + default: null, + }, + max: { + types: ['number', 'null'], + help: argHelp.max, + default: 1000, + }, + size: { + types: ['number'], + default: 100, + help: argHelp.size, + }, + }, + fn: async (count, args) => { + if (args.emptyImage !== null && isValidUrl(args.emptyImage) && args.max === null) { + throw new Error(errors.getMissingMaxArgumentErrorMessage()); + } + const { elasticOutline } = await getElasticOutline(); + return { + type: 'render', + as: 'repeatImage', + value: { + count: Math.floor(count), + ...args, + image: resolveWithMissingImage(args.image, elasticOutline), + emptyImage: resolveWithMissingImage(args.emptyImage), + }, + }; + }, + }; +}; diff --git a/src/plugins/expression_repeat_image/common/index.ts b/src/plugins/expression_repeat_image/common/index.ts new file mode 100755 index 000000000000..1b7668c49def --- /dev/null +++ b/src/plugins/expression_repeat_image/common/index.ts @@ -0,0 +1,11 @@ +/* + * 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'; +export * from './expression_functions'; diff --git a/src/plugins/expression_repeat_image/common/types/expression_functions.ts b/src/plugins/expression_repeat_image/common/types/expression_functions.ts new file mode 100644 index 000000000000..3e278ddcc97c --- /dev/null +++ b/src/plugins/expression_repeat_image/common/types/expression_functions.ts @@ -0,0 +1,30 @@ +/* + * 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, ExpressionValueRender } from '../../../expressions'; + +interface Arguments { + image: string | null; + size: number; + max: number | null; + emptyImage: string | null; +} + +export interface Return { + count: number; + image: string; + size: number; + max: number; + emptyImage: string | null; +} + +export type ExpressionRepeatImageFunction = () => ExpressionFunctionDefinition< + 'repeatImage', + number, + Arguments, + Promise> +>; diff --git a/src/plugins/expression_repeat_image/common/types/expression_renderers.ts b/src/plugins/expression_repeat_image/common/types/expression_renderers.ts new file mode 100644 index 000000000000..190abd33f2b1 --- /dev/null +++ b/src/plugins/expression_repeat_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. + */ + +export type OriginString = 'bottom' | 'left' | 'top' | 'right'; +export interface RepeatImageRendererConfig { + max: number; + count: number; + emptyImage: string; + image: string; + size: number; +} + +export interface NodeDimensions { + width: number; + height: number; +} diff --git a/src/plugins/expression_repeat_image/common/types/index.ts b/src/plugins/expression_repeat_image/common/types/index.ts new file mode 100644 index 000000000000..ec934e7affe8 --- /dev/null +++ b/src/plugins/expression_repeat_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_repeat_image/jest.config.js b/src/plugins/expression_repeat_image/jest.config.js new file mode 100644 index 000000000000..cf1039263840 --- /dev/null +++ b/src/plugins/expression_repeat_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_repeat_image'], +}; diff --git a/src/plugins/expression_repeat_image/kibana.json b/src/plugins/expression_repeat_image/kibana.json new file mode 100755 index 000000000000..33f1f9c8b759 --- /dev/null +++ b/src/plugins/expression_repeat_image/kibana.json @@ -0,0 +1,10 @@ +{ + "id": "expressionRepeatImage", + "version": "1.0.0", + "kibanaVersion": "kibana", + "server": true, + "ui": true, + "requiredPlugins": ["expressions", "presentationUtil"], + "optionalPlugins": [], + "requiredBundles": [] +} diff --git a/src/plugins/expression_repeat_image/public/components/index.ts b/src/plugins/expression_repeat_image/public/components/index.ts new file mode 100644 index 000000000000..5d7878fc46b5 --- /dev/null +++ b/src/plugins/expression_repeat_image/public/components/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 './repeat_image_component'; diff --git a/src/plugins/expression_repeat_image/public/components/repeat_image_component.tsx b/src/plugins/expression_repeat_image/public/components/repeat_image_component.tsx new file mode 100644 index 000000000000..7a136b470e94 --- /dev/null +++ b/src/plugins/expression_repeat_image/public/components/repeat_image_component.tsx @@ -0,0 +1,99 @@ +/* + * 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, { ReactElement, useEffect, useState } from 'react'; +import { times } from 'lodash'; +import { IInterpreterRenderHandlers } from 'src/plugins/expressions'; +import { RepeatImageRendererConfig } from '../../common'; + +interface RepeatImageComponentProps extends RepeatImageRendererConfig { + onLoaded: IInterpreterRenderHandlers['done']; + parentNode: HTMLElement; +} + +interface LoadedImages { + image: HTMLImageElement | null; + emptyImage: HTMLImageElement | null; +} + +async function loadImage(src: string): Promise { + return new Promise((resolve, reject) => { + const img = new Image(); + img.onload = () => resolve(img); + img.onerror = (error) => reject(error); + img.src = src; + }); +} + +async function loadImages(images: string[]): Promise> { + const results = await Promise.allSettled([...images.map(loadImage)]); + return results.map((loadedImage) => + loadedImage.status === 'rejected' ? null : loadedImage.value + ); +} + +function setImageSize(img: HTMLImageElement, size: number) { + if (img.naturalHeight > img.naturalWidth) { + img.height = size; + } else { + img.width = size; + } +} + +function createImageJSX(img: HTMLImageElement | null) { + if (!img) return null; + const params = img.width > img.height ? { heigth: img.height } : { width: img.width }; + return ; +} + +function RepeatImageComponent({ + max, + count, + emptyImage: emptyImageSrc, + image: imageSrc, + size, + onLoaded, +}: RepeatImageComponentProps) { + const [images, setImages] = useState({ + image: null, + emptyImage: null, + }); + + useEffect(() => { + loadImages([imageSrc, emptyImageSrc]).then((result) => { + const [image, emptyImage] = result; + setImages({ image, emptyImage }); + onLoaded(); + }); + }, [imageSrc, emptyImageSrc, onLoaded]); + + const imagesToRender: Array = []; + + const { image, emptyImage } = images; + + if (max && count > max) count = max; + + if (image) { + setImageSize(image, size); + times(count, () => imagesToRender.push(createImageJSX(image))); + } + + if (emptyImage) { + setImageSize(emptyImage, size); + times(max - count, () => imagesToRender.push(createImageJSX(emptyImage))); + } + + return ( +
+ {imagesToRender} +
+ ); +} +// default export required for React.Lazy +// eslint-disable-next-line import/no-default-export +export { RepeatImageComponent as default }; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/__snapshots__/repeat_image.stories.storyshot b/src/plugins/expression_repeat_image/public/expression_renderers/__stories__/__snapshots__/repeat_image.stories.storyshot similarity index 100% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/__snapshots__/repeat_image.stories.storyshot rename to src/plugins/expression_repeat_image/public/expression_renderers/__stories__/__snapshots__/repeat_image.stories.storyshot diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/repeat_image.stories.tsx b/src/plugins/expression_repeat_image/public/expression_renderers/__stories__/repeat_image_renderer.stories.tsx similarity index 69% rename from x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/repeat_image.stories.tsx rename to src/plugins/expression_repeat_image/public/expression_renderers/__stories__/repeat_image_renderer.stories.tsx index 0052b9139aae..42f008b2570e 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/__stories__/repeat_image.stories.tsx +++ b/src/plugins/expression_repeat_image/public/expression_renderers/__stories__/repeat_image_renderer.stories.tsx @@ -1,19 +1,20 @@ /* * 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 React from 'react'; import { storiesOf } from '@storybook/react'; -import { repeatImage } from '../repeat_image'; +import { Render } from '../../../../presentation_util/public/__stories__'; +import { repeatImageRenderer } from '../repeat_image_renderer'; import { getElasticLogo, getElasticOutline, } from '../../../../../../src/plugins/presentation_util/common/lib'; import { waitFor } from '../../../../../../src/plugins/presentation_util/public/__stories__'; -import { Render } from './render'; const Renderer = ({ elasticLogo, @@ -30,7 +31,7 @@ const Renderer = ({ emptyImage: elasticOutline, }; - return ; + return ; }; storiesOf('enderers/repeatImage', module).add( diff --git a/src/plugins/expression_repeat_image/public/expression_renderers/index.ts b/src/plugins/expression_repeat_image/public/expression_renderers/index.ts new file mode 100644 index 000000000000..5c5625f8c773 --- /dev/null +++ b/src/plugins/expression_repeat_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 { repeatImageRenderer } from './repeat_image_renderer'; + +export const renderers = [repeatImageRenderer]; + +export { repeatImageRenderer }; diff --git a/src/plugins/expression_repeat_image/public/expression_renderers/repeat_image_renderer.tsx b/src/plugins/expression_repeat_image/public/expression_renderers/repeat_image_renderer.tsx new file mode 100644 index 000000000000..bd35de79713c --- /dev/null +++ b/src/plugins/expression_repeat_image/public/expression_renderers/repeat_image_renderer.tsx @@ -0,0 +1,58 @@ +/* + * 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, { lazy } from 'react'; +import { render, unmountComponentAtNode } from 'react-dom'; +import { I18nProvider } from '@kbn/i18n/react'; +import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions'; +import { i18n } from '@kbn/i18n'; +import { getElasticOutline, isValidUrl, withSuspense } from '../../../presentation_util/public'; +import { RepeatImageRendererConfig } from '../../common/types'; + +const strings = { + getDisplayName: () => + i18n.translate('expressionRepeatImage.renderer.repeatImage.displayName', { + defaultMessage: 'RepeatImage', + }), + getHelpDescription: () => + i18n.translate('expressionRepeatImage.renderer.repeatImage.helpDescription', { + defaultMessage: 'Render a basic repeatImage', + }), +}; + +const LazyRepeatImageComponent = lazy(() => import('../components/repeat_image_component')); +const RepeatImageComponent = withSuspense(LazyRepeatImageComponent, null); + +export const repeatImageRenderer = (): ExpressionRenderDefinition => ({ + name: 'repeatImage', + displayName: strings.getDisplayName(), + help: strings.getHelpDescription(), + reuseDomNode: true, + render: async ( + domNode: HTMLElement, + config: RepeatImageRendererConfig, + handlers: IInterpreterRenderHandlers + ) => { + const { elasticOutline } = await getElasticOutline(); + const settings = { + ...config, + image: isValidUrl(config.image) ? config.image : elasticOutline, + emptyImage: config.emptyImage || '', + }; + + handlers.onDestroy(() => { + unmountComponentAtNode(domNode); + }); + + render( + + + , + domNode + ); + }, +}); diff --git a/src/plugins/expression_repeat_image/public/index.ts b/src/plugins/expression_repeat_image/public/index.ts new file mode 100755 index 000000000000..6e1677525645 --- /dev/null +++ b/src/plugins/expression_repeat_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 { ExpressionRepeatImagePlugin } from './plugin'; + +export type { ExpressionRepeatImagePluginSetup, ExpressionRepeatImagePluginStart } from './plugin'; + +export function plugin() { + return new ExpressionRepeatImagePlugin(); +} + +export * from './expression_renderers'; diff --git a/src/plugins/expression_repeat_image/public/plugin.ts b/src/plugins/expression_repeat_image/public/plugin.ts new file mode 100755 index 000000000000..aba8fff219c4 --- /dev/null +++ b/src/plugins/expression_repeat_image/public/plugin.ts @@ -0,0 +1,41 @@ +/* + * 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 { repeatImageFunction } from '../common/expression_functions'; +import { repeatImageRenderer } from './expression_renderers'; + +interface SetupDeps { + expressions: ExpressionsSetup; +} + +interface StartDeps { + expression: ExpressionsStart; +} + +export type ExpressionRepeatImagePluginSetup = void; +export type ExpressionRepeatImagePluginStart = void; + +export class ExpressionRepeatImagePlugin + implements + Plugin< + ExpressionRepeatImagePluginSetup, + ExpressionRepeatImagePluginStart, + SetupDeps, + StartDeps + > { + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionRepeatImagePluginSetup { + expressions.registerFunction(repeatImageFunction); + expressions.registerRenderer(repeatImageRenderer); + } + + public start(core: CoreStart): ExpressionRepeatImagePluginStart {} + + public stop() {} +} diff --git a/src/plugins/expression_repeat_image/server/index.ts b/src/plugins/expression_repeat_image/server/index.ts new file mode 100755 index 000000000000..07d0df9f78e0 --- /dev/null +++ b/src/plugins/expression_repeat_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 { ExpressionRepeatImagePlugin } from './plugin'; + +export type { ExpressionRepeatImagePluginSetup, ExpressionRepeatImagePluginStart } from './plugin'; + +export function plugin() { + return new ExpressionRepeatImagePlugin(); +} diff --git a/src/plugins/expression_repeat_image/server/plugin.ts b/src/plugins/expression_repeat_image/server/plugin.ts new file mode 100755 index 000000000000..744a3fb7f35b --- /dev/null +++ b/src/plugins/expression_repeat_image/server/plugin.ts @@ -0,0 +1,39 @@ +/* + * 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 { repeatImageFunction } from '../common'; + +interface SetupDeps { + expressions: ExpressionsServerSetup; +} + +interface StartDeps { + expression: ExpressionsServerStart; +} + +export type ExpressionRepeatImagePluginSetup = void; +export type ExpressionRepeatImagePluginStart = void; + +export class ExpressionRepeatImagePlugin + implements + Plugin< + ExpressionRepeatImagePluginSetup, + ExpressionRepeatImagePluginStart, + SetupDeps, + StartDeps + > { + public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionRepeatImagePluginSetup { + expressions.registerFunction(repeatImageFunction); + } + + public start(core: CoreStart): ExpressionRepeatImagePluginStart {} + + public stop() {} +} diff --git a/src/plugins/expression_repeat_image/tsconfig.json b/src/plugins/expression_repeat_image/tsconfig.json new file mode 100644 index 000000000000..aa4562ec7357 --- /dev/null +++ b/src/plugins/expression_repeat_image/tsconfig.json @@ -0,0 +1,21 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "composite": true, + "outDir": "./target/types", + "emitDeclarationOnly": true, + "declaration": true, + "declarationMap": true, + "isolatedModules": true + }, + "include": [ + "common/**/*", + "public/**/*", + "server/**/*", + ], + "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/index.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/index.ts index 5e7f837d9c68..6ab7abac985c 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 @@ -42,7 +42,6 @@ import { render } from './render'; import { replace } from './replace'; import { rounddate } from './rounddate'; import { rowCount } from './rowCount'; -import { repeatImage } from './repeat_image'; import { seriesStyle } from './seriesStyle'; import { sort } from './sort'; import { staticColumn } from './staticColumn'; @@ -90,7 +89,6 @@ export const functions = [ ply, progress, render, - repeatImage, replace, rounddate, rowCount, diff --git a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/repeat_image.ts b/x-pack/plugins/canvas/canvas_plugin_src/functions/common/repeat_image.ts deleted file mode 100644 index 751573e27183..000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/functions/common/repeat_image.ts +++ /dev/null @@ -1,84 +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 { - getElasticOutline, - resolveWithMissingImage, -} from '../../../../../../src/plugins/presentation_util/common/lib'; -import { Render } from '../../../types'; -import { getFunctionHelp } from '../../../i18n'; - -interface Arguments { - image: string | null; - size: number; - max: number; - emptyImage: string | null; -} - -export interface Return { - count: number; - image: string; - size: number; - max: number; - emptyImage: string | null; -} - -export function repeatImage(): ExpressionFunctionDefinition< - 'repeatImage', - number, - Arguments, - Promise> -> { - const { help, args: argHelp } = getFunctionHelp().repeatImage; - return { - name: 'repeatImage', - aliases: [], - type: 'render', - inputTypes: ['number'], - help, - args: { - emptyImage: { - types: ['string', 'null'], - help: argHelp.emptyImage, - default: null, - }, - image: { - types: ['string', 'null'], - help: argHelp.image, - default: null, - }, - max: { - types: ['number'], - help: argHelp.max, - default: 1000, - }, - size: { - types: ['number'], - default: 100, - help: argHelp.size, - }, - }, - fn: async (count, args) => { - const { elasticOutline } = await getElasticOutline(); - if (args.image === null) { - args.image = elasticOutline; - } - - return { - type: 'render', - as: 'repeatImage', - value: { - count: Math.floor(count), - ...args, - image: resolveWithMissingImage(args.image, elasticOutline), - emptyImage: resolveWithMissingImage(args.emptyImage), - }, - }; - }, - }; -} 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 d04e342ccb9e..8eabae4c661d 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/core.ts @@ -11,20 +11,9 @@ import { metric } from './metric'; import { pie } from './pie'; import { plot } from './plot'; import { progress } from './progress'; -import { repeatImage } from './repeat_image'; -import { table } from './table'; import { text } from './text'; +import { table } from './table'; -export const renderFunctions = [ - image, - markdown, - metric, - pie, - plot, - progress, - repeatImage, - table, - text, -]; +export const renderFunctions = [image, 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 f24fad04fab5..0c824fb3dd25 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts +++ b/x-pack/plugins/canvas/canvas_plugin_src/renderers/external.ts @@ -5,9 +5,17 @@ * 2.0. */ -import { revealImageRenderer } from '../../../../../src/plugins/expression_reveal_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'; import { shapeRenderer } from '../../../../../src/plugins/expression_shape/public'; -export const renderFunctions = [revealImageRenderer, debugRenderer, errorRenderer, shapeRenderer]; +export const renderFunctions = [ + revealImageRenderer, + debugRenderer, + errorRenderer, + shapeRenderer, + repeatImageRenderer, +]; + export const renderFunctionFactories = []; diff --git a/x-pack/plugins/canvas/canvas_plugin_src/renderers/repeat_image.ts b/x-pack/plugins/canvas/canvas_plugin_src/renderers/repeat_image.ts deleted file mode 100644 index b7a94c2089d8..000000000000 --- a/x-pack/plugins/canvas/canvas_plugin_src/renderers/repeat_image.ts +++ /dev/null @@ -1,79 +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 $ from 'jquery'; -import { times } from 'lodash'; -import { - getElasticOutline, - isValidUrl, -} from '../../../../../src/plugins/presentation_util/common/lib'; -import { RendererStrings, ErrorStrings } from '../../i18n'; -import { Return as Arguments } from '../functions/common/repeat_image'; -import { RendererFactory } from '../../types'; - -const { repeatImage: strings } = RendererStrings; -const { RepeatImage: errors } = ErrorStrings; - -export const repeatImage: RendererFactory = () => ({ - name: 'repeatImage', - displayName: strings.getDisplayName(), - help: strings.getHelpDescription(), - reuseDomNode: true, - render: async (domNode, config, handlers) => { - let image = config.image; - if (!isValidUrl(config.image)) { - image = (await getElasticOutline()).elasticOutline; - } - const settings = { - ...config, - image, - emptyImage: config.emptyImage || '', - }; - - const container = $('
'); - - function setSize(img: HTMLImageElement) { - if (img.naturalHeight > img.naturalWidth) { - img.height = settings.size; - } else { - img.width = settings.size; - } - } - - function finish() { - $(domNode).append(container); - handlers.done(); - } - - const img = new Image(); - img.onload = function () { - setSize(img); - if (settings.max && settings.count > settings.max) { - settings.count = settings.max; - } - times(settings.count, () => container.append($(img).clone())); - - if (isValidUrl(settings.emptyImage)) { - if (settings.max == null) { - throw new Error(errors.getMissingMaxArgumentErrorMessage()); - } - - const emptyImage = new Image(); - emptyImage.onload = function () { - setSize(emptyImage); - times(settings.max - settings.count, () => container.append($(emptyImage).clone())); - finish(); - }; - emptyImage.src = settings.emptyImage; - } else { - finish(); - } - }; - - img.src = settings.image; - }, -}); diff --git a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js index 9372e0035bd1..72fd42a1ff99 100644 --- a/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js +++ b/x-pack/plugins/canvas/canvas_plugin_src/uis/arguments/shape.js @@ -14,18 +14,20 @@ import { ArgumentStrings } from '../../../i18n'; const { Shape: strings } = ArgumentStrings; -const ShapeArgInput = ({ onValueChange, argValue, typeInstance }) => ( - - - - - -); +const ShapeArgInput = ({ onValueChange, argValue, typeInstance }) => { + return ( + + + + + + ); +}; ShapeArgInput.propTypes = { argValue: PropTypes.any.isRequired, diff --git a/x-pack/plugins/canvas/i18n/errors.ts b/x-pack/plugins/canvas/i18n/errors.ts index 8b6697e78ca3..bf1d08f7f1de 100644 --- a/x-pack/plugins/canvas/i18n/errors.ts +++ b/x-pack/plugins/canvas/i18n/errors.ts @@ -58,16 +58,6 @@ export const ErrorStrings = { }, }), }, - RepeatImage: { - getMissingMaxArgumentErrorMessage: () => - i18n.translate('xpack.canvas.error.repeatImage.missingMaxArgument', { - defaultMessage: '{maxArgument} must be set if providing an {emptyImageArgument}', - values: { - maxArgument: '`max`', - emptyImageArgument: '`emptyImage`', - }, - }), - }, WorkpadDropzone: { getTooManyFilesErrorMessage: () => i18n.translate('xpack.canvas.error.workpadDropzone.tooManyFilesErrorMessage', { diff --git a/x-pack/plugins/canvas/i18n/functions/dict/repeat_image.ts b/x-pack/plugins/canvas/i18n/functions/dict/repeat_image.ts deleted file mode 100644 index 8f557229f5be..000000000000 --- a/x-pack/plugins/canvas/i18n/functions/dict/repeat_image.ts +++ /dev/null @@ -1,47 +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 { repeatImage } from '../../../canvas_plugin_src/functions/common/repeat_image'; -import { FunctionHelp } from '../function_help'; -import { FunctionFactory } from '../../../types'; -import { CONTEXT, BASE64, URL } from '../../constants'; - -export const help: FunctionHelp> = { - help: i18n.translate('xpack.canvas.functions.repeatImageHelpText', { - defaultMessage: 'Configures a repeating image element.', - }), - args: { - emptyImage: i18n.translate('xpack.canvas.functions.repeatImage.args.emptyImageHelpText', { - defaultMessage: - 'Fills the difference between the {CONTEXT} and {maxArg} parameter for the element with this image. ' + - 'Provide an image asset as a {BASE64} data {URL}, or pass in a sub-expression.', - values: { - BASE64, - CONTEXT, - maxArg: '`max`', - URL, - }, - }), - image: i18n.translate('xpack.canvas.functions.repeatImage.args.imageHelpText', { - defaultMessage: - 'The image to repeat. Provide an image asset as a {BASE64} data {URL}, or pass in a sub-expression.', - values: { - BASE64, - URL, - }, - }), - max: i18n.translate('xpack.canvas.functions.repeatImage.args.maxHelpText', { - defaultMessage: 'The maximum number of times the image can repeat.', - }), - size: i18n.translate('xpack.canvas.functions.repeatImage.args.sizeHelpText', { - defaultMessage: - 'The maximum height or width of the image, in pixels. ' + - 'When the image is taller than it is wide, this function limits the height.', - }), - }, -}; diff --git a/x-pack/plugins/canvas/i18n/functions/function_help.ts b/x-pack/plugins/canvas/i18n/functions/function_help.ts index d5b0d514f762..85063b9dbfd6 100644 --- a/x-pack/plugins/canvas/i18n/functions/function_help.ts +++ b/x-pack/plugins/canvas/i18n/functions/function_help.ts @@ -55,7 +55,6 @@ import { help as ply } from './dict/ply'; import { help as pointseries } from './dict/pointseries'; import { help as progress } from './dict/progress'; import { help as render } from './dict/render'; -import { help as repeatImage } from './dict/repeat_image'; import { help as replace } from './dict/replace'; import { help as rounddate } from './dict/rounddate'; import { help as rowCount } from './dict/row_count'; @@ -214,7 +213,6 @@ export const getFunctionHelp = (): FunctionHelpDict => ({ pointseries, progress, render, - repeatImage, replace, rounddate, rowCount, diff --git a/x-pack/plugins/canvas/i18n/renderers.ts b/x-pack/plugins/canvas/i18n/renderers.ts index fcdb3382af4e..80f1a5aecc89 100644 --- a/x-pack/plugins/canvas/i18n/renderers.ts +++ b/x-pack/plugins/canvas/i18n/renderers.ts @@ -119,16 +119,6 @@ export const RendererStrings = { defaultMessage: 'Render a progress indicator that reveals a percentage of an element', }), }, - repeatImage: { - getDisplayName: () => - i18n.translate('xpack.canvas.renderer.repeatImage.displayName', { - defaultMessage: 'Image repeat', - }), - getHelpDescription: () => - i18n.translate('xpack.canvas.renderer.repeatImage.helpDescription', { - defaultMessage: 'Repeat an image a given number of times', - }), - }, table: { getDisplayName: () => i18n.translate('xpack.canvas.renderer.table.displayName', { diff --git a/x-pack/plugins/canvas/kibana.json b/x-pack/plugins/canvas/kibana.json index a98fc3d210c1..1692d90884a6 100644 --- a/x-pack/plugins/canvas/kibana.json +++ b/x-pack/plugins/canvas/kibana.json @@ -11,9 +11,10 @@ "data", "embeddable", "expressionError", + "expressionRepeatImage", "expressionRevealImage", - "expressions", "expressionShape", + "expressions", "features", "inspector", "presentationUtil", diff --git a/x-pack/plugins/canvas/public/components/datasource/datasource_preview/datasource_preview.js b/x-pack/plugins/canvas/public/components/datasource/datasource_preview/datasource_preview.js index a45613f4bc96..be8e9f673090 100644 --- a/x-pack/plugins/canvas/public/components/datasource/datasource_preview/datasource_preview.js +++ b/x-pack/plugins/canvas/public/components/datasource/datasource_preview/datasource_preview.js @@ -7,6 +7,7 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { i18n } from '@kbn/i18n'; import { EuiModal, EuiModalBody, @@ -18,8 +19,6 @@ import { EuiSpacer, } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { i18n } from '@kbn/i18n'; - import { withSuspense } from '../../../../../../../src/plugins/presentation_util/public'; import { LazyErrorComponent } from '../../../../../../../src/plugins/expression_error/public'; import { Datatable } from '../../datatable'; diff --git a/x-pack/plugins/canvas/public/components/render_with_fn/render_with_fn.tsx b/x-pack/plugins/canvas/public/components/render_with_fn/render_with_fn.tsx index f267b48028f7..1f366468a833 100644 --- a/x-pack/plugins/canvas/public/components/render_with_fn/render_with_fn.tsx +++ b/x-pack/plugins/canvas/public/components/render_with_fn/render_with_fn.tsx @@ -84,12 +84,12 @@ export const RenderWithFn: FC = ({ [] ); - const render = useCallback(() => { + const render = useCallback(async () => { if (!isEqual(handlers.current, incomingHandlers)) { handlers.current = incomingHandlers; } - renderFn(renderTarget.current!, config, handlers.current); + await renderFn(renderTarget.current!, config, handlers.current); }, [renderTarget, config, renderFn, incomingHandlers]); useEffect(() => { @@ -101,12 +101,13 @@ export const RenderWithFn: FC = ({ resetRenderTarget(); } - try { - render(); - firstRender.current = false; - } catch (err: any) { - onError(err, { title: strings.getRenderErrorMessage(functionName) }); - } + render() + .then(() => { + firstRender.current = false; + }) + .catch((err) => { + onError(err, { title: strings.getRenderErrorMessage(functionName) }); + }); }, [domNode, functionName, onError, render, resetRenderTarget, reuseNode]); return ( diff --git a/x-pack/plugins/canvas/public/components/shape_picker/shape_picker.tsx b/x-pack/plugins/canvas/public/components/shape_picker/shape_picker.tsx index e06a199f85fe..0470699943bf 100644 --- a/x-pack/plugins/canvas/public/components/shape_picker/shape_picker.tsx +++ b/x-pack/plugins/canvas/public/components/shape_picker/shape_picker.tsx @@ -18,7 +18,7 @@ interface Props { export const ShapePicker: FC = ({ shapes, onChange = () => {} }) => ( - {shapes.sort().map((shapeKey) => ( + {shapes.sort().map((shapeKey: string) => ( onChange(shapeKey)}> diff --git a/x-pack/plugins/canvas/public/components/shape_preview/shape_preview.tsx b/x-pack/plugins/canvas/public/components/shape_preview/shape_preview.tsx index 22d0d8cca84e..48a6874eace0 100644 --- a/x-pack/plugins/canvas/public/components/shape_preview/shape_preview.tsx +++ b/x-pack/plugins/canvas/public/components/shape_preview/shape_preview.tsx @@ -9,7 +9,6 @@ import React, { FC, RefCallback, useCallback, useState } from 'react'; import PropTypes from 'prop-types'; import { LazyShapeDrawer, - Shape, ShapeDrawerComponentProps, getDefaultShapeData, SvgConfig, @@ -19,7 +18,7 @@ import { import { withSuspense } from '../../../../../../src/plugins/presentation_util/public'; interface Props { - shape?: Shape; + shape?: string; } const ShapeDrawer = withSuspense(LazyShapeDrawer); diff --git a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js index bb5880b7f40a..d5f0a2196814 100644 --- a/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js +++ b/x-pack/plugins/canvas/shareable_runtime/supported_renderers.js @@ -6,7 +6,6 @@ */ import { image } from '../canvas_plugin_src/renderers/image'; -import { repeatImage } from '../canvas_plugin_src/renderers/repeat_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'; @@ -14,11 +13,12 @@ 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 { revealImageRenderer as revealImage } from '../../../../src/plugins/expression_reveal_image/public'; import { errorRenderer as error, debugRenderer as debug, } from '../../../../src/plugins/expression_error/public'; +import { repeatImageRenderer as repeatImage } from '../../../../src/plugins/expression_repeat_image/public'; +import { revealImageRenderer as revealImage } from '../../../../src/plugins/expression_reveal_image/public'; import { shapeRenderer as shape } from '../../../../src/plugins/expression_shape/public'; /** diff --git a/x-pack/plugins/canvas/tsconfig.json b/x-pack/plugins/canvas/tsconfig.json index 8c97a78da7f0..6181df5abe46 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_repeat_image/tsconfig.json" }, { "path": "../../../src/plugins/expression_reveal_image/tsconfig.json" }, { "path": "../../../src/plugins/expression_shape/tsconfig.json" }, { "path": "../../../src/plugins/home/tsconfig.json" }, diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 8648c28b1553..c473471b33a3 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -6061,7 +6061,7 @@ "xpack.canvas.error.esService.fieldsFetchErrorMessage": "「{index}」の Elasticsearch フィールドを取得できませんでした", "xpack.canvas.error.esService.indicesFetchErrorMessage": "Elasticsearch インデックスを取得できませんでした", "xpack.canvas.error.RenderWithFn.renderErrorMessage": "「{functionName}」のレンダリングが失敗しました", - "xpack.canvas.error.repeatImage.missingMaxArgument": "{emptyImageArgument} を指定する場合は、{maxArgument} を設定する必要があります", + "expressionRepeatImage.error.repeatImage.missingMaxArgument": "{emptyImageArgument} を指定する場合は、{maxArgument} を設定する必要があります", "xpack.canvas.error.useCloneWorkpad.cloneFailureErrorMessage": "ワークパッドのクローンを作成できませんでした", "xpack.canvas.error.useCreateWorkpad.uploadFailureErrorMessage": "ワークパッドをアップロードできませんでした", "xpack.canvas.error.useDeleteWorkpads.deleteFailureErrorMessage": "すべてのワークパッドを削除できませんでした", @@ -6304,11 +6304,11 @@ "xpack.canvas.functions.render.args.containerStyleHelpText": "背景、境界、透明度を含む、コンテナーのスタイルです。", "xpack.canvas.functions.render.args.cssHelpText": "このエレメントの対象となるカスタム {CSS} のブロックです。", "xpack.canvas.functions.renderHelpText": "{CONTEXT}を特定のエレメントとしてレンダリングし、背景と境界のスタイルなどのエレメントレベルのオプションを設定します。", - "xpack.canvas.functions.repeatImage.args.emptyImageHelpText": "この画像のエレメントについて、{CONTEXT}および{maxArg}パラメーターの差異を解消します。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。", - "xpack.canvas.functions.repeatImage.args.imageHelpText": "繰り返す画像です。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。", - "xpack.canvas.functions.repeatImage.args.maxHelpText": "画像が繰り返される最高回数です。", - "xpack.canvas.functions.repeatImage.args.sizeHelpText": "画像の高さまたは幅のピクセル単位での最高値です。画像が縦長の場合、この関数は高さを制限します。", - "xpack.canvas.functions.repeatImageHelpText": "繰り返し画像エレメントを構成します。", + "expressionRepeatImage.functions.repeatImage.args.emptyImageHelpText": "この画像のエレメントについて、{CONTEXT}および{maxArg}パラメーターの差異を解消します。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。", + "expressionRepeatImage.functions.repeatImage.args.imageHelpText": "繰り返す画像です。画像アセットは{BASE64}データ{URL}として提供するか、部分式で渡します。", + "expressionRepeatImage.functions.repeatImage.args.maxHelpText": "画像が繰り返される最高回数です。", + "expressionRepeatImage.functions.repeatImage.args.sizeHelpText": "画像の高さまたは幅のピクセル単位での最高値です。画像が縦長の場合、この関数は高さを制限します。", + "expressionRepeatImage.functions.repeatImageHelpText": "繰り返し画像エレメントを構成します。", "xpack.canvas.functions.replace.args.flagsHelpText": "フラグを指定します。{url}を参照してください。", "xpack.canvas.functions.replace.args.patternHelpText": "{JS} 正規表現のテキストまたはパターンです。例:{example}。ここではキャプチャグループを使用できます。", "xpack.canvas.functions.replace.args.replacementHelpText": "文字列の一致する部分の代わりです。キャプチャグループはノードによってアクセス可能です。例:{example}。", @@ -6505,8 +6505,8 @@ "xpack.canvas.renderer.plot.helpDescription": "データから XY プロットをレンダリングします", "xpack.canvas.renderer.progress.displayName": "進捗インジケーター", "xpack.canvas.renderer.progress.helpDescription": "エレメントのパーセンテージを示す進捗インジケーターをレンダリングします", - "xpack.canvas.renderer.repeatImage.displayName": "画像の繰り返し", - "xpack.canvas.renderer.repeatImage.helpDescription": "画像を指定回数繰り返し表示します", + "expressionRepeatImage.renderer.repeatImage.displayName": "画像の繰り返し", + "expressionRepeatImage.renderer.repeatImage.helpDescription": "画像を指定回数繰り返し表示します", "expressionShape.renderer.shape.displayName": "形状", "expressionShape.renderer.shape.helpDescription": "基本的な図形をレンダリングします", "xpack.canvas.renderer.table.displayName": "データテーブル", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index a0244e296b23..2546865b743f 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -6097,7 +6097,7 @@ "xpack.canvas.error.esService.fieldsFetchErrorMessage": "无法为“{index}”提取 Elasticsearch 字段", "xpack.canvas.error.esService.indicesFetchErrorMessage": "无法提取 Elasticsearch 索引", "xpack.canvas.error.RenderWithFn.renderErrorMessage": "呈现“{functionName}”失败。", - "xpack.canvas.error.repeatImage.missingMaxArgument": "如果提供 {emptyImageArgument},则必须设置 {maxArgument}", + "expressionRepeatImage.error.repeatImage.missingMaxArgument": "如果提供 {emptyImageArgument},则必须设置 {maxArgument}", "xpack.canvas.error.useCloneWorkpad.cloneFailureErrorMessage": "无法克隆 Workpad", "xpack.canvas.error.useCreateWorkpad.uploadFailureErrorMessage": "无法上传 Workpad", "xpack.canvas.error.useDeleteWorkpads.deleteFailureErrorMessage": "无法删除所有 Workpad", @@ -6341,11 +6341,11 @@ "xpack.canvas.functions.render.args.containerStyleHelpText": "容器的样式,包括背景、边框和透明度。", "xpack.canvas.functions.render.args.cssHelpText": "要限定于元素的任何定制 {CSS} 块。", "xpack.canvas.functions.renderHelpText": "将 {CONTEXT} 呈现为特定元素,并设置元素级别选项,例如背景和边框样式。", - "xpack.canvas.functions.repeatImage.args.emptyImageHelpText": "使用此图像填充元素的 {CONTEXT} 和 {maxArg} 参数之间的差距。以 {BASE64} 数据 {URL} 的形式提供图像资产或传入子表达式。", - "xpack.canvas.functions.repeatImage.args.imageHelpText": "要重复的图像。以 {BASE64} 数据 {URL} 的形式提供图像资产或传入子表达式。", - "xpack.canvas.functions.repeatImage.args.maxHelpText": "图像可以重复的最大次数。", - "xpack.canvas.functions.repeatImage.args.sizeHelpText": "图像的最大高度或宽度,以像素为单位。图像的高大于宽时,此函数将限制高度。", - "xpack.canvas.functions.repeatImageHelpText": "配置重复图像元素。", + "expressionRepeatImage.functions.repeatImage.args.emptyImageHelpText": "使用此图像填充元素的 {CONTEXT} 和 {maxArg} 参数之间的差距。以 {BASE64} 数据 {URL} 的形式提供图像资产或传入子表达式。", + "expressionRepeatImage.functions.repeatImage.args.imageHelpText": "要重复的图像。以 {BASE64} 数据 {URL} 的形式提供图像资产或传入子表达式。", + "expressionRepeatImage.functions.repeatImage.args.maxHelpText": "图像可以重复的最大次数。", + "expressionRepeatImage.functions.repeatImage.args.sizeHelpText": "图像的最大高度或宽度,以像素为单位。图像的高大于宽时,此函数将限制高度。", + "expressionRepeatImage.functions.repeatImageHelpText": "配置重复图像元素。", "xpack.canvas.functions.replace.args.flagsHelpText": "指定标志。请参见 {url}。", "xpack.canvas.functions.replace.args.patternHelpText": "{JS} 正则表达式的文本或模式。例如,{example}。您可以在此处使用捕获组。", "xpack.canvas.functions.replace.args.replacementHelpText": "字符串匹配部分的替代。捕获组可以通过其索引进行访问。例如,{example}。", @@ -6542,10 +6542,12 @@ "xpack.canvas.renderer.plot.helpDescription": "根据您的数据呈现 XY 坐标图", "xpack.canvas.renderer.progress.displayName": "进度指示", "xpack.canvas.renderer.progress.helpDescription": "呈现显示元素百分比的进度指示", - "xpack.canvas.renderer.repeatImage.displayName": "图像重复", - "xpack.canvas.renderer.repeatImage.helpDescription": "重复图像给定次数", "expressionShape.renderer.shape.displayName": "形状", "expressionShape.renderer.shape.helpDescription": "呈现基本形状", + "expressionRepeatImage.renderer.repeatImage.displayName": "图像重复", + "expressionRepeatImage.renderer.repeatImage.helpDescription": "重复图像给定次数", + "expressionRevealImage.renderer.revealImage.displayName": "图像显示", + "expressionRevealImage.renderer.revealImage.helpDescription": "显示一定百分比的图像,以制作定制的仪表样式图表", "xpack.canvas.renderer.table.displayName": "数据表", "xpack.canvas.renderer.table.helpDescription": "将表格数据呈现为 {HTML}", "xpack.canvas.renderer.text.displayName": "纯文本", @@ -7026,8 +7028,6 @@ "expressionRevealImage.functions.revealImage.args.originHelpText": "要开始图像填充的位置。例如 {list} 或 {end}。", "expressionRevealImage.functions.revealImage.invalidPercentErrorMessage": "无效值:“{percent}”。百分比必须介于 0 和 1 之间", "expressionRevealImage.functions.revealImageHelpText": "配置图像显示元素。", - "expressionRevealImage.renderer.revealImage.displayName": "图像显示", - "expressionRevealImage.renderer.revealImage.helpDescription": "显示一定百分比的图像,以制作定制的仪表样式图表", "xpack.cases.connectors.cases.externalIncidentAdded": " (由 {user} 于 {date}添加) ", "xpack.cases.connectors.cases.externalIncidentCreated": " (由 {user} 于 {date}创建) ", "xpack.cases.connectors.cases.externalIncidentDefault": " (由 {user} 于 {date}创建) ",