[Canvas] Expression metric (#104390) (#106808)

* Added `expression_metric` plugin.

* Update src/plugins/expression_metric/README.md

Co-authored-by: Corey Robertson <crob611@gmail.com>

Co-authored-by: Yaroslav Kuznietsov <kuznetsov.yaroslav.yk@gmail.com>
Co-authored-by: Corey Robertson <crob611@gmail.com>
This commit is contained in:
Kibana Machine 2021-07-27 04:24:14 -04:00 committed by GitHub
parent 6661844cc9
commit 4340d8d102
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 697 additions and 333 deletions

View file

@ -19,6 +19,7 @@
"expressions": "src/plugins/expressions",
"expressionError": "src/plugins/expression_error",
"expressionImage": "src/plugins/expression_image",
"expressionMetric": "src/plugins/expression_metric",
"expressionRepeatImage": "src/plugins/expression_repeat_image",
"expressionRevealImage": "src/plugins/expression_reveal_image",
"expressionShape": "src/plugins/expression_shape",

View file

@ -78,6 +78,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a
|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_metric/README.md[expressionMetric]
|Expression Metric plugin adds a metric renderer and function to the expression plugin.
|{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.

View file

@ -115,4 +115,5 @@ pageLoadAssetSize:
expressionError: 22127
expressionRepeatImage: 22341
expressionImage: 19288
expressionMetric: 22238
expressionShape: 30033

View file

@ -19,6 +19,7 @@ export const storybookAliases = {
embeddable: 'src/plugins/embeddable/.storybook',
expression_error: 'src/plugins/expression_error/.storybook',
expression_image: 'src/plugins/expression_image/.storybook',
expression_metric: 'src/plugins/expression_metric/.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',

View file

@ -0,0 +1,12 @@
/*
* 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 '../common/expression_functions';
import { ExpressionFunction } from '../../../../src/plugins/expressions';
export const functionSpecs = [imageFunction].map((fn) => new ExpressionFunction(fn()));

View file

@ -6,4 +6,5 @@
* Side Public License, v 1.
*/
export { functionSpecs } from './function_specs';
export { imageFunction } from '../common/expression_functions';

View file

@ -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;

View file

@ -0,0 +1,9 @@
# expressionMetric
Expression Metric plugin adds a `metric` renderer and function to the expression plugin.
---
## Development
See the [kibana contributing guide](https://github.com/elastic/kibana/blob/master/CONTRIBUTING.md) for instructions setting up your development environment.

View file

@ -0,0 +1,12 @@
/*
* 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 { metricFunction } from '../common/expression_functions';
import { ExpressionFunction } from '../../../../src/plugins/expressions';
export const functionSpecs = [metricFunction].map((fn) => new ExpressionFunction(fn()));

View file

@ -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 './function_specs';

View file

@ -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.
*/
export const PLUGIN_ID = 'expressionMetric';
export const PLUGIN_NAME = 'expressionMetric';
export const FONT_FAMILY = '`font-family`';
export const FONT_WEIGHT = '`font-weight`';
export const CSS = 'CSS';
export const NUMERALJS = 'Numeral pattern';

View file

@ -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 { metricFunction } from './metric_function';
export const functions = [metricFunction];
export { metricFunction };

View file

@ -1,39 +1,44 @@
/*
* 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 { functionWrapper } from '../../../../../../src/plugins/presentation_util/common/lib';
import { fontStyle } from './__fixtures__/test_styles';
import { metric } from './metric';
import { ExecutionContext } from 'src/plugins/expressions';
import { functionWrapper, fontStyle } from '../../../presentation_util/common/lib';
import { metricFunction } from './metric_function';
describe('metric', () => {
const fn = functionWrapper(metric);
const fn = functionWrapper(metricFunction);
it('returns a render as metric', () => {
const result = fn(null);
const result = fn(null, {}, {} as ExecutionContext);
expect(result).toHaveProperty('type', 'render');
expect(result).toHaveProperty('as', 'metric');
});
it('sets the metric to context', () => {
const result = fn('2');
const result = fn('2', {}, {} as ExecutionContext);
expect(result.value).toHaveProperty('metric', '2');
});
it(`defaults metric to '?' when context is missing`, () => {
const result = fn(null);
const result = fn(null, {}, {} as ExecutionContext);
expect(result.value).toHaveProperty('metric', '?');
});
describe('args', () => {
describe('label', () => {
it('sets the label of the metric', () => {
const result = fn(null, {
label: 'My Label',
});
const result = fn(
null,
{
label: 'My Label',
},
{} as ExecutionContext
);
expect(result.value).toHaveProperty('label', 'My Label');
});
@ -41,9 +46,13 @@ describe('metric', () => {
describe('metricFont', () => {
it('sets the font style for the metric', () => {
const result = fn(null, {
metricFont: fontStyle,
});
const result = fn(
null,
{
metricFont: fontStyle,
},
{} as ExecutionContext
);
expect(result.value).toHaveProperty('metricFont', fontStyle);
});
@ -54,9 +63,13 @@ describe('metric', () => {
describe('labelFont', () => {
it('sets the font style for the label', () => {
const result = fn(null, {
labelFont: fontStyle,
});
const result = fn(
null,
{
labelFont: fontStyle,
},
{} as ExecutionContext
);
expect(result.value).toHaveProperty('labelFont', fontStyle);
});
@ -67,9 +80,13 @@ describe('metric', () => {
describe('metricFormat', () => {
it('sets the number format of the metric value', () => {
const result = fn(null, {
metricFormat: '0.0%',
});
const result = fn(
null,
{
metricFormat: '0.0%',
},
{} as ExecutionContext
);
expect(result.value).toHaveProperty('metricFormat', '0.0%');
});

View file

@ -0,0 +1,98 @@
/*
* 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 { openSans } from '../../../expressions/common/fonts';
import { FONT_FAMILY, FONT_WEIGHT, CSS, NUMERALJS } from '../constants';
import { ExpressionMetricFunction } from '../types';
export const strings = {
help: i18n.translate('expressionMetric.functions.metricHelpText', {
defaultMessage: 'Displays a number over a label.',
}),
args: {
label: i18n.translate('expressionMetric.functions.metric.args.labelHelpText', {
defaultMessage: 'The text describing the metric.',
}),
labelFont: i18n.translate('expressionMetric.functions.metric.args.labelFontHelpText', {
defaultMessage:
'The {CSS} font properties for the label. For example, {FONT_FAMILY} or {FONT_WEIGHT}.',
values: {
CSS,
FONT_FAMILY,
FONT_WEIGHT,
},
}),
metricFont: i18n.translate('expressionMetric.functions.metric.args.metricFontHelpText', {
defaultMessage:
'The {CSS} font properties for the metric. For example, {FONT_FAMILY} or {FONT_WEIGHT}.',
values: {
CSS,
FONT_FAMILY,
FONT_WEIGHT,
},
}),
// TODO: Find a way to generate the docs URL here
metricFormat: i18n.translate('expressionMetric.functions.metric.args.metricFormatHelpText', {
defaultMessage: 'A {NUMERALJS} format string. For example, {example1} or {example2}.',
values: {
example1: '`"0.0a"`',
example2: '`"0%"`',
NUMERALJS,
},
}),
},
};
export const metricFunction: ExpressionMetricFunction = () => {
const { help, args: argHelp } = strings;
return {
name: 'metric',
aliases: [],
type: 'render',
inputTypes: ['number', 'string', 'null'],
help,
args: {
label: {
types: ['string'],
aliases: ['_', 'text', 'description'],
help: argHelp.label,
default: '""',
},
labelFont: {
types: ['style'],
help: argHelp.labelFont,
default: `{font size=14 family="${openSans.value}" color="#000000" align=center}`,
},
metricFont: {
types: ['style'],
help: argHelp.metricFont,
default: `{font size=48 family="${openSans.value}" color="#000000" align=center lHeight=48}`,
},
metricFormat: {
types: ['string'],
aliases: ['format'],
help: argHelp.metricFormat,
},
},
fn: (input, { label, labelFont, metricFont, metricFormat }) => {
return {
type: 'render',
as: 'metric',
value: {
metric: input === null ? '?' : input,
label,
labelFont,
metricFont,
metricFormat,
},
};
},
};
};

View file

@ -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';

View file

@ -0,0 +1,24 @@
/*
* 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, Style } from '../../../expressions';
export type Input = number | string | null;
export interface Arguments {
label: string;
metricFont: Style;
metricFormat: string;
labelFont: Style;
}
export type ExpressionMetricFunction = () => ExpressionFunctionDefinition<
'metric',
Input,
Arguments,
ExpressionValueRender<Arguments>
>;

View file

@ -0,0 +1,27 @@
/*
* 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 { Style } from '../../../expressions/common';
export interface MetricRendererConfig {
/** The text to display under the metric */
label: string;
/** Font settings for the label */
labelFont: Style;
/** Value of the metric to display */
metric: string | number | null;
/** Font settings for the metric */
metricFont: Style;
/** NumeralJS format string */
metricFormat: string;
}
export interface NodeDimensions {
width: number;
height: number;
}

View file

@ -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';

View file

@ -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_metric'],
};

View file

@ -0,0 +1,9 @@
{
"id": "expressionMetric",
"version": "1.0.0",
"kibanaVersion": "kibana",
"server": true,
"ui": true,
"requiredPlugins": ["expressions", "presentationUtil"],
"optionalPlugins": []
}

View file

@ -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 './metric_component';

View file

@ -1,8 +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; 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, { FunctionComponent, CSSProperties } from 'react';
@ -21,7 +22,7 @@ interface Props {
metricFormat?: string;
}
export const Metric: FunctionComponent<Props> = ({
const Metric: FunctionComponent<Props> = ({
label,
metric,
labelFont,
@ -39,3 +40,6 @@ export const Metric: FunctionComponent<Props> = ({
)}
</div>
);
// eslint-disable-next-line import/no-default-export
export { Metric as default };

View file

@ -0,0 +1,109 @@
/*
* 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, { CSSProperties } from 'react';
import { storiesOf } from '@storybook/react';
import { Style } from 'src/plugins/expressions';
import { metricRenderer } from '../metric_renderer';
import { Render } from '../../../../presentation_util/public/__stories__';
import { MetricRendererConfig } from '../../../common';
const labelFontSpec: CSSProperties = {
fontFamily: "Baskerville, Georgia, Garamond, 'Times New Roman', Times, serif",
fontWeight: 'normal',
fontStyle: 'italic',
textDecoration: 'none',
textAlign: 'center',
fontSize: '24px',
lineHeight: '1',
color: '#000000',
};
const metricFontSpec: CSSProperties = {
fontFamily:
"Optima, 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, Arial, sans-serif",
fontWeight: 'bold',
fontStyle: 'normal',
textDecoration: 'none',
textAlign: 'center',
fontSize: '48px',
lineHeight: '1',
color: '#b83c6f',
};
storiesOf('renderers/Metric', module)
.add('with null metric', () => {
const config: MetricRendererConfig = {
metric: null,
metricFont: {} as Style,
labelFont: {} as Style,
label: '',
metricFormat: '',
};
return <Render renderer={metricRenderer} config={config} />;
})
.add('with number metric', () => {
const config: MetricRendererConfig = {
metric: '12345.6789',
metricFont: metricFontSpec as Style,
labelFont: {} as Style,
label: '',
metricFormat: '',
};
return <Render renderer={metricRenderer} config={config} />;
})
.add('with string metric', () => {
const config: MetricRendererConfig = {
metric: '$12.34',
metricFont: metricFontSpec as Style,
labelFont: labelFontSpec as Style,
label: '',
metricFormat: '',
};
return <Render renderer={metricRenderer} config={config} />;
})
.add('with label', () => {
const config: MetricRendererConfig = {
metric: '$12.34',
metricFont: metricFontSpec as Style,
labelFont: labelFontSpec as Style,
label: 'Average price',
metricFormat: '',
};
return <Render renderer={metricRenderer} config={config} />;
})
.add('with number metric and a specified format', () => {
const config: MetricRendererConfig = {
metric: '-0.0024',
metricFont: metricFontSpec as Style,
labelFont: labelFontSpec as Style,
label: 'Average price',
metricFormat: '0.00%',
};
return <Render renderer={metricRenderer} config={config} />;
})
.add('with formatted string metric and a specified format', () => {
const config: MetricRendererConfig = {
metric: '$10000000.00',
metricFont: metricFontSpec as Style,
labelFont: labelFontSpec as Style,
label: 'Total Revenue',
metricFormat: '$0a',
};
return <Render renderer={metricRenderer} config={config} />;
})
.add('with invalid metricFont', () => {
const config: MetricRendererConfig = {
metric: '$10000000.00',
metricFont: metricFontSpec as Style,
labelFont: labelFontSpec as Style,
label: 'Total Revenue',
metricFormat: '$0a',
};
return <Render renderer={metricRenderer} config={config} />;
});

View file

@ -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 { metricRenderer } from './metric_renderer';
export const renderers = [metricRenderer];
export { metricRenderer };

View file

@ -0,0 +1,55 @@
/*
* 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, { CSSProperties, lazy } from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { ExpressionRenderDefinition, IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { i18n } from '@kbn/i18n';
import { withSuspense } from '../../../presentation_util/public';
import { MetricRendererConfig } from '../../common/types';
const strings = {
getDisplayName: () =>
i18n.translate('expressionMetric.renderer.metric.displayName', {
defaultMessage: 'Metric',
}),
getHelpDescription: () =>
i18n.translate('expressionMetric.renderer.metric.helpDescription', {
defaultMessage: 'Render a number over a label',
}),
};
const LazyMetricComponent = lazy(() => import('../components/metric_component'));
const MetricComponent = withSuspense(LazyMetricComponent);
export const metricRenderer = (): ExpressionRenderDefinition<MetricRendererConfig> => ({
name: 'metric',
displayName: strings.getDisplayName(),
help: strings.getHelpDescription(),
reuseDomNode: true,
render: async (
domNode: HTMLElement,
config: MetricRendererConfig,
handlers: IInterpreterRenderHandlers
) => {
handlers.onDestroy(() => {
unmountComponentAtNode(domNode);
});
render(
<MetricComponent
label={config.label}
labelFont={config.labelFont ? (config.labelFont.spec as CSSProperties) : {}}
metric={config.metric}
metricFont={config.metricFont ? (config.metricFont.spec as CSSProperties) : {}}
metricFormat={config.metricFormat}
/>,
domNode,
() => handlers.done()
);
},
});

View file

@ -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 { ExpressionMetricPlugin } from './plugin';
export type { ExpressionMetricPluginSetup, ExpressionMetricPluginStart } from './plugin';
export function plugin() {
return new ExpressionMetricPlugin();
}
export * from './expression_renderers';

View file

@ -0,0 +1,36 @@
/*
* 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 { metricFunction } from '../common/expression_functions';
import { metricRenderer } from './expression_renderers';
interface SetupDeps {
expressions: ExpressionsSetup;
}
interface StartDeps {
expression: ExpressionsStart;
}
export type ExpressionMetricPluginSetup = void;
export type ExpressionMetricPluginStart = void;
export class ExpressionMetricPlugin
implements
Plugin<ExpressionMetricPluginSetup, ExpressionMetricPluginStart, SetupDeps, StartDeps> {
public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionMetricPluginSetup {
expressions.registerFunction(metricFunction);
expressions.registerRenderer(metricRenderer);
}
public start(core: CoreStart): ExpressionMetricPluginStart {}
public stop() {}
}

View file

@ -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 { ExpressionMetricPlugin } from './plugin';
export type { ExpressionMetricPluginSetup, ExpressionMetricPluginStart } from './plugin';
export function plugin() {
return new ExpressionMetricPlugin();
}

View file

@ -0,0 +1,34 @@
/*
* 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 { metricFunction } from '../common';
interface SetupDeps {
expressions: ExpressionsServerSetup;
}
interface StartDeps {
expression: ExpressionsServerStart;
}
export type ExpressionMetricPluginSetup = void;
export type ExpressionMetricPluginStart = void;
export class ExpressionMetricPlugin
implements
Plugin<ExpressionMetricPluginSetup, ExpressionMetricPluginStart, SetupDeps, StartDeps> {
public setup(core: CoreSetup, { expressions }: SetupDeps): ExpressionMetricPluginSetup {
expressions.registerFunction(metricFunction);
}
public start(core: CoreStart): ExpressionMetricPluginStart {}
public stop() {}
}

View file

@ -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" },
]
}

View file

@ -7,3 +7,4 @@
*/
export * from './function_wrapper';
export * from './test_styles';

View file

@ -0,0 +1,23 @@
/*
* 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 fontStyle = {
type: 'style',
spec: {
fontFamily: 'Chalkboard, serif',
fontWeight: 'bolder',
fontStyle: 'normal',
textDecoration: 'underline',
color: 'pink',
textAlign: 'center',
fontSize: '14px',
lineHeight: '21px',
},
css:
'font-family:Chalkboard, serif;font-weight:bolder;font-style:normal;text-decoration:underline;color:pink;text-align:center;font-size:14px;line-height:21px',
};

View file

@ -9,8 +9,10 @@ import { functions as browserFns } from '../canvas_plugin_src/functions/browser'
import { ExpressionFunction } from '../../../../src/plugins/expressions';
import { initFunctions } from '../public/functions';
import { functionSpecs as shapeFunctionSpecs } from '../../../../src/plugins/expression_shape/__fixtures__';
import { functionSpecs as imageFunctionSpecs } from '../../../../src/plugins/expression_image/__fixtures__';
import { functionSpecs as metricFunctionSpecs } from '../../../../src/plugins/expression_metric/__fixtures__';
export const functionSpecs = browserFns
.concat(...(initFunctions({} as any) as any))
.map((fn) => new ExpressionFunction(fn()))
.concat(...shapeFunctionSpecs);
.concat(...shapeFunctionSpecs, ...imageFunctionSpecs, ...metricFunctionSpecs);

View file

@ -5,9 +5,11 @@
* 2.0.
*/
import { functionWrapper } from '../../../../../../src/plugins/presentation_util/common/lib';
import {
functionWrapper,
fontStyle,
} from '../../../../../../src/plugins/presentation_util/common/lib';
import { testTable } from '../common/__fixtures__/test_tables';
import { fontStyle } from '../common/__fixtures__/test_styles';
import { markdown } from './markdown';
describe('markdown', () => {

View file

@ -33,7 +33,6 @@ import { joinRows } from './join_rows';
import { lt } from './lt';
import { lte } from './lte';
import { mapCenter } from './map_center';
import { metric } from './metric';
import { neq } from './neq';
import { ply } from './ply';
import { progress } from './progress';
@ -82,7 +81,6 @@ export const functions = [
lte,
joinRows,
mapCenter,
metric,
neq,
ply,
progress,

View file

@ -1,72 +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 { openSans } from '../../../common/lib/fonts';
import { Render, Style, ExpressionFunctionDefinition } from '../../../types';
import { getFunctionHelp } from '../../../i18n';
type Input = number | string | null;
interface Arguments {
label: string;
metricFont: Style;
metricFormat: string;
labelFont: Style;
}
export function metric(): ExpressionFunctionDefinition<
'metric',
Input,
Arguments,
Render<Arguments>
> {
const { help, args: argHelp } = getFunctionHelp().metric;
return {
name: 'metric',
aliases: [],
type: 'render',
inputTypes: ['number', 'string', 'null'],
help,
args: {
label: {
types: ['string'],
aliases: ['_', 'text', 'description'],
help: argHelp.label,
default: '""',
},
labelFont: {
types: ['style'],
help: argHelp.labelFont,
default: `{font size=14 family="${openSans.value}" color="#000000" align=center}`,
},
metricFont: {
types: ['style'],
help: argHelp.metricFont,
default: `{font size=48 family="${openSans.value}" color="#000000" align=center lHeight=48}`,
},
metricFormat: {
types: ['string'],
aliases: ['format'],
help: argHelp.metricFormat,
},
},
fn: (input, { label, labelFont, metricFont, metricFormat }) => {
return {
type: 'render',
as: 'metric',
value: {
metric: input === null ? '?' : input,
label,
labelFont,
metricFont,
metricFormat,
},
};
},
};
}

View file

@ -6,10 +6,12 @@
*/
import expect from '@kbn/expect';
import { functionWrapper } from '../../../../../../src/plugins/presentation_util/common/lib';
import {
functionWrapper,
fontStyle,
} from '../../../../../../src/plugins/presentation_util/common/lib';
import { getFunctionErrors } from '../../../i18n';
import { progress } from './progress';
import { fontStyle } from './__fixtures__/test_styles';
const errors = getFunctionErrors().progress;

View file

@ -5,9 +5,11 @@
* 2.0.
*/
import { functionWrapper } from '../../../../../../src/plugins/presentation_util/common/lib';
import {
functionWrapper,
fontStyle,
} from '../../../../../../src/plugins/presentation_util/common/lib';
import { testTable } from './__fixtures__/test_tables';
import { fontStyle } from './__fixtures__/test_styles';
import { table } from './table';
describe('table', () => {

View file

@ -6,13 +6,12 @@
*/
import { markdown } from './markdown';
import { metric } from './metric';
import { pie } from './pie';
import { plot } from './plot';
import { progress } from './progress';
import { text } from './text';
import { table } from './table';
export const renderFunctions = [markdown, metric, pie, plot, progress, table, text];
export const renderFunctions = [markdown, pie, plot, progress, table, text];
export const renderFunctionFactories = [];

View file

@ -6,16 +6,18 @@
*/
import { imageRenderer } from '../../../../../src/plugins/expression_image/public';
import { metricRenderer } from '../../../../../src/plugins/expression_metric/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,
imageRenderer,
metricRenderer,
revealImageRenderer,
shapeRenderer,
repeatImageRenderer,
];

View file

@ -1,85 +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 { storiesOf } from '@storybook/react';
import React, { CSSProperties } from 'react';
import { Metric } from '../metric';
const labelFontSpec: CSSProperties = {
fontFamily: "Baskerville, Georgia, Garamond, 'Times New Roman', Times, serif",
fontWeight: 'normal',
fontStyle: 'italic',
textDecoration: 'none',
textAlign: 'center',
fontSize: '24px',
lineHeight: '1',
color: '#000000',
};
const metricFontSpec: CSSProperties = {
fontFamily:
"Optima, 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, Arial, sans-serif",
fontWeight: 'bold',
fontStyle: 'normal',
textDecoration: 'none',
textAlign: 'center',
fontSize: '48px',
lineHeight: '1',
color: '#b83c6f',
};
storiesOf('renderers/Metric', module)
.addDecorator((story) => (
<div
style={{
width: '200px',
}}
>
{story()}
</div>
))
.add('with null metric', () => <Metric metric={null} metricFont={{}} labelFont={{}} />)
.add('with number metric', () => (
<Metric metric="12345.6789" labelFont={{}} metricFont={metricFontSpec} />
))
.add('with string metric', () => (
<Metric metric="$12.34" labelFont={labelFontSpec} metricFont={metricFontSpec} />
))
.add('with label', () => (
<Metric
label="Average price"
metric="$12.34"
labelFont={labelFontSpec}
metricFont={metricFontSpec}
/>
))
.add('with number metric and a specified format', () => (
<Metric
metric="-0.0024"
labelFont={labelFontSpec}
metricFont={metricFontSpec}
metricFormat="0.00%"
/>
))
.add('with formatted string metric and a specified format', () => (
<Metric
label="Total Revenue"
metric="$10000000.00"
labelFont={labelFontSpec}
metricFont={metricFontSpec}
metricFormat="$0a"
/>
))
.add('with invalid metricFont', () => (
<Metric
label="Total Revenue"
metric="$10000000.00"
labelFont={labelFontSpec}
metricFont={metricFontSpec}
metricFormat="$0a"
/>
));

View file

@ -1,8 +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.
*/
export { Metric } from './metric';

View file

@ -1,49 +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, { CSSProperties } from 'react';
import ReactDOM from 'react-dom';
import { RendererFactory, Style } from '../../../types';
import { RendererStrings } from '../../../i18n';
import { Metric } from './component/metric';
const { metric: strings } = RendererStrings;
export interface Config {
/** The text to display under the metric */
label: string;
/** Font settings for the label */
labelFont: Style;
/** Value of the metric to display */
metric: string | number | null;
/** Font settings for the metric */
metricFont: Style;
/** NumeralJS format string */
metricFormat: string;
}
export const metric: RendererFactory<Config> = () => ({
name: 'metric',
displayName: strings.getDisplayName(),
help: strings.getHelpDescription(),
reuseDomNode: true,
render(domNode, config, handlers) {
ReactDOM.render(
<Metric
label={config.label}
labelFont={config.labelFont ? (config.labelFont.spec as CSSProperties) : {}}
metric={config.metric}
metricFont={config.metricFont ? (config.metricFont.spec as CSSProperties) : {}}
metricFormat={config.metricFormat}
/>,
domNode,
() => handlers.done()
);
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
},
});

View file

@ -1,50 +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 { metric } from '../../../canvas_plugin_src/functions/common/metric';
import { FunctionHelp } from '../function_help';
import { FunctionFactory } from '../../../types';
import { FONT_FAMILY, FONT_WEIGHT, CSS, NUMERALJS } from '../../constants';
export const help: FunctionHelp<FunctionFactory<typeof metric>> = {
help: i18n.translate('xpack.canvas.functions.metricHelpText', {
defaultMessage: 'Displays a number over a label.',
}),
args: {
label: i18n.translate('xpack.canvas.functions.metric.args.labelHelpText', {
defaultMessage: 'The text describing the metric.',
}),
labelFont: i18n.translate('xpack.canvas.functions.metric.args.labelFontHelpText', {
defaultMessage:
'The {CSS} font properties for the label. For example, {FONT_FAMILY} or {FONT_WEIGHT}.',
values: {
CSS,
FONT_FAMILY,
FONT_WEIGHT,
},
}),
metricFont: i18n.translate('xpack.canvas.functions.metric.args.metricFontHelpText', {
defaultMessage:
'The {CSS} font properties for the metric. For example, {FONT_FAMILY} or {FONT_WEIGHT}.',
values: {
CSS,
FONT_FAMILY,
FONT_WEIGHT,
},
}),
// TODO: Find a way to generate the docs URL here
metricFormat: i18n.translate('xpack.canvas.functions.metric.args.metricFormatHelpText', {
defaultMessage: 'A {NUMERALJS} format string. For example, {example1} or {example2}.',
values: {
example1: '`"0.0a"`',
example2: '`"0%"`',
NUMERALJS,
},
}),
},
};

View file

@ -46,7 +46,6 @@ import { help as lt } from './dict/lt';
import { help as lte } from './dict/lte';
import { help as mapCenter } from './dict/map_center';
import { help as markdown } from './dict/markdown';
import { help as metric } from './dict/metric';
import { help as neq } from './dict/neq';
import { help as pie } from './dict/pie';
import { help as plot } from './dict/plot';
@ -203,7 +202,6 @@ export const getFunctionHelp = (): FunctionHelpDict => ({
lte,
mapCenter,
markdown,
metric,
neq,
pie,
plot,

View file

@ -69,16 +69,6 @@ export const RendererStrings = {
},
}),
},
metric: {
getDisplayName: () =>
i18n.translate('xpack.canvas.renderer.metric.displayName', {
defaultMessage: 'Metric',
}),
getHelpDescription: () =>
i18n.translate('xpack.canvas.renderer.metric.helpDescription', {
defaultMessage: 'Render a number over a label',
}),
},
pie: {
getDisplayName: () =>
i18n.translate('xpack.canvas.renderer.pie.displayName', {

View file

@ -12,6 +12,7 @@
"embeddable",
"expressionError",
"expressionImage",
"expressionMetric",
"expressionRepeatImage",
"expressionRevealImage",
"expressionShape",

View file

@ -6,9 +6,8 @@
*/
import { testPie } from '../../canvas_plugin_src/functions/common/__fixtures__/test_pointseries';
import { functionWrapper } from '../../../../../src/plugins/presentation_util/public';
import { functionWrapper, fontStyle } from '../../../../../src/plugins/presentation_util/public';
import {
fontStyle,
grayscalePalette,
seriesStyle,
} from '../../canvas_plugin_src/functions/common/__fixtures__/test_styles';

View file

@ -5,10 +5,9 @@
* 2.0.
*/
import { functionWrapper } from '../../../../../src/plugins/presentation_util/public';
import { functionWrapper, fontStyle } from '../../../../../src/plugins/presentation_util/public';
import { testPlot } from '../../canvas_plugin_src/functions/common/__fixtures__/test_pointseries';
import {
fontStyle,
grayscalePalette,
yAxisConfig,
xAxisConfig,

View file

@ -4,8 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { fontStyle } from '../../../canvas_plugin_src/functions/common/__fixtures__/test_styles';
import { fontStyle } from '../../../../../../src/plugins/presentation_util/common/lib';
import { defaultSpec, getFontSpec } from './get_font_spec';
describe('getFontSpec', () => {

View file

@ -6,7 +6,6 @@
*/
import { markdown } from '../canvas_plugin_src/renderers/markdown';
import { metric } from '../canvas_plugin_src/renderers/metric';
import { pie } from '../canvas_plugin_src/renderers/pie';
import { plot } from '../canvas_plugin_src/renderers/plot';
import { progress } from '../canvas_plugin_src/renderers/progress';
@ -20,6 +19,7 @@ import {
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';
import { metricRenderer as metric } from '../../../../src/plugins/expression_metric/public';
/**
* This is a collection of renderers which are bundled with the runtime. If

View file

@ -33,6 +33,7 @@
{ "path": "../../../src/plugins/expressions/tsconfig.json" },
{ "path": "../../../src/plugins/expression_error/tsconfig.json" },
{ "path": "../../../src/plugins/expression_image/tsconfig.json" },
{ "path": "../../../src/plugins/expression_metric/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" },

View file

@ -6727,11 +6727,11 @@
"xpack.canvas.functions.markdown.args.fontHelpText": "コンテンツの {CSS} フォントプロパティです。たとえば、{fontFamily} または {fontWeight} です。",
"xpack.canvas.functions.markdown.args.openLinkHelpText": "新しいタブでリンクを開くためのtrue/false値。デフォルト値は「false」です。「true」に設定するとすべてのリンクが新しいタブで開くようになります。",
"xpack.canvas.functions.markdownHelpText": "{MARKDOWN} テキストをレンダリングするエレメントを追加します。ヒント:単一の数字、メトリック、テキストの段落には {markdownFn} 関数を使います。",
"xpack.canvas.functions.metric.args.labelFontHelpText": "ラベルの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。",
"xpack.canvas.functions.metric.args.labelHelpText": "メトリックを説明するテキストです。",
"xpack.canvas.functions.metric.args.metricFontHelpText": "メトリックの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。",
"xpack.canvas.functions.metric.args.metricFormatHelpText": "{NUMERALJS} 形式の文字列。例:{example1} または {example2}。",
"xpack.canvas.functions.metricHelpText": "ラベルの上に数字を表示します。",
"expressionMetric.functions.metric.args.labelFontHelpText": "ラベルの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。",
"expressionMetric.functions.metric.args.labelHelpText": "メトリックを説明するテキストです。",
"expressionMetric.functions.metric.args.metricFontHelpText": "メトリックの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。",
"expressionMetric.functions.metric.args.metricFormatHelpText": "{NUMERALJS} 形式の文字列。例:{example1} または {example2}。",
"expressionMetric.functions.metricHelpText": "ラベルの上に数字を表示します。",
"xpack.canvas.functions.neq.args.valueHelpText": "{CONTEXT} と比較される値です。",
"xpack.canvas.functions.neqHelpText": "{CONTEXT} が引数と等しくないかを戻します。",
"xpack.canvas.functions.pie.args.fontHelpText": "ラベルの {CSS} フォントプロパティです。例:{FONT_FAMILY} または {FONT_WEIGHT}。",
@ -6963,8 +6963,8 @@
"expressionImage.renderer.image.helpDescription": "画像をレンダリングします",
"xpack.canvas.renderer.markdown.displayName": "マークダウン",
"xpack.canvas.renderer.markdown.helpDescription": "{MARKDOWN} インプットを使用して {HTML} を表示",
"xpack.canvas.renderer.metric.displayName": "メトリック",
"xpack.canvas.renderer.metric.helpDescription": "ラベルの上に数字をレンダリングします",
"expressionMetric.renderer.metric.displayName": "メトリック",
"expressionMetric.renderer.metric.helpDescription": "ラベルの上に数字をレンダリングします",
"xpack.canvas.renderer.pie.displayName": "円グラフ",
"xpack.canvas.renderer.pie.helpDescription": "データから円グラフをレンダリングします",
"xpack.canvas.renderer.plot.displayName": "座標プロット",

View file

@ -6768,11 +6768,11 @@
"xpack.canvas.functions.markdown.args.fontHelpText": "内容的 {CSS} 字体属性。例如 {fontFamily} 或 {fontWeight}。",
"xpack.canvas.functions.markdown.args.openLinkHelpText": "用于在新标签页中打开链接的 true 或 false 值。默认值为 `false`。设置为 `true` 时将在新标签页中打开所有链接。",
"xpack.canvas.functions.markdownHelpText": "添加呈现 {MARKDOWN} 文本的元素。提示:将 {markdownFn} 函数用于单个数字、指标和文本段落。",
"xpack.canvas.functions.metric.args.labelFontHelpText": "标签的 {CSS} 字体属性。例如 {FONT_FAMILY} 或 {FONT_WEIGHT}。",
"xpack.canvas.functions.metric.args.labelHelpText": "描述指标的文本。",
"xpack.canvas.functions.metric.args.metricFontHelpText": "指标的 {CSS} 字体属性。例如 {FONT_FAMILY} 或 {FONT_WEIGHT}。",
"xpack.canvas.functions.metric.args.metricFormatHelpText": "{NUMERALJS} 格式字符串。例如 {example1} 或 {example2}。",
"xpack.canvas.functions.metricHelpText": "在标签上显示数字。",
"expressionMetric.functions.metric.args.labelFontHelpText": "标签的 {CSS} 字体属性。例如 {FONT_FAMILY} 或 {FONT_WEIGHT}。",
"expressionMetric.functions.metric.args.labelHelpText": "描述指标的文本。",
"expressionMetric.functions.metric.args.metricFontHelpText": "指标的 {CSS} 字体属性。例如 {FONT_FAMILY} 或 {FONT_WEIGHT}。",
"expressionMetric.functions.metric.args.metricFormatHelpText": "{NUMERALJS} 格式字符串。例如 {example1} 或 {example2}。",
"expressionMetric.functions.metricHelpText": "在标签上显示数字。",
"xpack.canvas.functions.neq.args.valueHelpText": "与 {CONTEXT} 比较的值。",
"xpack.canvas.functions.neqHelpText": "返回 {CONTEXT} 是否不等于参数。",
"xpack.canvas.functions.pie.args.fontHelpText": "标签的 {CSS} 字体属性。例如 {FONT_FAMILY} 或 {FONT_WEIGHT}。",
@ -7004,8 +7004,8 @@
"expressionImage.renderer.image.helpDescription": "呈现图像",
"xpack.canvas.renderer.markdown.displayName": "Markdown",
"xpack.canvas.renderer.markdown.helpDescription": "使用 {MARKDOWN} 输入呈现 {HTML}",
"xpack.canvas.renderer.metric.displayName": "指标",
"xpack.canvas.renderer.metric.helpDescription": "在标签上呈现数字",
"expressionMetric.renderer.metric.displayName": "指标",
"expressionMetric.renderer.metric.helpDescription": "在标签上呈现数字",
"xpack.canvas.renderer.pie.displayName": "饼图",
"xpack.canvas.renderer.pie.helpDescription": "根据您的数据呈现饼图",
"xpack.canvas.renderer.plot.displayName": "坐标图",