Skip to content

Commit

Permalink
[Canvas] Expression repeat image (#104255)
Browse files Browse the repository at this point in the history
* Repeat Image plugin added.
  • Loading branch information
Kuznietsov authored Jul 23, 2021
1 parent a8da74d commit 3e4b64b
Show file tree
Hide file tree
Showing 47 changed files with 667 additions and 323 deletions.
1 change: 1 addition & 0 deletions .i18nrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 4 additions & 0 deletions docs/developer/plugin-list.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,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.
Expand Down
3 changes: 2 additions & 1 deletion packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,6 @@ pageLoadAssetSize:
expressionRevealImage: 25675
cases: 144442
expressionError: 22127
userSetup: 18532
expressionRepeatImage: 22341
expressionShape: 30033
userSetup: 18532
1 change: 1 addition & 0 deletions src/dev/storybook/aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
12 changes: 6 additions & 6 deletions src/plugins/expression_error/public/components/error/error.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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', {
Expand All @@ -23,12 +29,6 @@ const strings = {
}),
};

export interface Props {
payload: {
error: Error;
};
}

export const Error: FC<Props> = ({ payload }) => {
const message = get(payload, 'error.message');

Expand Down
10 changes: 10 additions & 0 deletions src/plugins/expression_repeat_image/.storybook/main.js
Original file line number Diff line number Diff line change
@@ -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;
9 changes: 9 additions & 0 deletions src/plugins/expression_repeat_image/README.md
Original file line number Diff line number Diff line change
@@ -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.
14 changes: 14 additions & 0 deletions src/plugins/expression_repeat_image/common/constants.ts
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
@@ -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 };
Original file line number Diff line number Diff line change
@@ -1,76 +1,79 @@
/*
* 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');
});

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);
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -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),
},
};
},
};
};
11 changes: 11 additions & 0 deletions src/plugins/expression_repeat_image/common/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
@@ -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<ExpressionValueRender<Arguments>>
>;
Original file line number Diff line number Diff line change
@@ -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;
}
9 changes: 9 additions & 0 deletions src/plugins/expression_repeat_image/common/types/index.ts
Original file line number Diff line number Diff line change
@@ -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';
Loading

0 comments on commit 3e4b64b

Please sign in to comment.