mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
* Added numberFormat arg type and metricFormat arg to metric function Updated function reference doc Fixed default arg value * Added stories for NumberFormatArgInput * Added arg alias * Added stories for metric renderer * Fixed ts errors and added comments * Updated comments * Removed extra test * Added snapshots * Addressing feedback * Fixed typo * Updated metricFormat help text * Removed redundant help prop
This commit is contained in:
parent
a594fdbee6
commit
ddfe6e8dea
19 changed files with 931 additions and 64 deletions
|
@ -1209,17 +1209,23 @@ Aliases: `label`, `text`, `description`
|
|||
|
||||
Default: `""`
|
||||
|
||||
|`labelFont`
|
||||
|`style`
|
||||
|The CSS font properties for the label. For example, `font-family` or `font-weight`.
|
||||
|
||||
Default: `{font size=14 family="'Open Sans', Helvetica, Arial, sans-serif" color="#000000" align=center}`.
|
||||
|
||||
|`metricFont`
|
||||
|`style`
|
||||
|The CSS font properties for the metric. For example, `font-family` or `font-weight`.
|
||||
|
||||
Default: `{font size=48 family="'Open Sans', Helvetica, Arial, sans-serif" color="#000000" align=center lHeight=48}`.
|
||||
|
||||
|`labelFont`
|
||||
|`style`
|
||||
|The CSS font properties for the label. For example, `font-family` or `font-weight`.
|
||||
|`metricFormat`
|
||||
|
||||
Default: `{font size=14 family="'Open Sans', Helvetica, Arial, sans-serif" color="#000000" align=center}`.
|
||||
Alias: `format`
|
||||
|`string`
|
||||
|A NumeralJS format string. For example, `"0.0a"` or `"0%"`. See http://numeraljs.com/#format.
|
||||
|===
|
||||
|
||||
*Returns:* `render`
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import { openSans } from '../../../common/lib/fonts';
|
||||
import header from './header.png';
|
||||
import { AdvancedSettings } from '../../../public/lib/kibana_advanced_settings';
|
||||
|
||||
import { ElementFactory } from '../../../types';
|
||||
export const metric: ElementFactory = () => ({
|
||||
|
@ -22,5 +23,6 @@ export const metric: ElementFactory = () => ({
|
|||
| metric "Countries"
|
||||
metricFont={font size=48 family="${openSans.value}" color="#000000" align="center" lHeight=48}
|
||||
labelFont={font size=14 family="${openSans.value}" color="#000000" align="center"}
|
||||
metricFormat="${AdvancedSettings.get('format:number:defaultPattern')}"
|
||||
| render`,
|
||||
});
|
||||
|
|
|
@ -38,7 +38,7 @@ describe('metric', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('metricStyle', () => {
|
||||
describe('metricFont', () => {
|
||||
it('sets the font style for the metric', () => {
|
||||
const result = fn(null, {
|
||||
metricFont: fontStyle,
|
||||
|
@ -51,7 +51,7 @@ describe('metric', () => {
|
|||
// it("sets a default style for the metric when not provided, () => {});
|
||||
});
|
||||
|
||||
describe('labelStyle', () => {
|
||||
describe('labelFont', () => {
|
||||
it('sets the font style for the label', () => {
|
||||
const result = fn(null, {
|
||||
labelFont: fontStyle,
|
||||
|
@ -63,5 +63,15 @@ describe('metric', () => {
|
|||
// TODO: write test when using an instance of the interpreter
|
||||
// it("sets a default style for the label when not provided, () => {});
|
||||
});
|
||||
|
||||
describe('metricFormat', () => {
|
||||
it('sets the number format of the metric value', () => {
|
||||
const result = fn(null, {
|
||||
metricFormat: '0.0%',
|
||||
});
|
||||
|
||||
expect(result.value).toHaveProperty('metricFormat', '0.0%');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ type Context = number | string | null;
|
|||
interface Arguments {
|
||||
label: string;
|
||||
metricFont: Style;
|
||||
metricFormat: string;
|
||||
labelFont: Style;
|
||||
}
|
||||
|
||||
|
@ -35,26 +36,32 @@ export function metric(): ExpressionFunction<'metric', Context, Arguments, Rende
|
|||
help: argHelp.label,
|
||||
default: '""',
|
||||
},
|
||||
metricFont: {
|
||||
types: ['style'],
|
||||
help: argHelp.metricFont,
|
||||
default: `{font size=48 family="${openSans.value}" color="#000000" align=center lHeight=48}`,
|
||||
},
|
||||
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: (context, { label, metricFont, labelFont }) => {
|
||||
fn: (context, { label, labelFont, metricFont, metricFormat }) => {
|
||||
return {
|
||||
type: 'render',
|
||||
as: 'metric',
|
||||
value: {
|
||||
metric: context === null ? '?' : context,
|
||||
label,
|
||||
metricFont,
|
||||
labelFont,
|
||||
metricFont,
|
||||
metricFormat,
|
||||
},
|
||||
};
|
||||
},
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots renderers/Metric with formatted string metric and a specified format 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"width": "200px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="canvasMetric"
|
||||
>
|
||||
<div
|
||||
className="canvasMetric__metric"
|
||||
style={
|
||||
Object {
|
||||
"color": "#b83c6f",
|
||||
"fontFamily": "Optima, 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, Arial, sans-serif",
|
||||
"fontSize": "48px",
|
||||
"fontStyle": "normal",
|
||||
"fontWeight": "bold",
|
||||
"lineHeight": "1",
|
||||
"textAlign": "center",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
$10m
|
||||
</div>
|
||||
<div
|
||||
className="canvasMetric__label"
|
||||
style={
|
||||
Object {
|
||||
"color": "#000000",
|
||||
"fontFamily": "Baskerville, Georgia, Garamond, 'Times New Roman', Times, serif",
|
||||
"fontSize": "24px",
|
||||
"fontStyle": "italic",
|
||||
"fontWeight": "normal",
|
||||
"lineHeight": "1",
|
||||
"textAlign": "center",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
Total Revenue
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots renderers/Metric with invalid metricFont 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"width": "200px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="canvasMetric"
|
||||
>
|
||||
<div
|
||||
className="canvasMetric__metric"
|
||||
style={
|
||||
Object {
|
||||
"color": "#b83c6f",
|
||||
"fontFamily": "Optima, 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, Arial, sans-serif",
|
||||
"fontSize": "48px",
|
||||
"fontStyle": "normal",
|
||||
"fontWeight": "bold",
|
||||
"lineHeight": "1",
|
||||
"textAlign": "center",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
$10m
|
||||
</div>
|
||||
<div
|
||||
className="canvasMetric__label"
|
||||
style={
|
||||
Object {
|
||||
"color": "#000000",
|
||||
"fontFamily": "Baskerville, Georgia, Garamond, 'Times New Roman', Times, serif",
|
||||
"fontSize": "24px",
|
||||
"fontStyle": "italic",
|
||||
"fontWeight": "normal",
|
||||
"lineHeight": "1",
|
||||
"textAlign": "center",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
Total Revenue
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots renderers/Metric with label 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"width": "200px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="canvasMetric"
|
||||
>
|
||||
<div
|
||||
className="canvasMetric__metric"
|
||||
style={
|
||||
Object {
|
||||
"color": "#b83c6f",
|
||||
"fontFamily": "Optima, 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, Arial, sans-serif",
|
||||
"fontSize": "48px",
|
||||
"fontStyle": "normal",
|
||||
"fontWeight": "bold",
|
||||
"lineHeight": "1",
|
||||
"textAlign": "center",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
$12.34
|
||||
</div>
|
||||
<div
|
||||
className="canvasMetric__label"
|
||||
style={
|
||||
Object {
|
||||
"color": "#000000",
|
||||
"fontFamily": "Baskerville, Georgia, Garamond, 'Times New Roman', Times, serif",
|
||||
"fontSize": "24px",
|
||||
"fontStyle": "italic",
|
||||
"fontWeight": "normal",
|
||||
"lineHeight": "1",
|
||||
"textAlign": "center",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
Average price
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots renderers/Metric with null metric 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"width": "200px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="canvasMetric"
|
||||
>
|
||||
<div
|
||||
className="canvasMetric__metric"
|
||||
style={Object {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots renderers/Metric with number metric 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"width": "200px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="canvasMetric"
|
||||
>
|
||||
<div
|
||||
className="canvasMetric__metric"
|
||||
style={
|
||||
Object {
|
||||
"color": "#b83c6f",
|
||||
"fontFamily": "Optima, 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, Arial, sans-serif",
|
||||
"fontSize": "48px",
|
||||
"fontStyle": "normal",
|
||||
"fontWeight": "bold",
|
||||
"lineHeight": "1",
|
||||
"textAlign": "center",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
12345.6789
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots renderers/Metric with number metric and a specified format 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"width": "200px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="canvasMetric"
|
||||
>
|
||||
<div
|
||||
className="canvasMetric__metric"
|
||||
style={
|
||||
Object {
|
||||
"color": "#b83c6f",
|
||||
"fontFamily": "Optima, 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, Arial, sans-serif",
|
||||
"fontSize": "48px",
|
||||
"fontStyle": "normal",
|
||||
"fontWeight": "bold",
|
||||
"lineHeight": "1",
|
||||
"textAlign": "center",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
-0.24%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`Storyshots renderers/Metric with string metric 1`] = `
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"width": "200px",
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
className="canvasMetric"
|
||||
>
|
||||
<div
|
||||
className="canvasMetric__metric"
|
||||
style={
|
||||
Object {
|
||||
"color": "#b83c6f",
|
||||
"fontFamily": "Optima, 'Lucida Grande', 'Lucida Sans Unicode', Verdana, Helvetica, Arial, sans-serif",
|
||||
"fontSize": "48px",
|
||||
"fontStyle": "normal",
|
||||
"fontWeight": "bold",
|
||||
"lineHeight": "1",
|
||||
"textAlign": "center",
|
||||
"textDecoration": "none",
|
||||
}
|
||||
}
|
||||
>
|
||||
$12.34
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
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"
|
||||
/>
|
||||
));
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { Metric } from './metric';
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FunctionComponent, CSSProperties } from 'react';
|
||||
import numeral from '@elastic/numeral';
|
||||
|
||||
interface Props {
|
||||
/** The text to display under the metric */
|
||||
label?: string;
|
||||
/** CSS font properties for the label */
|
||||
labelFont: CSSProperties;
|
||||
/** Value of the metric to display */
|
||||
metric: string | number | null;
|
||||
/** CSS font properties for the metric */
|
||||
metricFont: CSSProperties;
|
||||
/** NumeralJS format string */
|
||||
metricFormat?: string;
|
||||
}
|
||||
|
||||
export const Metric: FunctionComponent<Props> = ({
|
||||
label,
|
||||
metric,
|
||||
labelFont,
|
||||
metricFont,
|
||||
metricFormat,
|
||||
}) => (
|
||||
<div className="canvasMetric">
|
||||
<div className="canvasMetric__metric" style={metricFont}>
|
||||
{metricFormat ? numeral(metric).format(metricFormat) : metric}
|
||||
</div>
|
||||
{label && (
|
||||
<div className="canvasMetric__label" style={labelFont}>
|
||||
{label}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
|
@ -1,34 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
export const metric = () => ({
|
||||
name: 'metric',
|
||||
displayName: 'Metric',
|
||||
help: 'Render HTML Markup for the Metric element',
|
||||
reuseDomNode: true,
|
||||
render(domNode, config, handlers) {
|
||||
const metricFontStyle = config.metricFont ? config.metricFont.spec : {};
|
||||
const labelFontStyle = config.labelFont ? config.labelFont.spec : {};
|
||||
|
||||
ReactDOM.render(
|
||||
<div className="canvasMetric">
|
||||
<div className="canvasMetric__metric" style={metricFontStyle}>
|
||||
{config.metric}
|
||||
</div>
|
||||
<div className="canvasMetric__label" style={labelFontStyle}>
|
||||
{config.label}
|
||||
</div>
|
||||
</div>,
|
||||
domNode,
|
||||
() => handlers.done()
|
||||
);
|
||||
|
||||
handlers.onDestroy(() => ReactDOM.unmountComponentAtNode(domNode));
|
||||
},
|
||||
});
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { CSSProperties } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { RendererFactory, Style } from '../../../types';
|
||||
import { Metric } from './component/metric';
|
||||
|
||||
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: 'Metric',
|
||||
help: 'Render HTML Markup for the Metric element',
|
||||
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));
|
||||
},
|
||||
});
|
|
@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import { metric } from '../../functions/common/metric';
|
||||
import { FunctionHelp } from '.';
|
||||
import { FunctionFactory } from '../../../types';
|
||||
import { FONT_FAMILY, FONT_WEIGHT, CSS } from '../constants';
|
||||
import { FONT_FAMILY, FONT_WEIGHT, CSS, NUMERALJS } from '../constants';
|
||||
|
||||
export const help: FunctionHelp<FunctionFactory<typeof metric>> = {
|
||||
help: i18n.translate('xpack.canvas.functions.metricHelpText', {
|
||||
|
@ -18,15 +18,6 @@ export const help: FunctionHelp<FunctionFactory<typeof metric>> = {
|
|||
label: i18n.translate('xpack.canvas.functions.metric.args.labelHelpText', {
|
||||
defaultMessage: 'The text describing the metric.',
|
||||
}),
|
||||
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,
|
||||
},
|
||||
}),
|
||||
labelFont: i18n.translate('xpack.canvas.functions.metric.args.labelFontHelpText', {
|
||||
defaultMessage:
|
||||
'The {CSS} font properties for the label. For example, {FONT_FAMILY} or {FONT_WEIGHT}.',
|
||||
|
@ -36,5 +27,24 @@ export const help: FunctionHelp<FunctionFactory<typeof metric>> = {
|
|||
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,
|
||||
},
|
||||
}),
|
||||
metricFormat: i18n.translate('xpack.canvas.functions.metric.args.metricFormatHelpText', {
|
||||
defaultMessage:
|
||||
'A {NUMERALJS} format string. For example, {example1} or {example2}. See {url}.',
|
||||
values: {
|
||||
example1: `"0.0a"`,
|
||||
example2: `"0%"`,
|
||||
NUMERALJS,
|
||||
url: 'http://numeraljs.com/#format',
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ import { datacolumn } from './datacolumn';
|
|||
import { filterGroup } from './filter_group';
|
||||
import { imageUpload } from './image_upload';
|
||||
import { number } from './number';
|
||||
import { numberFormat } from './number_format';
|
||||
import { palette } from './palette';
|
||||
import { percentage } from './percentage';
|
||||
import { range } from './range';
|
||||
|
@ -24,6 +25,7 @@ export const args = [
|
|||
filterGroup,
|
||||
imageUpload,
|
||||
number,
|
||||
numberFormat,
|
||||
palette,
|
||||
percentage,
|
||||
range,
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Storyshots arguments/NumberFormat with custom format 1`] = `
|
||||
Array [
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--compressed"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<select
|
||||
className="euiSelect euiSelect--compressed"
|
||||
id="NumberFormatExample3"
|
||||
onChange={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
value=""
|
||||
>
|
||||
<option
|
||||
value="0.0[000]"
|
||||
>
|
||||
Number
|
||||
</option>
|
||||
<option
|
||||
value="0.0%"
|
||||
>
|
||||
Percent
|
||||
</option>
|
||||
<option
|
||||
value="$0.00"
|
||||
>
|
||||
Currency
|
||||
</option>
|
||||
<option
|
||||
value="00:00:00"
|
||||
>
|
||||
Duration
|
||||
</option>
|
||||
<option
|
||||
value="0.0b"
|
||||
>
|
||||
Bytes
|
||||
</option>
|
||||
<option
|
||||
value=""
|
||||
>
|
||||
Custom
|
||||
</option>
|
||||
</select>
|
||||
<div
|
||||
className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<span
|
||||
className="euiFormControlLayoutCustomIcon"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--medium euiIcon-isLoading euiFormControlLayoutCustomIcon__icon"
|
||||
focusable="false"
|
||||
height={16}
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width={16}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
className="euiSpacer euiSpacer--s"
|
||||
/>,
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--compressed"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<input
|
||||
className="euiFieldText euiFieldText--compressed"
|
||||
onChange={[Function]}
|
||||
placeholder="0.0a"
|
||||
type="text"
|
||||
value="0.0[000]a"
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Storyshots arguments/NumberFormat with no format 1`] = `
|
||||
Array [
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--compressed"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<select
|
||||
className="euiSelect euiSelect--compressed"
|
||||
id="NumberFormatExample1"
|
||||
onChange={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
value=""
|
||||
>
|
||||
<option
|
||||
value="0.0[000]"
|
||||
>
|
||||
Number
|
||||
</option>
|
||||
<option
|
||||
value="0.0%"
|
||||
>
|
||||
Percent
|
||||
</option>
|
||||
<option
|
||||
value="$0.00"
|
||||
>
|
||||
Currency
|
||||
</option>
|
||||
<option
|
||||
value="00:00:00"
|
||||
>
|
||||
Duration
|
||||
</option>
|
||||
<option
|
||||
value="0.0b"
|
||||
>
|
||||
Bytes
|
||||
</option>
|
||||
<option
|
||||
value=""
|
||||
>
|
||||
Custom
|
||||
</option>
|
||||
</select>
|
||||
<div
|
||||
className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<span
|
||||
className="euiFormControlLayoutCustomIcon"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--medium euiIcon-isLoading euiFormControlLayoutCustomIcon__icon"
|
||||
focusable="false"
|
||||
height={16}
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width={16}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
<div
|
||||
className="euiSpacer euiSpacer--s"
|
||||
/>,
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--compressed"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<input
|
||||
className="euiFieldText euiFieldText--compressed"
|
||||
onChange={[Function]}
|
||||
placeholder="0.0a"
|
||||
type="text"
|
||||
value=""
|
||||
/>
|
||||
</div>
|
||||
</div>,
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`Storyshots arguments/NumberFormat with preset format 1`] = `
|
||||
<div
|
||||
className="euiFormControlLayout euiFormControlLayout--compressed"
|
||||
>
|
||||
<div
|
||||
className="euiFormControlLayout__childrenWrapper"
|
||||
>
|
||||
<select
|
||||
className="euiSelect euiSelect--compressed"
|
||||
id="NumberFormatExample2"
|
||||
onChange={[Function]}
|
||||
onMouseUp={[Function]}
|
||||
value="$0.00"
|
||||
>
|
||||
<option
|
||||
value="0.0[000]"
|
||||
>
|
||||
Number
|
||||
</option>
|
||||
<option
|
||||
value="0.0%"
|
||||
>
|
||||
Percent
|
||||
</option>
|
||||
<option
|
||||
value="$0.00"
|
||||
>
|
||||
Currency
|
||||
</option>
|
||||
<option
|
||||
value="00:00:00"
|
||||
>
|
||||
Duration
|
||||
</option>
|
||||
<option
|
||||
value="0.0b"
|
||||
>
|
||||
Bytes
|
||||
</option>
|
||||
<option
|
||||
value=""
|
||||
>
|
||||
Custom
|
||||
</option>
|
||||
</select>
|
||||
<div
|
||||
className="euiFormControlLayoutIcons euiFormControlLayoutIcons--right"
|
||||
>
|
||||
<span
|
||||
className="euiFormControlLayoutCustomIcon"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="euiIcon euiIcon--medium euiIcon-isLoading euiFormControlLayoutCustomIcon__icon"
|
||||
focusable="false"
|
||||
height={16}
|
||||
style={null}
|
||||
viewBox="0 0 16 16"
|
||||
width={16}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import React from 'react';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { NumberFormatArgInput } from '../number_format';
|
||||
|
||||
const numberFormats = [
|
||||
{ value: '0.0[000]', text: 'Number' },
|
||||
{ value: '0.0%', text: 'Percent' },
|
||||
{ value: '$0.00', text: 'Currency' },
|
||||
{ value: '00:00:00', text: 'Duration' },
|
||||
{ value: '0.0b', text: 'Bytes' },
|
||||
];
|
||||
|
||||
storiesOf('arguments/NumberFormat', module)
|
||||
.add('with no format', () => (
|
||||
<NumberFormatArgInput
|
||||
numberFormats={numberFormats}
|
||||
onValueChange={action('onValueChange')}
|
||||
argValue=""
|
||||
argId="NumberFormatExample1"
|
||||
/>
|
||||
))
|
||||
.add('with preset format', () => (
|
||||
<NumberFormatArgInput
|
||||
numberFormats={numberFormats}
|
||||
onValueChange={action('onValueChange')}
|
||||
argValue="$0.00"
|
||||
argId="NumberFormatExample2"
|
||||
/>
|
||||
))
|
||||
.add('with custom format', () => (
|
||||
<NumberFormatArgInput
|
||||
numberFormats={numberFormats}
|
||||
onValueChange={action('onValueChange')}
|
||||
argValue="0.0[000]a"
|
||||
argId="NumberFormatExample3"
|
||||
/>
|
||||
));
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { compose, withProps } from 'recompose';
|
||||
import { NumberFormatArgInput as Component, Props as ComponentProps } from './number_format';
|
||||
import { AdvancedSettings } from '../../../../public/lib/kibana_advanced_settings';
|
||||
// @ts-ignore untyped local lib
|
||||
import { templateFromReactComponent } from '../../../../public/lib/template_from_react_component';
|
||||
|
||||
const formatMap = {
|
||||
NUMBER: AdvancedSettings.get('format:number:defaultPattern'),
|
||||
PERCENT: AdvancedSettings.get('format:percent:defaultPattern'),
|
||||
CURRENCY: AdvancedSettings.get('format:currency:defaultPattern'),
|
||||
DURATION: '00:00:00',
|
||||
BYTES: AdvancedSettings.get('format:bytes:defaultPattern'),
|
||||
};
|
||||
|
||||
const numberFormats = [
|
||||
{ value: formatMap.NUMBER, text: 'Number' },
|
||||
{ value: formatMap.PERCENT, text: 'Percent' },
|
||||
{ value: formatMap.CURRENCY, text: 'Currency' },
|
||||
{ value: formatMap.DURATION, text: 'Duration' },
|
||||
{ value: formatMap.BYTES, text: 'Bytes' },
|
||||
];
|
||||
|
||||
export const NumberFormatArgInput = compose<ComponentProps, null>(withProps({ numberFormats }))(
|
||||
Component
|
||||
);
|
||||
|
||||
export const numberFormat = () => ({
|
||||
name: 'numberFormat',
|
||||
displayName: 'Number Format',
|
||||
help: 'Select or enter a valid NumeralJS format',
|
||||
simpleTemplate: templateFromReactComponent(NumberFormatArgInput),
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Fragment, ChangeEvent, FunctionComponent } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EuiSelect, EuiFieldText, EuiSpacer } from '@elastic/eui';
|
||||
|
||||
interface NumberFormatOption {
|
||||
/** A NumeralJS format string */
|
||||
value: string;
|
||||
/** The name to display for the format */
|
||||
text: string;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
/** An array of number formats options */
|
||||
numberFormats: NumberFormatOption[];
|
||||
/** The handler to invoke when value changes */
|
||||
onValueChange: (value: string) => void;
|
||||
/** The value of the argument */
|
||||
argValue: string;
|
||||
/** The ID for the argument */
|
||||
argId: string;
|
||||
}
|
||||
|
||||
export const NumberFormatArgInput: FunctionComponent<Props> = ({
|
||||
numberFormats,
|
||||
onValueChange,
|
||||
argValue,
|
||||
argId,
|
||||
}) => {
|
||||
const formatOptions = numberFormats.concat({ value: '', text: 'Custom' });
|
||||
const handleTextChange = (ev: ChangeEvent<HTMLInputElement>) => onValueChange(ev.target.value);
|
||||
const handleSelectChange = (ev: ChangeEvent<HTMLSelectElement>) => {
|
||||
const { value } = formatOptions[ev.target.selectedIndex];
|
||||
return onValueChange(value || '0.0a');
|
||||
};
|
||||
|
||||
// checks if the argValue is one of the preset formats
|
||||
const isCustomFormat = !argValue || !formatOptions.map(({ value }) => value).includes(argValue);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiSelect
|
||||
compressed
|
||||
id={argId}
|
||||
value={isCustomFormat ? '' : argValue}
|
||||
options={formatOptions}
|
||||
onChange={handleSelectChange}
|
||||
/>
|
||||
{isCustomFormat && (
|
||||
<Fragment>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiFieldText
|
||||
placeholder="0.0a"
|
||||
value={argValue}
|
||||
compressed
|
||||
onChange={handleTextChange}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
NumberFormatArgInput.propTypes = {
|
||||
onValueChange: PropTypes.func.isRequired,
|
||||
argValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]).isRequired,
|
||||
argId: PropTypes.string.isRequired,
|
||||
};
|
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
import { openSans } from '../../../common/lib/fonts';
|
||||
import { AdvancedSettings } from '../../../public/lib/kibana_advanced_settings';
|
||||
|
||||
export const metric = () => ({
|
||||
name: 'metric',
|
||||
|
@ -19,6 +20,13 @@ export const metric = () => ({
|
|||
argType: 'string',
|
||||
default: '""',
|
||||
},
|
||||
{
|
||||
name: 'labelFont',
|
||||
displayName: 'Label text settings',
|
||||
help: 'Fonts, alignment and color',
|
||||
argType: 'font',
|
||||
default: `{font size=18 family="${openSans.value}" color="#000000" align=center}`,
|
||||
},
|
||||
{
|
||||
name: 'metricFont',
|
||||
displayName: 'Metric text settings',
|
||||
|
@ -27,11 +35,10 @@ export const metric = () => ({
|
|||
default: `{font size=48 family="${openSans.value}" color="#000000" align=center lHeight=48}`,
|
||||
},
|
||||
{
|
||||
name: 'labelFont',
|
||||
displayName: 'Label text settings',
|
||||
help: 'Fonts, alignment and color',
|
||||
argType: 'font',
|
||||
default: `{font size=18 family="${openSans.value}" color="#000000" align=center}`,
|
||||
name: 'metricFormat',
|
||||
displayName: 'Metric Format',
|
||||
argType: 'numberFormat',
|
||||
default: `"${AdvancedSettings.get('format:number:defaultPattern')}"`,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
|
||||
export const AdvancedSettings = chrome.getUiSettingsClient();
|
|
@ -7,19 +7,32 @@
|
|||
type GenericCallback = (callback: () => void) => void;
|
||||
|
||||
export interface RendererHandlers {
|
||||
/** Handler to invoke when an element has finished rendering */
|
||||
done: () => void;
|
||||
getFilter: () => string;
|
||||
/** Handler to invoke when an element is deleted or changes to a different render type */
|
||||
onDestroy: GenericCallback;
|
||||
/** Handler to invoke when an element's dimensions have changed*/
|
||||
onResize: GenericCallback;
|
||||
/** Retrieves the value of the filter property on the element object persisted on the workpad */
|
||||
getFilter: () => string;
|
||||
/** Sets the value of the filter property on the element object persisted on the workpad */
|
||||
setFilter: (filter: string) => void;
|
||||
}
|
||||
|
||||
export interface RendererSpec<RendererConfig = {}> {
|
||||
/** The render type */
|
||||
name: string;
|
||||
/** The name to display */
|
||||
displayName: string;
|
||||
/** A description of what is rendered */
|
||||
help: string;
|
||||
/** Indicate whether the element should reuse the existing DOM element when re-rendering */
|
||||
reuseDomNode: boolean;
|
||||
height: number;
|
||||
/** The default width of the element in pixels */
|
||||
width?: number;
|
||||
/** The default height of the element in pixels */
|
||||
height?: number;
|
||||
/** A function that renders an element into the specified DOM element */
|
||||
render: (domNode: HTMLElement, config: RendererConfig, handlers: RendererHandlers) => void;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue