From 54ed36f69e03241745d687e35ff6ed8cac1f18e2 Mon Sep 17 00:00:00 2001 From: dej611 Date: Fri, 18 Sep 2020 14:42:38 +0200 Subject: [PATCH 01/24] :sparkles: New alternative formatter for ranges --- .../common/search/aggs/utils/get_format_with_aggs.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts index 01369206ab3cf..5f339ad1fbee7 100644 --- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts +++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts @@ -53,8 +53,20 @@ export function getFormatWithAggs(getFieldFormat: GetFieldFormat): GetFieldForma id: nestedFormatter.id, params: nestedFormatter.params, }); + const lte = '\u2264'; const gte = '\u2265'; const lt = '\u003c'; + if (params.template === 'within') { + return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessageWithin', { + defaultMessage: '{from} {lte} and {lt} {to}', + values: { + lte, + from: format.convert(range.gte), + lt, + to: format.convert(range.lt), + }, + }); + } return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessage', { defaultMessage: '{gte} {from} and {lt} {to}', values: { From 1904551ba214d16b9b131019811154234c7123b5 Mon Sep 17 00:00:00 2001 From: dej611 Date: Fri, 18 Sep 2020 14:47:21 +0200 Subject: [PATCH 02/24] :white_check_mark: Add range formatter test for alternative format --- .../search/aggs/utils/get_format_with_aggs.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts index 20d8cfc105e49..735b3f224f6d8 100644 --- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts +++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts @@ -79,6 +79,15 @@ describe('getFormatWithAggs', () => { expect(getFormat).toHaveBeenCalledTimes(1); }); + test('creates alternative format for range', () => { + const mapping = { id: 'range', params: { template: 'within' } }; + const getFieldFormat = getFormatWithAggs(getFormat); + const format = getFieldFormat(mapping); + + expect(format.convert({ gte: 1, lt: 20 })).toBe('1 ≤ and < 20'); + expect(getFormat).toHaveBeenCalledTimes(1); + }); + test('creates custom format for terms', () => { const mapping = { id: 'terms', From 004a580a744ba744206f87268413550e1438f4a7 Mon Sep 17 00:00:00 2001 From: dej611 Date: Mon, 12 Oct 2020 13:15:53 +0200 Subject: [PATCH 03/24] :lipstick: Adopt new range format template --- .../common/search/aggs/utils/get_format_with_aggs.test.ts | 4 ++-- .../data/common/search/aggs/utils/get_format_with_aggs.ts | 8 +++----- .../operations/definitions/ranges/ranges.tsx | 5 ++++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts index 735b3f224f6d8..8444b2dddaa83 100644 --- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts +++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts @@ -80,11 +80,11 @@ describe('getFormatWithAggs', () => { }); test('creates alternative format for range', () => { - const mapping = { id: 'range', params: { template: 'within' } }; + const mapping = { id: 'range', params: { template: 'compact' } }; const getFieldFormat = getFormatWithAggs(getFormat); const format = getFieldFormat(mapping); - expect(format.convert({ gte: 1, lt: 20 })).toBe('1 ≤ and < 20'); + expect(format.convert({ gte: 1, lt: 20 })).toBe('1 - 20'); expect(getFormat).toHaveBeenCalledTimes(1); }); diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts index 5f339ad1fbee7..debab2702a6b7 100644 --- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts +++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts @@ -53,16 +53,14 @@ export function getFormatWithAggs(getFieldFormat: GetFieldFormat): GetFieldForma id: nestedFormatter.id, params: nestedFormatter.params, }); - const lte = '\u2264'; + const gte = '\u2265'; const lt = '\u003c'; - if (params.template === 'within') { + if (params.template === 'compact') { return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessageWithin', { - defaultMessage: '{from} {lte} and {lt} {to}', + defaultMessage: '{from} - {to}', values: { - lte, from: format.convert(range.gte), - lt, to: format.convert(range.lt), }, }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index a8304456262eb..9bde6655a546b 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -149,7 +149,10 @@ export const rangeOperation: OperationDefinition { - const rangeFormatter = data.fieldFormats.deserialize({ id: 'range' }); + const rangeFormatter = data.fieldFormats.deserialize({ + id: 'range', + params: { template: 'compact' }, + }); const MAX_HISTOGRAM_BARS = uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS); const granularityStep = (MAX_HISTOGRAM_BARS - MIN_HISTOGRAM_BARS) / SLICES; const maxBarsDefaultValue = (MAX_HISTOGRAM_BARS - MIN_HISTOGRAM_BARS) / 2; From 5a1224ea20ef61f201c36e2786b7675e155e94f7 Mon Sep 17 00:00:00 2001 From: dej611 Date: Wed, 14 Oct 2020 12:51:21 +0200 Subject: [PATCH 04/24] :lipstick: Revisit styling for range popover --- .../definitions/ranges/advanced_editor.scss | 4 ++++ .../definitions/ranges/advanced_editor.tsx | 14 ++------------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss index b1658043f3204..2a7e8508ffa5a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss @@ -3,4 +3,8 @@ @include euiFontSizeS; min-height: $euiSizeXL; width: 100%; +} + +.lnsRangesOperation__popoverNumberField { + width: 100px; } \ No newline at end of file diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx index 16b861ae034fa..be2080ac401f6 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx @@ -107,6 +107,7 @@ export const RangePopover = ({ { const newRange = { @@ -116,12 +117,6 @@ export const RangePopover = ({ setTempRange(newRange); saveRangeAndReset(newRange); }} - append={ - - {lteAppendLabel} - - } - fullWidth compressed placeholder={FROM_PLACEHOLDER} isInvalid={!isValidRange(tempRange)} @@ -132,6 +127,7 @@ export const RangePopover = ({ { const newRange = { @@ -141,12 +137,6 @@ export const RangePopover = ({ setTempRange(newRange); saveRangeAndReset(newRange); }} - prepend={ - - {ltPrependLabel} - - } - fullWidth compressed placeholder={TO_PLACEHOLDER} isInvalid={!isValidRange(tempRange)} From a058a9ca86c7943683ddc9cef9217dd34cc80323 Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 15 Oct 2020 15:38:38 +0200 Subject: [PATCH 05/24] :recycle: Rework the logic of the formatter to better handle the range base and custom scenario --- .../search/aggs/utils/get_format_with_aggs.ts | 20 ++++-- .../editor_frame_service/format_column.ts | 42 +++++++++-- .../dimension_panel/dimension_editor.tsx | 3 +- .../definitions/ranges/advanced_editor.tsx | 23 +----- .../operations/definitions/ranges/ranges.tsx | 33 ++++++++- .../indexpattern_datasource/to_expression.ts | 70 ++++++++++++++----- 6 files changed, 139 insertions(+), 52 deletions(-) diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts index 9f7336b681569..dc2ef2d4dec17 100644 --- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts +++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts @@ -59,12 +59,22 @@ export function getFormatWithAggs(getFieldFormat: GetFieldFormat): GetFieldForma const gte = '\u2265'; const lt = '\u003c'; + let fromValue = format.convert(range.gte); + let toValue = format.convert(range.lt); + // In case of identity formatter and a specific flag, replace Infinity values by specific strings + if (params.replaceInfinity && nestedFormatter.id == null) { + const FROM_PLACEHOLDER = '\u2212\u221E'; + const TO_PLACEHOLDER = '+\u221E'; + fromValue = isFinite(range.gte) ? fromValue : FROM_PLACEHOLDER; + toValue = isFinite(range.lt) ? toValue : TO_PLACEHOLDER; + } + if (params.template === 'compact') { - return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessageWithin', { + return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessageCompact', { defaultMessage: '{from} - {to}', values: { - from: format.convert(range.gte), - to: format.convert(range.lt), + from: fromValue, + to: toValue, }, }); } @@ -72,9 +82,9 @@ export function getFormatWithAggs(getFieldFormat: GetFieldFormat): GetFieldForma defaultMessage: '{gte} {from} and {lt} {to}', values: { gte, - from: format.convert(range.gte), + from: fromValue, lt, - to: format.convert(range.lt), + to: toValue, }, }); }); diff --git a/x-pack/plugins/lens/public/editor_frame_service/format_column.ts b/x-pack/plugins/lens/public/editor_frame_service/format_column.ts index b95139a00ec57..e1d43e38cce8c 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/format_column.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/format_column.ts @@ -10,9 +10,15 @@ interface FormatColumn { format: string; columnId: string; decimals?: number; + nestedFormat?: string; + template?: string; + replaceInfinity?: boolean; } -const supportedFormats: Record string }> = { +export const supportedFormats: Record< + string, + { decimalsToPattern: (decimals?: number) => string } +> = { number: { decimalsToPattern: (decimals = 2) => { if (decimals === 0) { @@ -63,13 +69,27 @@ export const formatColumn: ExpressionFunctionDefinition< types: ['number'], help: '', }, + nestedFormat: { + types: ['string'], + help: '', + }, + template: { + types: ['string'], + help: '', + }, + replaceInfinity: { + types: ['boolean'], + help: '', + }, }, inputTypes: ['kibana_datatable'], - fn(input, { format, columnId, decimals }: FormatColumn) { + fn(input, { format, columnId, decimals, nestedFormat, template, replaceInfinity }: FormatColumn) { return { ...input, columns: input.columns.map((col) => { if (col.id === columnId) { + const extraParams = { template, replaceInfinity }; + if (supportedFormats[format]) { return { ...col, @@ -78,12 +98,26 @@ export const formatColumn: ExpressionFunctionDefinition< params: { pattern: supportedFormats[format].decimalsToPattern(decimals) }, }, }; - } else { + } + if (nestedFormat && supportedFormats[nestedFormat]) { return { ...col, - formatHint: { id: format, params: {} }, + formatHint: { + id: format, + params: { + id: nestedFormat, + params: { + pattern: supportedFormats[nestedFormat].decimalsToPattern(decimals), + }, + ...extraParams, + }, + }, }; } + return { + ...col, + formatHint: { id: format, params: { ...extraParams } }, + }; } return col; }), diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx index 8b0c9011f2c27..161d69ad8cd69 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/dimension_panel/dimension_editor.tsx @@ -431,7 +431,8 @@ export function DimensionEditor(props: DimensionEditorProps) { /> )} - {selectedColumn && selectedColumn.dataType === 'number' ? ( + {selectedColumn && + (selectedColumn.dataType === 'number' || selectedColumn.operationType === 'range') ? ( { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx index d281eb9d0639c..3efb5e9c69091 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx @@ -18,7 +18,6 @@ import { EuiLink, EuiText, EuiPopover, - EuiToolTip, htmlIdGenerator, } from '@elastic/eui'; import { keys } from '@elastic/eui'; @@ -39,8 +38,8 @@ type LocalRangeType = RangeTypeLens & { id: string }; const getBetterLabel = (range: RangeTypeLens, formatter: IFieldFormat) => range.label || formatter.convert({ - gte: isValidNumber(range.from) ? range.from : FROM_PLACEHOLDER, - lt: isValidNumber(range.to) ? range.to : TO_PLACEHOLDER, + gte: isValidNumber(range.from) ? range.from : -Infinity, + lt: isValidNumber(range.to) ? range.to : Infinity, }); export const RangePopover = ({ @@ -55,7 +54,6 @@ export const RangePopover = ({ Button: React.FunctionComponent<{ onClick: MouseEventHandler }>; isOpenByCreation: boolean; setIsOpenByCreation: (open: boolean) => void; - formatter: IFieldFormat; }) => { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [tempRange, setTempRange] = useState(range); @@ -70,22 +68,6 @@ export const RangePopover = ({ }; const { from, to, label } = tempRange; - const lteAppendLabel = i18n.translate('xpack.lens.indexPattern.ranges.lessThanOrEqualAppend', { - defaultMessage: '\u2264', - }); - const lteTooltipContent = i18n.translate( - 'xpack.lens.indexPattern.ranges.lessThanOrEqualTooltip', - { - defaultMessage: 'Less than or equal to', - } - ); - const ltPrependLabel = i18n.translate('xpack.lens.indexPattern.ranges.lessThanPrepend', { - defaultMessage: '\u003c', - }); - const ltTooltipContent = i18n.translate('xpack.lens.indexPattern.ranges.lessThanTooltip', { - defaultMessage: 'Less than', - }); - const onSubmit = () => { setIsPopoverOpen(false); setIsOpenByCreation(false); @@ -274,7 +256,6 @@ export const AdvancedRangeEditor = ({ } setLocalRanges(newRanges); }} - formatter={formatter} Button={({ onClick }: { onClick: MouseEventHandler }) => ( { + const numberFormat = currentColumn.params.format as + | undefined + | { + id: string; + params: { decimals: number }; + }; + const numberFormatterPattern = + numberFormat && + supportedFormats[numberFormat.id] && + supportedFormats[numberFormat.id].decimalsToPattern(numberFormat.params.decimals); + const rangeFormatter = data.fieldFormats.deserialize({ - id: 'range', - params: { template: 'compact' }, + ...currentColumn.params.parentFormat, + params: { + ...currentColumn.params.parentFormat?.params, + ...(numberFormat && { id: numberFormat.id, params: { pattern: numberFormatterPattern } }), + }, }); + const MAX_HISTOGRAM_BARS = uiSettings.get(UI_SETTINGS.HISTOGRAM_MAX_BARS); const granularityStep = (MAX_HISTOGRAM_BARS - MIN_HISTOGRAM_BARS) / SLICES; const maxBarsDefaultValue = (MAX_HISTOGRAM_BARS - MIN_HISTOGRAM_BARS) / 2; @@ -174,6 +197,10 @@ export const rangeOperation: OperationDefinition { const scale = newMode === MODES.Range ? 'ordinal' : 'interval'; const dataType = newMode === MODES.Range ? 'string' : 'number'; + const parentFormat = + newMode === MODES.Range + ? { id: 'range', params: { template: 'compact', replaceInfinity: true } } + : undefined; setState( changeColumn({ state, @@ -187,6 +214,8 @@ export const rangeOperation: OperationDefinition); - type FormattedColumn = Required>; + type FormattedColumn = Required< + Extract< + IndexPatternColumn, + | { + params?: { + format: unknown; + }; + } + // when formatters are nested there's a slightly different format + | { + params: { + format?: unknown; + parentFormat: unknown; + }; + } + > + >; const columnsWithFormatters = columnEntries.filter( - ([, col]) => col.params && 'format' in col.params && col.params.format + ([, col]) => + col.params && + (('format' in col.params && col.params.format) || + ('parentFormat' in col.params && col.params.parentFormat)) ) as Array<[string, FormattedColumn]>; - const formatterOverrides: ExpressionFunctionAST[] = columnsWithFormatters.map(([id, col]) => { - const format = (col as FormattedColumn).params!.format; - const base: ExpressionFunctionAST = { - type: 'function', - function: 'lens_format_column', - arguments: { - format: [format.id], - columnId: [id], - }, - }; - if (typeof format.params?.decimals === 'number') { - return { - ...base, + const formatterOverrides: ExpressionFunctionAST[] = columnsWithFormatters.map( + ([id, col]: [string, FormattedColumn]) => { + const parentFormat = 'parentFormat' in col.params ? col.params!.parentFormat : undefined; + const format = (col as FormattedColumn).params!.format; + const base: ExpressionFunctionAST = { + type: 'function', + function: 'lens_format_column', arguments: { - ...base.arguments, - decimals: [format.params.decimals], + format: [(format || parentFormat!).id], // either one of the two is available + columnId: [id], }, }; + + // Handle nested formatter carefully one parameter at the time + if (parentFormat) { + base.arguments.format = [parentFormat.id]; + if (format) { + base.arguments.nestedFormat = [format.id]; + } + } + if (parentFormat?.params?.template) { + base.arguments.template = [parentFormat.params?.template]; + } + + if (parentFormat?.params?.replaceInfinity) { + base.arguments.replaceInfinity = [parentFormat.params?.replaceInfinity]; + } + + if (typeof format?.params?.decimals === 'number') { + base.arguments.decimals = [format.params.decimals]; + } + return base; } - return base; - }); + ); const allDateHistogramFields = Object.values(columns) .map((column) => From 2db30148d11e6e91639b244734cc112d8a6c283d Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 15 Oct 2020 15:52:09 +0200 Subject: [PATCH 06/24] :label: fix type issue --- .../lens/public/indexpattern_datasource/to_expression.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index 7688942c3f5d3..7a0883b77ea7c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -75,7 +75,7 @@ function getExpressionForLayer( | { params: { format?: unknown; - parentFormat: unknown; + parentFormat?: unknown; }; } > @@ -89,7 +89,8 @@ function getExpressionForLayer( ) as Array<[string, FormattedColumn]>; const formatterOverrides: ExpressionFunctionAST[] = columnsWithFormatters.map( ([id, col]: [string, FormattedColumn]) => { - const parentFormat = 'parentFormat' in col.params ? col.params!.parentFormat : undefined; + // TODO: improve the type handling here + const parentFormat = 'parentFormat' in col.params ? col.params!.parentFormat! : undefined; const format = (col as FormattedColumn).params!.format; const base: ExpressionFunctionAST = { type: 'function', From 142cbfd83ba01defeebc4cf55aa432a8edc1e4c8 Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 15 Oct 2020 15:54:32 +0200 Subject: [PATCH 07/24] :lipstick: Use the arrow format rather than dash --- .../common/search/aggs/utils/get_format_with_aggs.test.ts | 4 ++-- .../data/common/search/aggs/utils/get_format_with_aggs.ts | 6 +++--- .../operations/definitions/ranges/ranges.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts index 9fbdff88a4d25..f76d94adc191d 100644 --- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts +++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts @@ -80,11 +80,11 @@ describe('getFormatWithAggs', () => { }); test('creates alternative format for range', () => { - const mapping = { id: 'range', params: { template: 'compact' } }; + const mapping = { id: 'range', params: { template: 'arrow_right' } }; const getFieldFormat = getFormatWithAggs(getFormat); const format = getFieldFormat(mapping); - expect(format.convert({ gte: 1, lt: 20 })).toBe('1 - 20'); + expect(format.convert({ gte: 1, lt: 20 })).toBe('1 -> 20'); expect(getFormat).toHaveBeenCalledTimes(1); }); diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts index dc2ef2d4dec17..482b0f47689f4 100644 --- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts +++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts @@ -69,9 +69,9 @@ export function getFormatWithAggs(getFieldFormat: GetFieldFormat): GetFieldForma toValue = isFinite(range.lt) ? toValue : TO_PLACEHOLDER; } - if (params.template === 'compact') { - return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessageCompact', { - defaultMessage: '{from} - {to}', + if (params.template === 'arrow_right') { + return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessageArrowRight', { + defaultMessage: '{from} -> {to}', values: { from: fromValue, to: toValue, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index 96da70f7abcf6..bf743da7aa105 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -199,7 +199,7 @@ export const rangeOperation: OperationDefinition Date: Thu, 15 Oct 2020 16:49:07 +0200 Subject: [PATCH 08/24] :lipstick: Restored append and prepend symbols --- .../definitions/ranges/advanced_editor.tsx | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx index 3efb5e9c69091..f9e4c5bea78f1 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx @@ -18,9 +18,10 @@ import { EuiLink, EuiText, EuiPopover, + EuiToolTip, htmlIdGenerator, + keys, } from '@elastic/eui'; -import { keys } from '@elastic/eui'; import { IFieldFormat } from '../../../../../../../../src/plugins/data/common'; import { RangeTypeLens, isValidRange, isValidNumber } from './ranges'; import { FROM_PLACEHOLDER, TO_PLACEHOLDER, TYPING_DEBOUNCE_TIME } from './constants'; @@ -68,6 +69,22 @@ export const RangePopover = ({ }; const { from, to, label } = tempRange; + const lteAppendLabel = i18n.translate('xpack.lens.indexPattern.ranges.lessThanOrEqualAppend', { + defaultMessage: '\u2264', + }); + const lteTooltipContent = i18n.translate( + 'xpack.lens.indexPattern.ranges.lessThanOrEqualTooltip', + { + defaultMessage: 'Less than or equal to', + } + ); + const ltPrependLabel = i18n.translate('xpack.lens.indexPattern.ranges.lessThanPrepend', { + defaultMessage: '\u003c', + }); + const ltTooltipContent = i18n.translate('xpack.lens.indexPattern.ranges.lessThanTooltip', { + defaultMessage: 'Less than', + }); + const onSubmit = () => { setIsPopoverOpen(false); setIsOpenByCreation(false); @@ -104,6 +121,11 @@ export const RangePopover = ({ setTempRange(newRange); saveRangeAndReset(newRange); }} + append={ + + {lteAppendLabel} + + } compressed placeholder={FROM_PLACEHOLDER} isInvalid={!isValidRange(tempRange)} @@ -124,6 +146,11 @@ export const RangePopover = ({ setTempRange(newRange); saveRangeAndReset(newRange); }} + prepend={ + + {ltPrependLabel} + + } compressed placeholder={TO_PLACEHOLDER} isInvalid={!isValidRange(tempRange)} From 68b1f46549073c62b7fe0066c47d5836ff3ea1e6 Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 15 Oct 2020 16:49:39 +0200 Subject: [PATCH 09/24] :lipstick: Picked better arrow symbol --- .../data/common/search/aggs/utils/get_format_with_aggs.test.ts | 2 +- .../data/common/search/aggs/utils/get_format_with_aggs.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts index f76d94adc191d..d41491a621403 100644 --- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts +++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts @@ -84,7 +84,7 @@ describe('getFormatWithAggs', () => { const getFieldFormat = getFormatWithAggs(getFormat); const format = getFieldFormat(mapping); - expect(format.convert({ gte: 1, lt: 20 })).toBe('1 -> 20'); + expect(format.convert({ gte: 1, lt: 20 })).toBe('1 → 20'); expect(getFormat).toHaveBeenCalledTimes(1); }); diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts index 482b0f47689f4..6b03dc5f70edc 100644 --- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts +++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.ts @@ -71,7 +71,7 @@ export function getFormatWithAggs(getFieldFormat: GetFieldFormat): GetFieldForma if (params.template === 'arrow_right') { return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessageArrowRight', { - defaultMessage: '{from} -> {to}', + defaultMessage: '{from} → {to}', values: { from: fromValue, to: toValue, From 6557b112654b2787f7c62aeb161201d22b823500 Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 15 Oct 2020 17:18:10 +0200 Subject: [PATCH 10/24] :lipstick: Add missing compressed attribute --- .../operations/definitions/ranges/advanced_editor.tsx | 1 + .../operations/definitions/shared_components/label_input.tsx | 3 +++ 2 files changed, 4 insertions(+) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx index f9e4c5bea78f1..c6773e806aef8 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.tsx @@ -179,6 +179,7 @@ export const RangePopover = ({ { defaultMessage: 'Custom label' } )} onSubmit={onSubmit} + compressed dataTestSubj="indexPattern-ranges-label" /> diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/label_input.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/label_input.tsx index 882169c0675b0..09a98d9e48ef7 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/label_input.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/shared_components/label_input.tsx @@ -16,6 +16,7 @@ export const LabelInput = ({ inputRef, onSubmit, dataTestSubj, + compressed, }: { value: string; onChange: (value: string) => void; @@ -23,6 +24,7 @@ export const LabelInput = ({ inputRef?: React.MutableRefObject; onSubmit?: () => void; dataTestSubj?: string; + compressed?: boolean; }) => { const [inputValue, setInputValue] = useState(value); @@ -57,6 +59,7 @@ export const LabelInput = ({ prepend={i18n.translate('xpack.lens.labelInput.label', { defaultMessage: 'Label', })} + compressed={compressed} /> ); }; From f664fb054395962c7faaf3e4ae7b8e58e9eedf8b Mon Sep 17 00:00:00 2001 From: dej611 Date: Fri, 16 Oct 2020 10:46:47 +0200 Subject: [PATCH 11/24] :white_check_mark: Add more tests for range params scenarios --- .../aggs/utils/get_format_with_aggs.test.ts | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts index d41491a621403..826e402b14682 100644 --- a/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts +++ b/src/plugins/data/common/search/aggs/utils/get_format_with_aggs.test.ts @@ -79,7 +79,7 @@ describe('getFormatWithAggs', () => { expect(getFormat).toHaveBeenCalledTimes(1); }); - test('creates alternative format for range', () => { + test('creates alternative format for range using the template parameter', () => { const mapping = { id: 'range', params: { template: 'arrow_right' } }; const getFieldFormat = getFormatWithAggs(getFormat); const format = getFieldFormat(mapping); @@ -88,6 +88,24 @@ describe('getFormatWithAggs', () => { expect(getFormat).toHaveBeenCalledTimes(1); }); + test('handles Infinity values internally when no nestedFormatter is passed', () => { + const mapping = { id: 'range', params: { replaceInfinity: true } }; + const getFieldFormat = getFormatWithAggs(getFormat); + const format = getFieldFormat(mapping); + + expect(format.convert({ gte: -Infinity, lt: Infinity })).toBe('≥ −∞ and < +∞'); + expect(getFormat).toHaveBeenCalledTimes(1); + }); + + test('lets Infinity values handling to nestedFormatter even when flag is on', () => { + const mapping = { id: 'range', params: { replaceInfinity: true, id: 'any' } }; + const getFieldFormat = getFormatWithAggs(getFormat); + const format = getFieldFormat(mapping); + + expect(format.convert({ gte: -Infinity, lt: Infinity })).toBe('≥ -Infinity and < Infinity'); + expect(getFormat).toHaveBeenCalledTimes(1); + }); + test('returns custom label for range if provided', () => { const mapping = { id: 'range', params: {} }; const getFieldFormat = getFormatWithAggs(getFormat); From fa51aa75775f2e296bb3dda6811352e5256a1e66 Mon Sep 17 00:00:00 2001 From: Marco Liberati Date: Fri, 16 Oct 2020 17:27:14 +0200 Subject: [PATCH 12/24] Update x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss Co-authored-by: Caroline Horn <549577+cchaos@users.noreply.github.com> --- .../operations/definitions/ranges/advanced_editor.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss index 2a7e8508ffa5a..4af490e7479da 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/advanced_editor.scss @@ -6,5 +6,5 @@ } .lnsRangesOperation__popoverNumberField { - width: 100px; -} \ No newline at end of file + width: 14ch; // Roughly 10 characters plus extra for the padding +} From 7b860ff47451ef956ed74d59aa8ab189375ba0bd Mon Sep 17 00:00:00 2001 From: dej611 Date: Mon, 19 Oct 2020 12:06:44 +0200 Subject: [PATCH 13/24] :ok_hand: Refactor Expression phase based on review feedback --- .../editor_frame_service/format_column.ts | 26 +++++++++---------- .../indexpattern_datasource/to_expression.ts | 15 ++++++++--- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/format_column.ts b/x-pack/plugins/lens/public/editor_frame_service/format_column.ts index 44ad314060fef..e1e0e6b7da514 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/format_column.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/format_column.ts @@ -10,9 +10,7 @@ interface FormatColumn { format: string; columnId: string; decimals?: number; - nestedFormat?: string; - template?: string; - replaceInfinity?: boolean; + rangeParameters?: string; } export const supportedFormats: Record< @@ -69,25 +67,27 @@ export const formatColumn: ExpressionFunctionDefinition< types: ['number'], help: '', }, - nestedFormat: { + rangeParameters: { types: ['string'], help: '', }, - template: { - types: ['string'], - help: '', - }, - replaceInfinity: { - types: ['boolean'], - help: '', - }, }, inputTypes: ['datatable'], - fn(input, { format, columnId, decimals, nestedFormat, template, replaceInfinity }: FormatColumn) { + fn(input, { format, columnId, decimals, rangeParameters }: FormatColumn) { return { ...input, columns: input.columns.map((col) => { if (col.id === columnId) { + const { + template, + replaceInfinity, + nestedFormat, + }: { + template?: string; + replaceInfinity?: boolean; + nestedFormat?: string; + } = rangeParameters ? JSON.parse(rangeParameters) : {}; + const extraParams = { template, replaceInfinity }; if (supportedFormats[format]) { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index 7a0883b77ea7c..ccbce47465fcd 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -98,27 +98,36 @@ function getExpressionForLayer( arguments: { format: [(format || parentFormat!).id], // either one of the two is available columnId: [id], + rangeParameters: [''], }, }; // Handle nested formatter carefully one parameter at the time + const rangeParameters: Record = {}; + if (parentFormat) { base.arguments.format = [parentFormat.id]; if (format) { - base.arguments.nestedFormat = [format.id]; + rangeParameters.nestedFormat = format.id; } } if (parentFormat?.params?.template) { - base.arguments.template = [parentFormat.params?.template]; + rangeParameters.template = parentFormat.params?.template; } if (parentFormat?.params?.replaceInfinity) { - base.arguments.replaceInfinity = [parentFormat.params?.replaceInfinity]; + rangeParameters.replaceInfinity = parentFormat.params?.replaceInfinity; } if (typeof format?.params?.decimals === 'number') { base.arguments.decimals = [format.params.decimals]; } + + // To avoid mapping too many ad-hoc arguments, this passes thru a JSON serialization + if (Object.keys(rangeParameters).length) { + base.arguments.rangeParameters = [JSON.stringify(rangeParameters)]; + } + return base; } ); From a6dff88530592714c7de96f61a5dc1edaab7f5c9 Mon Sep 17 00:00:00 2001 From: dej611 Date: Mon, 19 Oct 2020 17:54:15 +0200 Subject: [PATCH 14/24] :bug: Fix default formatter handling --- .../operations/definitions/ranges/ranges.tsx | 27 ++++++++++++++++--- .../indexpattern_datasource/to_expression.ts | 7 +++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index bf743da7aa105..472072b5479d0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -15,6 +15,7 @@ import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { updateColumnParam, changeColumn } from '../../../state_helpers'; import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants'; +import { IndexPattern, IndexPatternField } from '../../../types'; type RangeType = Omit; // Try to cover all possible serialized states for ranges @@ -61,6 +62,21 @@ export const isValidRange = (range: RangeTypeLens): boolean => { return true; }; +function getFieldDefaultFormat( + indexPattern: IndexPattern, + field: IndexPatternField | undefined +): RangeIndexPatternColumn['params']['format'] { + if (field) { + if (field.format) { + return field.format; + } + if (indexPattern.fieldFormatMap && indexPattern.fieldFormatMap[field.name]) { + return indexPattern.fieldFormatMap[field.name] as RangeIndexPatternColumn['params']['format']; + } + } + return undefined; +} + function getEsAggsParams({ sourceField, params }: RangeIndexPatternColumn) { if (params.type === MODES.Range) { return { @@ -111,7 +127,7 @@ export const rangeOperation: OperationDefinition { - const numberFormat = currentColumn.params.format as + const indexPattern = state.indexPatterns[state.layers[layerId].indexPatternId]; + const currentField = indexPattern.fields.find( + (field) => field.name === currentColumn.sourceField + ); + const numberFormat = (currentColumn.params.format || + getFieldDefaultFormat(indexPattern, currentField)) as | undefined | { id: string; @@ -166,7 +187,7 @@ export const rangeOperation: OperationDefinition field.name === col.sourceField); + // If a format override is present use it, + // otherwise pick from either the field or indexPattern the set formatter when available if (format) { rangeParameters.nestedFormat = format.id; + } else if (currentField) { + const indexPatternFieldFormatter = indexPattern.fieldFormatMap?.[currentField.name]; + if (indexPatternFieldFormatter || currentField.format) + rangeParameters.nestedFormat = (indexPatternFieldFormatter || currentField.format).id; } } if (parentFormat?.params?.template) { From 8395a503d8b522b9cc7c1620c84fac2ad9fb2711 Mon Sep 17 00:00:00 2001 From: dej611 Date: Mon, 19 Oct 2020 17:54:59 +0200 Subject: [PATCH 15/24] :white_check_mark: Add test for default and override formatter scenarios --- .../definitions/ranges/ranges.test.tsx | 90 +++++++++++++++++-- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx index 5317ee913fcdd..86e9a7537cbd3 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx @@ -7,7 +7,14 @@ import React from 'react'; import { mount } from 'enzyme'; import { act } from 'react-dom/test-utils'; -import { EuiFieldNumber, EuiRange, EuiButtonEmpty, EuiLink } from '@elastic/eui'; +import { + EuiFieldNumber, + EuiRange, + EuiButtonEmpty, + EuiLink, + EuiText, + EuiFieldText, +} from '@elastic/eui'; import { IUiSettingsClient, SavedObjectsClientContract, HttpSetup } from 'kibana/public'; import { IStorageWrapper } from 'src/plugins/kibana_utils/public'; import { IndexPatternPrivateState, IndexPattern } from '../../../types'; @@ -23,12 +30,21 @@ import { } from './constants'; import { RangePopover } from './advanced_editor'; import { DragDropBuckets } from '../shared_components'; -import { EuiFieldText } from '@elastic/eui'; const dataPluginMockValue = dataPluginMock.createStartContract(); // need to overwrite the formatter field first -dataPluginMockValue.fieldFormats.deserialize = jest.fn().mockImplementation(() => { - return { convert: ({ gte, lt }: { gte: string; lt: string }) => `${gte} - ${lt}` }; +dataPluginMockValue.fieldFormats.deserialize = jest.fn().mockImplementation(({ params }) => { + return { + convert: ({ gte, lt }: { gte: string; lt: string }) => { + if (params?.id === 'custom') { + return `Custom format: ${gte} - ${lt}`; + } + if (params?.id === 'bytes') { + return `Bytes format: ${gte} - ${lt}`; + } + return `${gte} - ${lt}`; + }, + }; }); type ReactMouseEvent = React.MouseEvent & @@ -74,7 +90,14 @@ describe('ranges', () => { function getDefaultState(): IndexPatternPrivateState { return { indexPatternRefs: [], - indexPatterns: {}, + indexPatterns: { + '1': { + id: '1', + title: 'my_index_pattern', + hasRestrictions: false, + fields: [{ name: sourceField, type: 'number', displayName: sourceField }], + }, + }, existingFields: {}, currentIndexPatternId: '1', isFirstExistenceFetch: false, @@ -396,7 +419,7 @@ describe('ranges', () => { /> ); - // This series of act clojures are made to make it work properly the update flush + // This series of act closures are made to make it work properly the update flush act(() => { instance.find(EuiButtonEmpty).prop('onClick')!({} as ReactMouseEvent); }); @@ -453,7 +476,7 @@ describe('ranges', () => { /> ); - // This series of act clojures are made to make it work properly the update flush + // This series of act closures are made to make it work properly the update flush act(() => { instance.find(EuiButtonEmpty).prop('onClick')!({} as ReactMouseEvent); }); @@ -510,7 +533,7 @@ describe('ranges', () => { /> ); - // This series of act clojures are made to make it work properly the update flush + // This series of act closures are made to make it work properly the update flush act(() => { instance.find(RangePopover).find(EuiLink).prop('onClick')!({} as ReactMouseEvent); }); @@ -667,6 +690,57 @@ describe('ranges', () => { ); }); }); + + it('should correctly handle the default formatter for the field', () => { + const setStateSpy = jest.fn(); + + // set a default formatter for the sourceField used + state.indexPatterns['1'].fieldFormatMap = { + MyField: { id: 'custom', params: {} }, + }; + + const instance = mount( + + ); + + expect(instance.find(RangePopover).find(EuiText).prop('children')).toMatch( + /^Custom format:/ + ); + }); + + it('should correctly pick the dimension formatter for the field', () => { + const setStateSpy = jest.fn(); + + // set a default formatter for the sourceField used + state.indexPatterns['1'].fieldFormatMap = { + MyField: { id: 'custom', params: {} }, + }; + + // now set a format on the range operation + state.layers.first.columns.col1.params.format = { id: 'bytes', params: {} }; + + const instance = mount( + + ); + + expect(instance.find(RangePopover).find(EuiText).prop('children')).toMatch( + /^Bytes format:/ + ); + }); }); }); }); From fd2ddfeb0b4fbac02986e659d03b5acfae5e1e62 Mon Sep 17 00:00:00 2001 From: dej611 Date: Mon, 19 Oct 2020 18:43:59 +0200 Subject: [PATCH 16/24] :label: Fix type check --- .../operations/definitions/ranges/ranges.test.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx index 86e9a7537cbd3..c8a8ffa7b128f 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.test.tsx @@ -724,7 +724,10 @@ describe('ranges', () => { }; // now set a format on the range operation - state.layers.first.columns.col1.params.format = { id: 'bytes', params: {} }; + (state.layers.first.columns.col1 as RangeIndexPatternColumn).params.format = { + id: 'bytes', + params: { decimals: 0 }, + }; const instance = mount( Date: Wed, 21 Oct 2020 10:47:34 +0200 Subject: [PATCH 17/24] :bug: Fix decimals inconsistency with range parameters in specific case --- .../lens/public/indexpattern_datasource/to_expression.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index 302b166302903..485a16f74d167 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -133,6 +133,10 @@ function getExpressionForLayer( // To avoid mapping too many ad-hoc arguments, this passes thru a JSON serialization if (Object.keys(rangeParameters).length) { base.arguments.rangeParameters = [JSON.stringify(rangeParameters)]; + // this is a special scenario for ranges, where we need to enforce a 0 digit decimals + if (base.arguments.decimals == null) { + base.arguments.decimals = [0]; + } } return base; From e265b950f3e83ec36f8197708cd12ac53c7bfa12 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 21 Oct 2020 12:37:57 +0200 Subject: [PATCH 18/24] pass through index pattern format unchanged if no Lens format is specificed --- .../editor_frame_service/format_column.ts | 15 ++++++++++++ .../public/indexpattern_datasource/loader.ts | 4 +++- .../operations/definitions/ranges/ranges.tsx | 24 ++++++------------- .../indexpattern_datasource/to_expression.ts | 12 +--------- 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/lens/public/editor_frame_service/format_column.ts b/x-pack/plugins/lens/public/editor_frame_service/format_column.ts index e1e0e6b7da514..430e74097c7bc 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/format_column.ts +++ b/x-pack/plugins/lens/public/editor_frame_service/format_column.ts @@ -120,6 +120,21 @@ export const formatColumn: ExpressionFunctionDefinition< }, }; } + if (rangeParameters) { + return { + ...col, + meta: { + ...col.meta, + params: { + id: format, + params: { + ...col.meta.params?.params, + ...extraParams, + }, + }, + }, + }; + } return { ...col, meta: { diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index fd8e071d524ee..ef2a0161523ec 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -103,7 +103,9 @@ export async function loadIndexPatterns({ id: indexPattern.id!, // id exists for sure because we got index patterns by id title, timeFieldName, - fieldFormatMap, + fieldFormatMap: Object.fromEntries( + Object.entries(fieldFormatMap).map(([id, format]) => [id, format.toJSON()]) + ), fields: newFields, hasRestrictions: !!typeMeta?.aggs, }; diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index 472072b5479d0..efe3495f1d9f5 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -62,16 +62,10 @@ export const isValidRange = (range: RangeTypeLens): boolean => { return true; }; -function getFieldDefaultFormat( - indexPattern: IndexPattern, - field: IndexPatternField | undefined -): RangeIndexPatternColumn['params']['format'] { +function getFieldDefaultFormat(indexPattern: IndexPattern, field: IndexPatternField | undefined) { if (field) { - if (field.format) { - return field.format; - } if (indexPattern.fieldFormatMap && indexPattern.fieldFormatMap[field.name]) { - return indexPattern.fieldFormatMap[field.name] as RangeIndexPatternColumn['params']['format']; + return indexPattern.fieldFormatMap[field.name]; } } return undefined; @@ -177,23 +171,19 @@ export const rangeOperation: OperationDefinition field.name === currentColumn.sourceField ); - const numberFormat = (currentColumn.params.format || - getFieldDefaultFormat(indexPattern, currentField)) as - | undefined - | { - id: string; - params: { decimals: number }; - }; + const numberFormat = currentColumn.params.format; const numberFormatterPattern = numberFormat && supportedFormats[numberFormat.id] && - supportedFormats[numberFormat.id].decimalsToPattern(numberFormat.params.decimals || 0); + supportedFormats[numberFormat.id].decimalsToPattern(numberFormat.params?.decimals || 0); const rangeFormatter = data.fieldFormats.deserialize({ ...currentColumn.params.parentFormat, params: { ...currentColumn.params.parentFormat?.params, - ...(numberFormat && { id: numberFormat.id, params: { pattern: numberFormatterPattern } }), + ...(numberFormat + ? { id: numberFormat.id, params: { pattern: numberFormatterPattern } } + : getFieldDefaultFormat(indexPattern, currentField)), }, }); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index 485a16f74d167..a6f2fb6e90f78 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -107,15 +107,9 @@ function getExpressionForLayer( if (parentFormat) { base.arguments.format = [parentFormat.id]; - const currentField = indexPattern.fields.find((field) => field.name === col.sourceField); - // If a format override is present use it, - // otherwise pick from either the field or indexPattern the set formatter when available + // If a format override is present use it if (format) { rangeParameters.nestedFormat = format.id; - } else if (currentField) { - const indexPatternFieldFormatter = indexPattern.fieldFormatMap?.[currentField.name]; - if (indexPatternFieldFormatter || currentField.format) - rangeParameters.nestedFormat = (indexPatternFieldFormatter || currentField.format).id; } } if (parentFormat?.params?.template) { @@ -133,10 +127,6 @@ function getExpressionForLayer( // To avoid mapping too many ad-hoc arguments, this passes thru a JSON serialization if (Object.keys(rangeParameters).length) { base.arguments.rangeParameters = [JSON.stringify(rangeParameters)]; - // this is a special scenario for ranges, where we need to enforce a 0 digit decimals - if (base.arguments.decimals == null) { - base.arguments.decimals = [0]; - } } return base; From 04b391a90c3ddac33f423e6658b125ad9a3122fb Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 22 Oct 2020 10:59:25 +0200 Subject: [PATCH 19/24] :bug: Fix fieldFormatMap serialization issue --- .../plugins/lens/public/indexpattern_datasource/loader.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index ef2a0161523ec..314a18b385282 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -103,9 +103,11 @@ export async function loadIndexPatterns({ id: indexPattern.id!, // id exists for sure because we got index patterns by id title, timeFieldName, - fieldFormatMap: Object.fromEntries( - Object.entries(fieldFormatMap).map(([id, format]) => [id, format.toJSON()]) - ), + fieldFormatMap: + fieldFormatMap && + Object.fromEntries( + Object.entries(fieldFormatMap).map(([id, format]) => [id, format.toJSON()]) + ), fields: newFields, hasRestrictions: !!typeMeta?.aggs, }; From 7a93683e475a342db0473f6a224eabd6eaebac82 Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 22 Oct 2020 16:09:11 +0200 Subject: [PATCH 20/24] :white_check_mark: Add fieldformatMap field to test edge cases --- .../lens/public/indexpattern_datasource/loader.test.ts | 1 + x-pack/plugins/lens/public/indexpattern_datasource/loader.ts | 4 +++- x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index 06cfdf7e03481..77d1a4e7e6a88 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -117,6 +117,7 @@ const indexPattern2 = ({ title: 'my-fake-restricted-pattern', timeFieldName: 'timestamp', hasRestrictions: true, + fieldFormatMap: {}, fields: [ { name: 'timestamp', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index 314a18b385282..3567c484e100e 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -106,7 +106,9 @@ export async function loadIndexPatterns({ fieldFormatMap: fieldFormatMap && Object.fromEntries( - Object.entries(fieldFormatMap).map(([id, format]) => [id, format.toJSON()]) + Object.entries(fieldFormatMap) + .filter(([id, format]) => format) + .map(([id, format]) => [id, format.toJSON()]) ), fields: newFields, hasRestrictions: !!typeMeta?.aggs, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts b/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts index 21ed23321cf57..fb9767d2b3fad 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts @@ -82,6 +82,7 @@ export const createMockedRestrictedIndexPattern = () => ({ title: 'my-fake-restricted-pattern', timeFieldName: 'timestamp', hasRestrictions: true, + fieldFormatMap: { bytes: null }, fields: [ { name: 'timestamp', From 456a80bb0dede1e6b98448d4fe75f6cbcc3b523c Mon Sep 17 00:00:00 2001 From: dej611 Date: Thu, 22 Oct 2020 17:40:38 +0200 Subject: [PATCH 21/24] :bug: Better handling to support already serialized formatters --- .../lens/public/indexpattern_datasource/loader.test.ts | 2 +- .../plugins/lens/public/indexpattern_datasource/loader.ts | 7 ++++--- .../plugins/lens/public/indexpattern_datasource/mocks.ts | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts index 77d1a4e7e6a88..4222c02388433 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.test.ts @@ -117,7 +117,7 @@ const indexPattern2 = ({ title: 'my-fake-restricted-pattern', timeFieldName: 'timestamp', hasRestrictions: true, - fieldFormatMap: {}, + fieldFormatMap: { bytes: { id: 'bytes', params: { pattern: '0.0' } } }, fields: [ { name: 'timestamp', diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts index 3567c484e100e..70079cce6cc46 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/loader.ts @@ -106,9 +106,10 @@ export async function loadIndexPatterns({ fieldFormatMap: fieldFormatMap && Object.fromEntries( - Object.entries(fieldFormatMap) - .filter(([id, format]) => format) - .map(([id, format]) => [id, format.toJSON()]) + Object.entries(fieldFormatMap).map(([id, format]) => [ + id, + 'toJSON' in format ? format.toJSON() : format, + ]) ), fields: newFields, hasRestrictions: !!typeMeta?.aggs, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts b/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts index fb9767d2b3fad..744a9f6743d09 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/mocks.ts @@ -82,7 +82,7 @@ export const createMockedRestrictedIndexPattern = () => ({ title: 'my-fake-restricted-pattern', timeFieldName: 'timestamp', hasRestrictions: true, - fieldFormatMap: { bytes: null }, + fieldFormatMap: { bytes: { id: 'bytes', params: { pattern: '0.0' } } }, fields: [ { name: 'timestamp', From 624105582dd2c109a28838ec01bc4dfbfed05c59 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Thu, 22 Oct 2020 19:05:17 -0400 Subject: [PATCH 22/24] Organize the formatting differently --- .../editor_frame_service/format_column.ts | 153 ------------------ .../public/editor_frame_service/service.tsx | 2 - .../indexpattern_datasource/format_column.ts | 137 ++++++++++++++++ .../public/indexpattern_datasource/index.ts | 2 + .../operations/definitions/ranges/ranges.tsx | 4 +- .../indexpattern_datasource/to_expression.ts | 33 +--- 6 files changed, 145 insertions(+), 186 deletions(-) delete mode 100644 x-pack/plugins/lens/public/editor_frame_service/format_column.ts create mode 100644 x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts diff --git a/x-pack/plugins/lens/public/editor_frame_service/format_column.ts b/x-pack/plugins/lens/public/editor_frame_service/format_column.ts deleted file mode 100644 index 430e74097c7bc..0000000000000 --- a/x-pack/plugins/lens/public/editor_frame_service/format_column.ts +++ /dev/null @@ -1,153 +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 { ExpressionFunctionDefinition, Datatable } from 'src/plugins/expressions/public'; - -interface FormatColumn { - format: string; - columnId: string; - decimals?: number; - rangeParameters?: string; -} - -export const supportedFormats: Record< - string, - { decimalsToPattern: (decimals?: number) => string } -> = { - number: { - decimalsToPattern: (decimals = 2) => { - if (decimals === 0) { - return `0,0`; - } - return `0,0.${'0'.repeat(decimals)}`; - }, - }, - percent: { - decimalsToPattern: (decimals = 2) => { - if (decimals === 0) { - return `0,0%`; - } - return `0,0.${'0'.repeat(decimals)}%`; - }, - }, - bytes: { - decimalsToPattern: (decimals = 2) => { - if (decimals === 0) { - return `0,0b`; - } - return `0,0.${'0'.repeat(decimals)}b`; - }, - }, -}; - -export const formatColumn: ExpressionFunctionDefinition< - 'lens_format_column', - Datatable, - FormatColumn, - Datatable -> = { - name: 'lens_format_column', - type: 'datatable', - help: '', - args: { - format: { - types: ['string'], - help: '', - required: true, - }, - columnId: { - types: ['string'], - help: '', - required: true, - }, - decimals: { - types: ['number'], - help: '', - }, - rangeParameters: { - types: ['string'], - help: '', - }, - }, - inputTypes: ['datatable'], - fn(input, { format, columnId, decimals, rangeParameters }: FormatColumn) { - return { - ...input, - columns: input.columns.map((col) => { - if (col.id === columnId) { - const { - template, - replaceInfinity, - nestedFormat, - }: { - template?: string; - replaceInfinity?: boolean; - nestedFormat?: string; - } = rangeParameters ? JSON.parse(rangeParameters) : {}; - - const extraParams = { template, replaceInfinity }; - - if (supportedFormats[format]) { - return { - ...col, - meta: { - ...col.meta, - params: { - id: format, - params: { pattern: supportedFormats[format].decimalsToPattern(decimals) }, - }, - }, - }; - } - if (nestedFormat && supportedFormats[nestedFormat]) { - return { - ...col, - meta: { - ...col.meta, - params: { - id: format, - params: { - id: nestedFormat, - params: { - pattern: supportedFormats[nestedFormat].decimalsToPattern(decimals), - }, - ...extraParams, - }, - }, - }, - }; - } - if (rangeParameters) { - return { - ...col, - meta: { - ...col.meta, - params: { - id: format, - params: { - ...col.meta.params?.params, - ...extraParams, - }, - }, - }, - }; - } - return { - ...col, - meta: { - ...col.meta, - params: { - id: format, - params: { ...extraParams }, - }, - }, - }; - } - return col; - }), - }; - }, -}; diff --git a/x-pack/plugins/lens/public/editor_frame_service/service.tsx b/x-pack/plugins/lens/public/editor_frame_service/service.tsx index e2a382133cb3c..d1df63780594e 100644 --- a/x-pack/plugins/lens/public/editor_frame_service/service.tsx +++ b/x-pack/plugins/lens/public/editor_frame_service/service.tsx @@ -23,7 +23,6 @@ import { } from '../types'; import { Document } from '../persistence/saved_object_store'; import { mergeTables } from './merge_tables'; -import { formatColumn } from './format_column'; import { EmbeddableFactory, LensEmbeddableStartServices } from './embeddable/embeddable_factory'; import { UiActionsStart } from '../../../../../src/plugins/ui_actions/public'; import { DashboardStart } from '../../../../../src/plugins/dashboard/public'; @@ -86,7 +85,6 @@ export class EditorFrameService { getAttributeService: () => Promise ): EditorFrameSetup { plugins.expressions.registerFunction(() => mergeTables); - plugins.expressions.registerFunction(() => formatColumn); const getStartServices = async (): Promise => { const [coreStart, deps] = await core.getStartServices(); diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts b/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts new file mode 100644 index 0000000000000..3666528f43166 --- /dev/null +++ b/x-pack/plugins/lens/public/indexpattern_datasource/format_column.ts @@ -0,0 +1,137 @@ +/* + * 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 { + ExpressionFunctionDefinition, + Datatable, + DatatableColumn, +} from 'src/plugins/expressions/public'; + +interface FormatColumn { + format: string; + columnId: string; + decimals?: number; + parentFormat?: string; +} + +export const supportedFormats: Record< + string, + { decimalsToPattern: (decimals?: number) => string } +> = { + number: { + decimalsToPattern: (decimals = 2) => { + if (decimals === 0) { + return `0,0`; + } + return `0,0.${'0'.repeat(decimals)}`; + }, + }, + percent: { + decimalsToPattern: (decimals = 2) => { + if (decimals === 0) { + return `0,0%`; + } + return `0,0.${'0'.repeat(decimals)}%`; + }, + }, + bytes: { + decimalsToPattern: (decimals = 2) => { + if (decimals === 0) { + return `0,0b`; + } + return `0,0.${'0'.repeat(decimals)}b`; + }, + }, +}; + +export const formatColumn: ExpressionFunctionDefinition< + 'lens_format_column', + Datatable, + FormatColumn, + Datatable +> = { + name: 'lens_format_column', + type: 'datatable', + help: '', + args: { + format: { + types: ['string'], + help: '', + required: true, + }, + columnId: { + types: ['string'], + help: '', + required: true, + }, + decimals: { + types: ['number'], + help: '', + }, + parentFormat: { + types: ['string'], + help: '', + }, + }, + inputTypes: ['datatable'], + fn(input, { format, columnId, decimals, parentFormat }: FormatColumn) { + return { + ...input, + columns: input.columns.map((col) => { + if (col.id === columnId) { + if (!parentFormat) { + if (supportedFormats[format]) { + return withParams(col, { + id: format, + params: { pattern: supportedFormats[format].decimalsToPattern(decimals) }, + }); + } else if (format) { + return withParams(col, { id: format }); + } else { + return col; + } + } + + const parsedParentFormat = JSON.parse(parentFormat); + const parentFormatId = parsedParentFormat.id; + const parentFormatParams = parsedParentFormat.params ?? {}; + + if (!parentFormatId) { + return col; + } + + if (format && supportedFormats[format]) { + return withParams(col, { + id: parentFormatId, + params: { + id: format, + params: { + pattern: supportedFormats[format].decimalsToPattern(decimals), + }, + ...parentFormatParams, + }, + }); + } + if (parentFormatParams) { + const innerParams = (col.meta.params?.params as Record) ?? {}; + return withParams(col, { + ...col.meta.params, + params: { + ...innerParams, + ...parentFormatParams, + }, + }); + } + } + return col; + }), + }; + }, +}; + +function withParams(col: DatatableColumn, params: Record) { + return { ...col, meta: { ...col.meta, params } }; +} diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts index 4fbed04112632..c074cfc55b2d0 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts @@ -13,6 +13,7 @@ import { DataPublicPluginStart, } from '../../../../../src/plugins/data/public'; import { Datasource, EditorFrameSetup } from '../types'; +import { formatColumn } from './format_column'; export interface IndexPatternDatasourceSetupPlugins { expressions: ExpressionsSetup; @@ -35,6 +36,7 @@ export class IndexPatternDatasource { editorFrame.registerDatasource(async () => { const { getIndexPatternDatasource, renameColumns } = await import('../async_services'); expressions.registerFunction(renameColumns); + expressions.registerFunction(formatColumn); return core.getStartServices().then(([coreStart, { data }]) => getIndexPatternDatasource({ core: coreStart, diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx index efe3495f1d9f5..1050eef45a71c 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/operations/definitions/ranges/ranges.tsx @@ -7,13 +7,13 @@ import React from 'react'; import { i18n } from '@kbn/i18n'; -import { supportedFormats } from '../../../../editor_frame_service/format_column'; import { UI_SETTINGS } from '../../../../../../../../src/plugins/data/common'; import { Range } from '../../../../../../../../src/plugins/expressions/common/expression_types/index'; import { RangeEditor } from './range_editor'; import { OperationDefinition } from '../index'; import { FieldBasedIndexPatternColumn } from '../column_types'; import { updateColumnParam, changeColumn } from '../../../state_helpers'; +import { supportedFormats } from '../../../format_column'; import { MODES, AUTO_BARS, DEFAULT_INTERVAL, MIN_HISTOGRAM_BARS, SLICES } from './constants'; import { IndexPattern, IndexPatternField } from '../../../types'; @@ -37,7 +37,7 @@ export interface RangeIndexPatternColumn extends FieldBasedIndexPatternColumn { format?: { id: string; params?: { decimals: number } }; parentFormat?: { id: string; - params?: { template?: string; id?: string; replaceInfinity?: boolean }; + params?: { id?: string; template?: string; replaceInfinity?: boolean }; }; }; } diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts index a6f2fb6e90f78..e2c4323b56c2a 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/to_expression.ts @@ -92,43 +92,18 @@ function getExpressionForLayer( // TODO: improve the type handling here const parentFormat = 'parentFormat' in col.params ? col.params!.parentFormat! : undefined; const format = (col as FormattedColumn).params!.format; + const base: ExpressionFunctionAST = { type: 'function', function: 'lens_format_column', arguments: { - format: [(format || parentFormat!).id], // either one of the two is available + format: format ? [format.id] : [''], columnId: [id], - rangeParameters: [''], + decimals: typeof format?.params?.decimals === 'number' ? [format.params.decimals] : [], + parentFormat: parentFormat ? [JSON.stringify(parentFormat)] : [], }, }; - // Handle nested formatter carefully one parameter at the time - const rangeParameters: Record = {}; - - if (parentFormat) { - base.arguments.format = [parentFormat.id]; - // If a format override is present use it - if (format) { - rangeParameters.nestedFormat = format.id; - } - } - if (parentFormat?.params?.template) { - rangeParameters.template = parentFormat.params?.template; - } - - if (parentFormat?.params?.replaceInfinity) { - rangeParameters.replaceInfinity = parentFormat.params?.replaceInfinity; - } - - if (typeof format?.params?.decimals === 'number') { - base.arguments.decimals = [format.params.decimals]; - } - - // To avoid mapping too many ad-hoc arguments, this passes thru a JSON serialization - if (Object.keys(rangeParameters).length) { - base.arguments.rangeParameters = [JSON.stringify(rangeParameters)]; - } - return base; } ); From 6e249975744b707c16f4b57e66fa99df37a7e430 Mon Sep 17 00:00:00 2001 From: dej611 Date: Tue, 27 Oct 2020 09:28:42 +0100 Subject: [PATCH 23/24] :wrench: Raise the limit for data plugin by 5kb --- packages/kbn-optimizer/limits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index fd0be15affab3..846388d0b08d1 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -14,7 +14,7 @@ pageLoadAssetSize: dashboard: 374194 dashboardEnhanced: 65646 dashboardMode: 22716 - data: 1170713 + data: 1175833 dataEnhanced: 50420 devTools: 38637 discover: 105145 From adc0ff32ffeb54210222b9850eb79f452db6c34a Mon Sep 17 00:00:00 2001 From: dej611 Date: Tue, 27 Oct 2020 14:53:00 +0100 Subject: [PATCH 24/24] :recycle: Refactor to use async_services --- x-pack/plugins/lens/public/indexpattern_datasource/index.ts | 5 +++-- .../lens/public/indexpattern_datasource/indexpattern.tsx | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts index c074cfc55b2d0..35987656f6670 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/index.ts +++ b/x-pack/plugins/lens/public/indexpattern_datasource/index.ts @@ -13,7 +13,6 @@ import { DataPublicPluginStart, } from '../../../../../src/plugins/data/public'; import { Datasource, EditorFrameSetup } from '../types'; -import { formatColumn } from './format_column'; export interface IndexPatternDatasourceSetupPlugins { expressions: ExpressionsSetup; @@ -34,7 +33,9 @@ export class IndexPatternDatasource { { expressions, editorFrame, charts }: IndexPatternDatasourceSetupPlugins ) { editorFrame.registerDatasource(async () => { - const { getIndexPatternDatasource, renameColumns } = await import('../async_services'); + const { getIndexPatternDatasource, renameColumns, formatColumn } = await import( + '../async_services' + ); expressions.registerFunction(renameColumns); expressions.registerFunction(formatColumn); return core.getStartServices().then(([coreStart, { data }]) => diff --git a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx index 28aeac223e4a6..a6edfd71c93ca 100644 --- a/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx +++ b/x-pack/plugins/lens/public/indexpattern_datasource/indexpattern.tsx @@ -107,6 +107,7 @@ export function uniqueLabels(layers: Record) { } export * from './rename_columns'; +export * from './format_column'; export function getIndexPatternDatasource({ core,