Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Canvas] Expression image #104318

Merged
merged 7 commits into from
Jul 26, 2021
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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",
"expressionImage": "src/plugins/expression_image",
"expressionRepeatImage": "src/plugins/expression_repeat_image",
"expressionRevealImage": "src/plugins/expression_reveal_image",
"expressionShape": "src/plugins/expression_shape",
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_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.

Expand Down
1 change: 1 addition & 0 deletions packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,6 @@ pageLoadAssetSize:
cases: 144442
expressionError: 22127
expressionRepeatImage: 22341
expressionImage: 19288
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_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',
Expand Down
10 changes: 10 additions & 0 deletions src/plugins/expression_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_image/README.md
Original file line number Diff line number Diff line change
@@ -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.
9 changes: 9 additions & 0 deletions src/plugins/expression_image/__fixtures__/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 { imageFunction } from '../common/expression_functions';
14 changes: 14 additions & 0 deletions src/plugins/expression_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 = 'expressionImage';
export const PLUGIN_NAME = 'expressionImage';

export const CONTEXT = '_context_';
export const BASE64 = '`base64`';
export const URL = 'URL';
Original file line number Diff line number Diff line change
@@ -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');
});
});
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
};
},
};
};
13 changes: 13 additions & 0 deletions src/plugins/expression_image/common/expression_functions/index.ts
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 { imageFunction } from './image_function';

export const functions = [imageFunction];

export { imageFunction };
10 changes: 10 additions & 0 deletions src/plugins/expression_image/common/index.ts
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.
*/

export * from './constants';
export * from './types';
32 changes: 32 additions & 0 deletions src/plugins/expression_image/common/types/expression_functions.ts
Original file line number Diff line number Diff line change
@@ -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<Return>
>;
21 changes: 21 additions & 0 deletions src/plugins/expression_image/common/types/expression_renderers.ts
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.
*/

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;
}
9 changes: 9 additions & 0 deletions src/plugins/expression_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';
13 changes: 13 additions & 0 deletions src/plugins/expression_image/jest.config.js
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.
*/

module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/src/plugins/expression_image'],
};
9 changes: 9 additions & 0 deletions src/plugins/expression_image/kibana.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"id": "expressionImage",
"version": "1.0.0",
"kibanaVersion": "kibana",
"server": true,
"ui": true,
"requiredPlugins": ["expressions", "presentationUtil"],
"optionalPlugins": []
}
Loading