From 6a1a77e42c7f029c66ed2ed889dd21e10f7b2d2f Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Thu, 2 Jan 2020 13:21:32 -0500 Subject: [PATCH 01/23] [Formatters] Add new field formatters --- .../kibana/migrations/migrations.js | 27 +- .../kibana/ui_setting_defaults.js | 2 +- .../editors/bytes/bytes.js | 23 +- .../editors/currency/currency.js | 124 +++++++ .../editors/currency/index.js | 20 ++ .../editors/default_number/index.js | 20 ++ .../editors/default_number/number.js | 312 ++++++++++++++++++ .../editors/large_number/index.js | 20 ++ .../editors/large_number/large_number.js | 47 +++ .../editors/number/index.js | 2 +- .../editors/number/number.js | 88 ++++- .../editors/percent/percent.js | 18 +- .../field_format_editor/get_editors.js | 50 +++ .../components/field_format_editor/index.js | 1 + .../field_format_editor/register.js | 24 +- .../field_formats/converters/boolean.ts | 5 +- .../common/field_formats/converters/bytes.ts | 5 +- .../common/field_formats/converters/color.ts | 5 +- .../field_formats/converters/currency.ts | 46 +++ .../common/field_formats/converters/date.ts | 5 +- .../field_formats/converters/date_nanos.ts | 5 +- .../field_formats/converters/date_server.ts | 5 +- .../converters/default_number.ts | 45 +++ .../field_formats/converters/duration.ts | 11 +- .../common/field_formats/converters/index.ts | 5 +- .../converters/intl_number_format.ts | 60 ++++ .../common/field_formats/converters/ip.ts | 5 +- .../field_formats/converters/large_number.ts | 37 +++ .../field_formats/converters/number.test.ts | 6 +- .../common/field_formats/converters/number.ts | 13 +- .../field_formats/converters/numeral.ts | 14 +- .../field_formats/converters/percent.ts | 26 +- .../field_formats/converters/relative_date.ts | 5 +- .../field_formats/converters/static_lookup.ts | 5 +- .../common/field_formats/converters/string.ts | 4 +- .../field_formats/converters/truncate.ts | 5 +- .../common/field_formats/converters/url.ts | 4 +- .../data/common/field_formats/types.ts | 5 +- .../field_formats_service.ts | 10 +- src/test_utils/public/stub_field_formats.ts | 8 +- 40 files changed, 1040 insertions(+), 82 deletions(-) create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/index.js create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/index.js create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/number.js create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/index.js create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/large_number.js create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js create mode 100644 src/plugins/data/common/field_formats/converters/currency.ts create mode 100644 src/plugins/data/common/field_formats/converters/default_number.ts create mode 100644 src/plugins/data/common/field_formats/converters/intl_number_format.ts create mode 100644 src/plugins/data/common/field_formats/converters/large_number.ts diff --git a/src/legacy/core_plugins/kibana/migrations/migrations.js b/src/legacy/core_plugins/kibana/migrations/migrations.js index 29b6e632d19fd..9ba5fa65df87c 100644 --- a/src/legacy/core_plugins/kibana/migrations/migrations.js +++ b/src/legacy/core_plugins/kibana/migrations/migrations.js @@ -484,6 +484,31 @@ function migrateSubTypeAndParentFieldProperties(doc) { }; } +function migrateNumeralFieldFormatters(doc) { + if (!doc.attributes.fieldFormatMap) return doc; + + const fieldFormatMap = doc.attributes.fieldFormatMap; + const fieldFormats = JSON.parse(fieldFormatMap); + // const migratedFields = fields.map(field => { + // if (field.subType === 'multi') { + // return { + // ...omit(field, 'parent'), + // subType: { multi: { parent: field.parent } }, + // }; + // } + + // return field; + // }); + + return { + ...doc, + attributes: { + ...doc.attributes, + fieldFormatMap: JSON.stringify(fieldFormats), + }, + }; +} + const executeMigrations720 = flow( migratePercentileRankAggregation, migrateDateHistogramAggregation @@ -508,7 +533,7 @@ export const migrations = { doc.attributes.typeMeta = doc.attributes.typeMeta || undefined; return doc; }, - '7.6.0': flow(migrateSubTypeAndParentFieldProperties), + '7.6.0': flow(migrateSubTypeAndParentFieldProperties, migrateNumeralFieldFormatters), }, visualization: { /** diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js index 196d9662f8b15..1cea84d19b85d 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js @@ -702,7 +702,7 @@ export function getUiSettingDefaults() { "ip": { "id": "ip", "params": {} }, "date": { "id": "date", "params": {} }, "date_nanos": { "id": "date_nanos", "params": {}, "es": true }, - "number": { "id": "number", "params": {} }, + "number": { "id": "default_number", "params": {} }, "boolean": { "id": "boolean", "params": {} }, "_source": { "id": "_source", "params": {} }, "_default_": { "id": "string", "params": {} } diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/bytes.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/bytes.js index cb5d0758efcdb..497795eb88e2a 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/bytes.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/bytes.js @@ -17,9 +17,12 @@ * under the License. */ -import { NumberFormatEditor } from '../number'; +import React, { Fragment } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { DefaultFormatEditor } from '../default'; +import { FormatEditorSamples } from '../../samples'; -export class BytesFormatEditor extends NumberFormatEditor { +export class BytesFormatEditor extends DefaultFormatEditor { static formatId = 'bytes'; constructor(props) { @@ -30,4 +33,20 @@ export class BytesFormatEditor extends NumberFormatEditor { sampleInputs: [256, 1024, 5150000, 1990000000], }; } + + render() { + const { samples } = this.state; + const locale = this.props.format.getConfig('format:number:defaultLocale'); + + return ( + + + + + ); + } } diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js new file mode 100644 index 0000000000000..6b4f85f0a52fb --- /dev/null +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js @@ -0,0 +1,124 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { Fragment } from 'react'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFormRow, EuiComboBox } from '@elastic/eui'; + +import { DefaultNumberFormatEditor } from '../default_number'; +import { FormatEditorSamples } from '../../samples'; + +// Top 35 most traded currencies per https://en.wikipedia.org/wiki/Template:Most_traded_currencies +// This list is not a full list of currencies, but the ISO standard full list of currencies +// does not provide some of the amenities of this Wiki list- a mashup of sources would be required +// to provide country name, currency name, and currency symbol. +// The full ISO reference: https://www.currency-iso.org/en/home/tables/table-a1.html +const topCurrencies = [ + { name: 'United States dollar', code: 'USD', symbol: 'US$' }, + { name: 'Euro', code: 'EUR', symbol: '€' }, + { name: 'Japanese yen', code: 'JPY', symbol: '¥' }, + { name: 'Pound sterling', code: 'GBP', symbol: '£' }, + { name: 'Australian dollar', code: 'AUD', symbol: 'A$' }, + { name: 'Canadian dollar', code: 'CAD', symbol: 'C$' }, + { name: 'Swiss franc', code: 'CHF', symbol: 'CHF' }, + { name: 'Renminbi', code: 'CNY', symbol: '元' }, + { name: 'Hong Kong dollar', code: 'HKD', symbol: 'HK$' }, + { name: 'New Zealand dollar', code: 'NZD', symbol: 'NZ$' }, + { name: 'Swedish krona', code: 'SEK', symbol: 'kr' }, + { name: 'South Korean won', code: 'KRW', symbol: '₩' }, + { name: 'Singapore dollar', code: 'SGD', symbol: 'S$' }, + { name: 'Norwegian krone', code: 'NOK', symbol: 'kr' }, + { name: 'Mexican peso', code: 'MXN', symbol: '$' }, + { name: 'Indian rupee', code: 'INR', symbol: '₹' }, + { name: 'Russian ruble', code: 'RUB', symbol: '₽' }, + { name: 'South African rand', code: 'ZAR', symbol: 'R' }, + { name: 'Turkish lira', code: 'TRY', symbol: '₺' }, + { name: 'Brazilian real', code: 'BRL', symbol: 'R$' }, + { name: 'New Taiwan dollar', code: 'TWD', symbol: 'NT$' }, + { name: 'Danish krone', code: 'DKK', symbol: 'kr' }, + { name: 'Polish zloty', code: 'PLN', symbol: 'zł' }, + { name: 'Thai baht', code: 'THB', symbol: '฿' }, + { name: 'Indonesian rupiah', code: 'IDR', symbol: 'Rp' }, + { name: 'Hungarian forint', code: 'HUF', symbol: 'Ft' }, + { name: 'Czech koruna', code: 'CZK', symbol: 'Kč' }, + { name: 'Israeli new shekel', code: 'ILS', symbol: '₪' }, + { name: 'Chilean peso', code: 'CLP', symbol: 'CLP$' }, + { name: 'Philippine peso', code: 'PHP', symbol: '₱' }, + { name: 'UAE dirham', code: 'AED', symbol: 'د.إ' }, + { name: 'Colombian peso', code: 'COP', symbol: 'COL$' }, + { name: 'Saudi riyal', code: 'SAR', symbol: '﷼' }, + { name: 'Malaysian ringgit', code: 'MYR', symbol: 'RM' }, + { name: 'Romanian leu', code: 'RON', symbol: 'L' }, +]; + +export class CurrencyFormatEditor extends DefaultNumberFormatEditor { + static formatId = 'currency'; + + constructor(props) { + super(props); + + this.state = { + ...this.state, + sampleInputs: [1234, 99.9999, 5150000.0001, 0.00005], + }; + } + + render() { + const { formatParams } = this.props; + const { samples } = this.state; + const currencyCode = formatParams.currencyCode; + const currencyMatch = topCurrencies.find(cur => cur.code === currencyCode); + let currencyLabel = currencyCode; + if (currencyMatch) { + currencyLabel = `${currencyMatch.name} (${currencyMatch.code}) ${currencyMatch.symbol}`; + } + + return ( + + + } + > + ({ + value: cur.code, + label: `${cur.name} (${cur.code}) ${cur.symbol}`, + }))} + onChange={choices => { + this.onChange({ currencyCode: choices[0].value }); + }} + /> + + + {this.renderLocaleOverride()} + + + + ); + } +} diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/index.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/index.js new file mode 100644 index 0000000000000..126c4c1c56980 --- /dev/null +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/index.js @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { CurrencyFormatEditor } from './currency'; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/index.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/index.js new file mode 100644 index 0000000000000..abee50aaa303e --- /dev/null +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/index.js @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { DefaultNumberFormatEditor } from './number'; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/number.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/number.js new file mode 100644 index 0000000000000..7c61e16796845 --- /dev/null +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/number.js @@ -0,0 +1,312 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { Fragment } from 'react'; + +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { EuiFormRow, EuiFieldNumber, EuiText, EuiSpacer, EuiSwitch, EuiSelect } from '@elastic/eui'; + +import { DefaultFormatEditor } from '../default'; + +import { FormatEditorSamples } from '../../samples'; + +// List of supported Chrome locales +const locales = { + af: 'Afrikaans', + am: 'Amharic', + ar: 'Arabic', + az: 'Azerbaijani', + be: 'Belarusian', + bg: 'Bulgarian', + bh: 'Bihari', + bn: 'Bengali', + br: 'Breton', + bs: 'Bosnian', + ca: 'Catalan', + co: 'Corsican', + cs: 'Czech', + cy: 'Welsh', + da: 'Danish', + de: 'German', + 'de-AT': 'German (Austria)', + 'de-CH': 'German (Switzerland)', + 'de-DE': 'German (Germany)', + el: 'Greek', + en: 'English', + 'en-US': 'English (US)', + 'en-IN': 'English (India)', + 'en-NG': 'English (Nigeria)', + 'en-PK': 'English (Pakistan)', + 'en-PH': 'English (Philippines)', + 'en-GB': 'English (UK)', + 'en-CA': 'English (Canada)', + 'en-AU': 'English (Australia)', + 'en-NZ': 'English (New Zealand)', + 'en-ZA': 'English (South Africa)', + eo: 'Esperanto', + es: 'Spanish', + 'es-419': 'Spanish (Latin America)', + et: 'Estonian', + eu: 'Basque', + fa: 'Persian', + fi: 'Finnish', + fil: 'Filipino', + fo: 'Faroese', + fr: 'French', + 'fr-CA': 'French (Canada)', + 'fr-CH': 'French (Switzerland)', + 'fr-FR': 'French (France)', + fy: 'Frisian', + ga: 'Irish', + gd: 'Scots Gaelic', + gl: 'Galician', + gn: 'Guarani', + gu: 'Gujarati', + ha: 'Hausa', + haw: 'Hawaiian', + he: 'Hebrew', + hi: 'Hindi', + hr: 'Croatian', + hu: 'Hungarian', + hy: 'Armenian', + ia: 'Interlingua', + id: 'Indonesian', + is: 'Icelandic', + it: 'Italian', + 'it-CH': 'Italian (Switzerland)', + 'it-IT': 'Italian (Italy)', + ja: 'Japanese', + jw: 'Javanese', + ka: 'Georgian', + kk: 'Kazakh', + km: 'Cambodian', + kn: 'Kannada', + ko: 'Korean', + ku: 'Kurdish', + ky: 'Kyrgyz', + la: 'Latin', + ln: 'Lingala', + lo: 'Laothian', + lt: 'Lithuanian', + lv: 'Latvian', + mk: 'Macedonian', + ml: 'Malayalam', + mn: 'Mongolian', + mo: 'Moldavian', + mr: 'Marathi', + ms: 'Malay', + mt: 'Maltese', + nb: 'Norwegian (Bokmal)', + ne: 'Nepali', + nl: 'Dutch', + nn: 'Norwegian (Nynorsk)', + no: 'Norwegian', + oc: 'Occitan', + om: 'Oromo', + or: 'Oriya', + pa: 'Punjabi', + pl: 'Polish', + ps: 'Pashto', + pt: 'Portuguese', + 'pt-BR': 'Portuguese (Brazil)', + 'pt-PT': 'Portuguese (Portugal)', + qu: 'Quechua', + rm: 'Romansh', + ro: 'Romanian', + ru: 'Russian', + sd: 'Sindhi', + sh: 'Serbo-Croatian', + si: 'Sinhalese', + sk: 'Slovak', + sl: 'Slovenian', + sn: 'Shona', + so: 'Somali', + sq: 'Albanian', + sr: 'Serbian', + st: 'Sesotho', + su: 'Sundanese', + sv: 'Swedish', + sw: 'Swahili', + ta: 'Tamil', + te: 'Telugu', + tg: 'Tajik', + th: 'Thai', + ti: 'Tigrinya', + tk: 'Turkmen', + to: 'Tonga', + tr: 'Turkish', + tt: 'Tatar', + tw: 'Twi', + ug: 'Uighur', + uk: 'Ukrainian', + ur: 'Urdu', + uz: 'Uzbek', + vi: 'Vietnamese', + xh: 'Xhosa', + yi: 'Yiddish', + yo: 'Yoruba', + zh: 'Chinese', + 'zh-CN': 'Chinese (Simplified)', + 'zh-TW': 'Chinese (Traditional)', + zu: 'Zulu', +}; + +export class DefaultNumberFormatEditor extends DefaultFormatEditor { + static formatId = 'default_number'; + + constructor(props) { + super(props); + this.state = { + ...this.state, + sampleInputs: [ + 10000, + 12.345678, + -1, + -999, + 0.52, + 0.00000000000000123456789, + 19900000000000000000000, + ], + supportedLocales: Intl.NumberFormat.supportedLocalesOf(Object.keys(locales)), + }; + } + + renderLocaleOverride = () => { + const { formatParams } = this.props; + + const defaultLocale = this.props.format.getConfig('format:number:defaultLocale'); + + return ( + + + } + > + <> + + {i18n.translate('common.ui.fieldEditor.numberLocaleLabel', { + defaultMessage: + 'Number formatting is set to ({locale}) by the Kibana advanced setting "format:number:defaultLocale".', + values: { locale: defaultLocale }, + })} + + + + + { + this.onChange({ localeOverride: e.target.checked }); + }} + /> + + + + {formatParams.localeOverride ? ( + + } + > + ({ + value: localeId, + text: locales[localeId], + })) + )} + onChange={e => { + this.onChange({ localeOverride: e.target.value }); + }} + value={formatParams.localeOverride || null} + /> + + ) : null} + + ); + }; + + render() { + const { formatParams } = this.props; + const { samples } = this.state; + + return ( + + + } + > + { + this.onChange({ minDecimals: Number(e.target.value) || 0 }); + }} + /> + + + + } + > + { + this.onChange({ maxDecimals: Number(e.target.value) || 0 }); + }} + /> + + + {this.renderLocaleOverride()} + + + + ); + } +} diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/index.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/index.js new file mode 100644 index 0000000000000..9a9aedd660797 --- /dev/null +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/index.js @@ -0,0 +1,20 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export { LargeNumberFormatEditor } from './large_number'; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/large_number.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/large_number.js new file mode 100644 index 0000000000000..f0e0e3c53c33f --- /dev/null +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/large_number.js @@ -0,0 +1,47 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { Fragment } from 'react'; +import { DefaultNumberFormatEditor } from '../default_number'; +import { FormatEditorSamples } from '../../samples'; + +export class LargeNumberFormatEditor extends DefaultNumberFormatEditor { + static formatId = 'large_number'; + + constructor(props) { + super(props); + + this.state = { + ...this.state, + sampleInputs: [1234, 99.9999, 5150000.0001, 0.00005, 199000000], + }; + } + + render() { + const { samples } = this.state; + + return ( + + {this.renderLocaleOverride()} + + + + ); + } +} diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/index.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/index.js index a3317d93cdd80..56ce9efa3ca69 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/index.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/index.js @@ -17,4 +17,4 @@ * under the License. */ -export { NumberFormatEditor } from './number'; +export { CustomNumberFormatEditor } from './number'; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.js index 509508590780f..e5c2ca91d936f 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.js @@ -17,9 +17,23 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; -import { EuiCode, EuiFieldText, EuiFormRow, EuiIcon, EuiLink } from '@elastic/eui'; +// @ts-ignore +import numeralLanguages from '@elastic/numeral/languages'; + +import { + EuiCode, + EuiText, + EuiFieldText, + EuiFormRow, + EuiIcon, + EuiLink, + EuiSelect, + EuiSwitch, + EuiSpacer, +} from '@elastic/eui'; import { DefaultFormatEditor } from '../default'; @@ -27,18 +41,28 @@ import { FormatEditorSamples } from '../../samples'; import { FormattedMessage } from '@kbn/i18n/react'; -export class NumberFormatEditor extends DefaultFormatEditor { +export class CustomNumberFormatEditor extends DefaultFormatEditor { static formatId = 'number'; constructor(props) { super(props); - this.state.sampleInputs = [10000, 12.345678, -1, -999, 0.52]; + this.state.sampleInputs = [ + 10000, + 12.345678, + -1, + -999, + 0.52, + 0.00000000000000123456789, + 19900000000000000000000, + ]; } render() { const { format, formatParams } = this.props; const { error, samples } = this.state; const defaultPattern = format.getParamDefaults().pattern; + const overrideLocale = formatParams.localeOverride; + const locale = format.getConfig('format:number:defaultLocale'); return ( @@ -74,6 +98,64 @@ export class NumberFormatEditor extends DefaultFormatEditor { isInvalid={!!error} /> + + + } + > + <> + + {i18n.translate('common.ui.fieldEditor.numberLocaleLabel', { + defaultMessage: + 'Number formatting is set to ({locale}) by the Kibana advanced setting "format:number:defaultLocale".', + values: { locale }, + })} + + + + + { + if (e.target.checked) { + this.onChange({ localeOverride: locale }); + } else { + this.onChange({ localeOverride: false }); + } + }} + /> + + + + {overrideLocale ? ( + + } + > + ({ + value: lang.id, + text: lang.name || lang.id, + }))} + onChange={e => { + this.onChange({ localeOverride: e.target.value }); + }} + value={overrideLocale} + /> + + ) : null} + ); diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/percent.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/percent.js index 26effb8d80095..ec67f22e879a6 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/percent.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/percent.js @@ -17,9 +17,11 @@ * under the License. */ -import { NumberFormatEditor } from '../number'; +import React, { Fragment } from 'react'; +import { DefaultNumberFormatEditor } from '../default_number'; +import { FormatEditorSamples } from '../../samples'; -export class PercentFormatEditor extends NumberFormatEditor { +export class PercentFormatEditor extends DefaultNumberFormatEditor { static formatId = 'percent'; constructor(props) { @@ -30,4 +32,16 @@ export class PercentFormatEditor extends NumberFormatEditor { sampleInputs: [0.1, 0.99999, 1, 100, 1000], }; } + + render() { + const { samples } = this.state; + + return ( + + {this.renderLocaleOverride()} + + + + ); + } } diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js b/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js new file mode 100644 index 0000000000000..fce19c0add571 --- /dev/null +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js @@ -0,0 +1,50 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { BytesFormatEditor } from './editors/bytes'; +import { ColorFormatEditor } from './editors/color'; +import { CurrencyFormatEditor } from './editors/currency'; +import { DateFormatEditor } from './editors/date'; +import { DateNanosFormatEditor } from './editors/date_nanos'; +import { DefaultNumberFormatEditor } from './editors/default_number'; +import { CustomNumberFormatEditor } from './editors/number'; +import { DurationFormatEditor } from './editors/duration'; +import { LargeNumberFormatEditor } from './editors/large_number'; +import { PercentFormatEditor } from './editors/percent'; +import { StaticLookupFormatEditor } from './editors/static_lookup'; +import { StringFormatEditor } from './editors/string'; +import { TruncateFormatEditor } from './editors/truncate'; +import { UrlFormatEditor } from './editors/url/url'; + +export const editors = [ + BytesFormatEditor, + ColorFormatEditor, + CurrencyFormatEditor, + DateFormatEditor, + DateNanosFormatEditor, + CustomNumberFormatEditor, + DefaultNumberFormatEditor, + DurationFormatEditor, + LargeNumberFormatEditor, + PercentFormatEditor, + StaticLookupFormatEditor, + StringFormatEditor, + TruncateFormatEditor, + UrlFormatEditor, +]; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/index.js b/src/legacy/ui/public/field_editor/components/field_format_editor/index.js index ccfc98b2d5882..acdd8ba565d33 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/index.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/index.js @@ -18,3 +18,4 @@ */ export { FieldFormatEditor } from './field_format_editor'; +export { editors } from './get_editors'; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/register.js b/src/legacy/ui/public/field_editor/components/field_format_editor/register.js index 3062a3ba8ac14..93c8d75feafb5 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/register.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/register.js @@ -18,26 +18,6 @@ */ import { RegistryFieldFormatEditorsProvider } from 'ui/registry/field_format_editors'; -import { BytesFormatEditor } from './editors/bytes'; -import { ColorFormatEditor } from './editors/color'; -import { DateFormatEditor } from './editors/date'; -import { DateNanosFormatEditor } from './editors/date_nanos'; -import { DurationFormatEditor } from './editors/duration'; -import { NumberFormatEditor } from './editors/number'; -import { PercentFormatEditor } from './editors/percent'; -import { StaticLookupFormatEditor } from './editors/static_lookup'; -import { StringFormatEditor } from './editors/string'; -import { TruncateFormatEditor } from './editors/truncate'; -import { UrlFormatEditor } from './editors/url/url'; +import { editors } from './get_editors'; -RegistryFieldFormatEditorsProvider.register(() => BytesFormatEditor); -RegistryFieldFormatEditorsProvider.register(() => ColorFormatEditor); -RegistryFieldFormatEditorsProvider.register(() => DateFormatEditor); -RegistryFieldFormatEditorsProvider.register(() => DateNanosFormatEditor); -RegistryFieldFormatEditorsProvider.register(() => DurationFormatEditor); -RegistryFieldFormatEditorsProvider.register(() => NumberFormatEditor); -RegistryFieldFormatEditorsProvider.register(() => PercentFormatEditor); -RegistryFieldFormatEditorsProvider.register(() => StaticLookupFormatEditor); -RegistryFieldFormatEditorsProvider.register(() => StringFormatEditor); -RegistryFieldFormatEditorsProvider.register(() => TruncateFormatEditor); -RegistryFieldFormatEditorsProvider.register(() => UrlFormatEditor); +editors.forEach(editor => RegistryFieldFormatEditorsProvider.register(() => editor)); diff --git a/src/plugins/data/common/field_formats/converters/boolean.ts b/src/plugins/data/common/field_formats/converters/boolean.ts index 6cc6c71465d50..b2ef6f21202a2 100644 --- a/src/plugins/data/common/field_formats/converters/boolean.ts +++ b/src/plugins/data/common/field_formats/converters/boolean.ts @@ -17,6 +17,7 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; @@ -24,7 +25,9 @@ import { asPrettyString } from '../utils'; export class BoolFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.BOOLEAN; - static title = 'Boolean'; + static title = i18n.translate('data.common.fieldFormats.boolean.title', { + defaultMessage: 'Boolean', + }); static fieldType = [KBN_FIELD_TYPES.BOOLEAN, KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING]; textConvert: TextContextTypeConvert = value => { diff --git a/src/plugins/data/common/field_formats/converters/bytes.ts b/src/plugins/data/common/field_formats/converters/bytes.ts index 6c6df5eb7367d..20f49975c30ab 100644 --- a/src/plugins/data/common/field_formats/converters/bytes.ts +++ b/src/plugins/data/common/field_formats/converters/bytes.ts @@ -17,12 +17,15 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { NumeralFormat } from './numeral'; import { FIELD_FORMAT_IDS } from '../types'; export class BytesFormat extends NumeralFormat { static id = FIELD_FORMAT_IDS.BYTES; - static title = 'Bytes'; + static title = i18n.translate('data.common.fieldFormats.bytes.title', { + defaultMessage: 'Bytes', + }); id = BytesFormat.id; title = BytesFormat.title; diff --git a/src/plugins/data/common/field_formats/converters/color.ts b/src/plugins/data/common/field_formats/converters/color.ts index ffc72ba9a2c30..bf9d05585089a 100644 --- a/src/plugins/data/common/field_formats/converters/color.ts +++ b/src/plugins/data/common/field_formats/converters/color.ts @@ -17,6 +17,7 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { findLast, cloneDeep, template, escape } from 'lodash'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; @@ -28,7 +29,9 @@ const convertTemplate = template('<%- val %>') export class ColorFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.COLOR; - static title = 'Color'; + static title = i18n.translate('data.common.fieldFormats.color.title', { + defaultMessage: 'Color', + }); static fieldType = [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.STRING]; getParamDefaults() { diff --git a/src/plugins/data/common/field_formats/converters/currency.ts b/src/plugins/data/common/field_formats/converters/currency.ts new file mode 100644 index 0000000000000..83d45639bc854 --- /dev/null +++ b/src/plugins/data/common/field_formats/converters/currency.ts @@ -0,0 +1,46 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { IntlNumberFormat } from './intl_number_format'; +import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; +import { FIELD_FORMAT_IDS } from '../types'; + +export class CurrencyFormat extends IntlNumberFormat { + static id = FIELD_FORMAT_IDS.CURRENCY; + static title = i18n.translate('data.common.fieldFormats.currency.title', { + defaultMessage: 'Currency', + }); + static fieldType = KBN_FIELD_TYPES.NUMBER; + + getParamDefaults = () => ({ + currencyCode: 'USD', + localeOverride: false, + }); + + id = CurrencyFormat.id; + title = CurrencyFormat.title; + + getArguments = () => { + return { + style: 'currency', + currency: this.param('currencyCode'), + }; + }; +} diff --git a/src/plugins/data/common/field_formats/converters/date.ts b/src/plugins/data/common/field_formats/converters/date.ts index 06af64d9c17c2..b2213e74096bf 100644 --- a/src/plugins/data/common/field_formats/converters/date.ts +++ b/src/plugins/data/common/field_formats/converters/date.ts @@ -17,6 +17,7 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { memoize, noop } from 'lodash'; import moment from 'moment'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; @@ -25,7 +26,9 @@ import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class DateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DATE; - static title = 'Date'; + static title = i18n.translate('data.common.fieldFormats.date.title', { + defaultMessage: 'Date', + }); static fieldType = KBN_FIELD_TYPES.DATE; private memoizedConverter: Function = noop; diff --git a/src/plugins/data/common/field_formats/converters/date_nanos.ts b/src/plugins/data/common/field_formats/converters/date_nanos.ts index 8b0f8b111694e..420f9807b54c2 100644 --- a/src/plugins/data/common/field_formats/converters/date_nanos.ts +++ b/src/plugins/data/common/field_formats/converters/date_nanos.ts @@ -17,6 +17,7 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import moment, { Moment } from 'moment'; import { memoize, noop } from 'lodash'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; @@ -70,7 +71,9 @@ export function formatWithNanos( export class DateNanosFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DATE_NANOS; - static title = 'Date Nanos'; + static title = i18n.translate('data.common.fieldFormats.date_nanos.title', { + defaultMessage: 'Date nanos', + }); static fieldType = KBN_FIELD_TYPES.DATE; private memoizedConverter: Function = noop; diff --git a/src/plugins/data/common/field_formats/converters/date_server.ts b/src/plugins/data/common/field_formats/converters/date_server.ts index 34278ea9fe641..0bbbef35a1ad7 100644 --- a/src/plugins/data/common/field_formats/converters/date_server.ts +++ b/src/plugins/data/common/field_formats/converters/date_server.ts @@ -17,6 +17,7 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { memoize, noop } from 'lodash'; import moment from 'moment-timezone'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; @@ -25,7 +26,9 @@ import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class DateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DATE; - static title = 'Date'; + static title = i18n.translate('data.common.fieldFormats.date.title', { + defaultMessage: 'Date', + }); static fieldType = KBN_FIELD_TYPES.DATE; private memoizedConverter: Function = noop; diff --git a/src/plugins/data/common/field_formats/converters/default_number.ts b/src/plugins/data/common/field_formats/converters/default_number.ts new file mode 100644 index 0000000000000..3f31e2102cfbe --- /dev/null +++ b/src/plugins/data/common/field_formats/converters/default_number.ts @@ -0,0 +1,45 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; + +import { FIELD_FORMAT_IDS } from '../types'; +import { IntlNumberFormat } from './intl_number_format'; + +export class DefaultNumberFormat extends IntlNumberFormat { + static id = FIELD_FORMAT_IDS.DEFAULT_NUMBER; + static title = i18n.translate('data.common.fieldFormats.default_number.title', { + defaultMessage: 'Default number format', + }); + + id = DefaultNumberFormat.id; + title = DefaultNumberFormat.title; + + getParamDefaults = () => ({ + localeOverride: false, + minDecimals: 0, + maxDecimals: 3, + }); + + getArguments = () => ({ + style: 'decimal', + minimumFractionDigits: this.param('minDecimals'), + maximumFractionDigits: this.param('maxDecimals'), + }); +} diff --git a/src/plugins/data/common/field_formats/converters/duration.ts b/src/plugins/data/common/field_formats/converters/duration.ts index d02de1a2fd889..feeb156c35317 100644 --- a/src/plugins/data/common/field_formats/converters/duration.ts +++ b/src/plugins/data/common/field_formats/converters/duration.ts @@ -167,7 +167,9 @@ function parseInputAsDuration(val: number, inputFormat: string) { export class DurationFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.DURATION; - static title = 'Duration'; + static title = i18n.translate('data.common.fieldFormats.duration.title', { + defaultMessage: 'Duration', + }); static fieldType = KBN_FIELD_TYPES.NUMBER; static inputFormats = inputFormats; static outputFormats = outputFormats; @@ -188,7 +190,12 @@ export class DurationFormat extends FieldFormat { const outputFormat = this.param('outputFormat') as keyof Duration; const outputPrecision = this.param('outputPrecision'); const human = this.isHuman(); - const prefix = val < 0 && human ? 'minus ' : ''; + const prefix = + val < 0 && human + ? i18n.translate('data.common.fieldFormats.duration.negativeLabel', { + defaultMessage: 'minus', + }) + ' ' + : ''; const duration = parseInputAsDuration(val, inputFormat) as Record; const formatted = duration[outputFormat](); const precise = human ? formatted : formatted.toFixed(outputPrecision); diff --git a/src/plugins/data/common/field_formats/converters/index.ts b/src/plugins/data/common/field_formats/converters/index.ts index f7e50539b44d8..f8d42a7c0383b 100644 --- a/src/plugins/data/common/field_formats/converters/index.ts +++ b/src/plugins/data/common/field_formats/converters/index.ts @@ -19,12 +19,15 @@ export { UrlFormat } from './url'; export { BytesFormat } from './bytes'; +export { CurrencyFormat } from './currency'; +export { CustomNumberFormat } from './number'; +export { DefaultNumberFormat } from './default_number'; export { DateFormat } from './date_server'; export { DateNanosFormat } from './date_nanos'; export { RelativeDateFormat } from './relative_date'; export { DurationFormat } from './duration'; export { IpFormat } from './ip'; -export { NumberFormat } from './number'; +export { LargeNumberFormat } from './large_number'; export { PercentFormat } from './percent'; export { StringFormat } from './string'; export { SourceFormat } from './source'; diff --git a/src/plugins/data/common/field_formats/converters/intl_number_format.ts b/src/plugins/data/common/field_formats/converters/intl_number_format.ts new file mode 100644 index 0000000000000..40e189ecbb4e3 --- /dev/null +++ b/src/plugins/data/common/field_formats/converters/intl_number_format.ts @@ -0,0 +1,60 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// import { i18n } from '@kbn/i18n'; +import { FieldFormat } from '../field_format'; +// import { FIELD_FORMAT_IDS } from '../types'; +import { TextContextTypeConvert } from '../types'; +import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; + +export abstract class IntlNumberFormat extends FieldFormat { + static fieldType = KBN_FIELD_TYPES.NUMBER; + + abstract id: string; + abstract title: string; + + getParamDefaults = () => ({ + localeOverride: false, + }); + + abstract getArguments: () => Record; + + protected getConvertedValue(val: any): string { + if (val === -Infinity) return '-∞'; + if (val === +Infinity) return '+∞'; + if (typeof val !== 'number') { + val = parseFloat(val); + } + + if (isNaN(val)) return ''; + + const locale = + this.param('localeOverride') || + (this.getConfig && this.getConfig('format:number:defaultLocale')) || + 'en'; + + const inst = new Intl.NumberFormat(locale, this.getArguments()); + + return inst.format(val); + } + + textConvert: TextContextTypeConvert = val => { + return this.getConvertedValue(val); + }; +} diff --git a/src/plugins/data/common/field_formats/converters/ip.ts b/src/plugins/data/common/field_formats/converters/ip.ts index 3e011e8d7dde8..903a6e4151f75 100644 --- a/src/plugins/data/common/field_formats/converters/ip.ts +++ b/src/plugins/data/common/field_formats/converters/ip.ts @@ -17,13 +17,16 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class IpFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.IP; - static title = 'IP Address'; + static title = i18n.translate('data.common.fieldFormats.ip.title', { + defaultMessage: 'IP address', + }); static fieldType = KBN_FIELD_TYPES.IP; textConvert: TextContextTypeConvert = val => { diff --git a/src/plugins/data/common/field_formats/converters/large_number.ts b/src/plugins/data/common/field_formats/converters/large_number.ts new file mode 100644 index 0000000000000..1edcf5fdc06f0 --- /dev/null +++ b/src/plugins/data/common/field_formats/converters/large_number.ts @@ -0,0 +1,37 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { i18n } from '@kbn/i18n'; +import { IntlNumberFormat } from './intl_number_format'; +import { FIELD_FORMAT_IDS } from '../types'; + +export class LargeNumberFormat extends IntlNumberFormat { + static id = FIELD_FORMAT_IDS.LARGE_NUMBER; + static title = i18n.translate('data.common.fieldFormats.large_number.title', { + defaultMessage: 'Shorter number', + }); + + id = LargeNumberFormat.id; + title = LargeNumberFormat.title; + + getArguments = () => ({ + style: 'decimal', + notation: 'compact', + }); +} diff --git a/src/plugins/data/common/field_formats/converters/number.test.ts b/src/plugins/data/common/field_formats/converters/number.test.ts index fe36d5b12e873..3676400a26d0e 100644 --- a/src/plugins/data/common/field_formats/converters/number.test.ts +++ b/src/plugins/data/common/field_formats/converters/number.test.ts @@ -17,7 +17,7 @@ * under the License. */ -import { NumberFormat } from './number'; +import { CustomNumberFormat } from './number'; describe('NumberFormat', () => { const config: Record = {}; @@ -27,13 +27,13 @@ describe('NumberFormat', () => { const getConfig = (key: string) => config[key]; test('default pattern', () => { - const formatter = new NumberFormat({}, getConfig); + const formatter = new CustomNumberFormat({}, getConfig); expect(formatter.convert(12.345678)).toBe('12.346'); }); test('custom pattern', () => { - const formatter = new NumberFormat({ pattern: '0,0' }, getConfig); + const formatter = new CustomNumberFormat({ pattern: '0,0' }, getConfig); expect(formatter.convert('12.345678')).toBe('12'); }); diff --git a/src/plugins/data/common/field_formats/converters/number.ts b/src/plugins/data/common/field_formats/converters/number.ts index 6969c1551e1cc..865cc6226c178 100644 --- a/src/plugins/data/common/field_formats/converters/number.ts +++ b/src/plugins/data/common/field_formats/converters/number.ts @@ -17,13 +17,16 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { NumeralFormat } from './numeral'; import { FIELD_FORMAT_IDS } from '../types'; -export class NumberFormat extends NumeralFormat { - static id = FIELD_FORMAT_IDS.NUMBER; - static title = 'Number'; +export class CustomNumberFormat extends NumeralFormat { + static id = FIELD_FORMAT_IDS.CUSTOM_NUMBER; + static title = i18n.translate('data.common.fieldFormats.number.title', { + defaultMessage: 'Custom numeral.js format string', + }); - id = NumberFormat.id; - title = NumberFormat.title; + id = CustomNumberFormat.id; + title = CustomNumberFormat.title; } diff --git a/src/plugins/data/common/field_formats/converters/numeral.ts b/src/plugins/data/common/field_formats/converters/numeral.ts index d8e46a480294f..2cfe776a1871d 100644 --- a/src/plugins/data/common/field_formats/converters/numeral.ts +++ b/src/plugins/data/common/field_formats/converters/numeral.ts @@ -39,9 +39,10 @@ export abstract class NumeralFormat extends FieldFormat { getParamDefaults = () => ({ pattern: this.getConfig!(`format:${this.id}:defaultPattern`), + localeOverride: false, }); - protected getConvertedValue(val: any): string { + protected getConvertedValue(val: any, pattern?: string): string { if (val === -Infinity) return '-∞'; if (val === +Infinity) return '+∞'; if (typeof val !== 'number') { @@ -51,10 +52,15 @@ export abstract class NumeralFormat extends FieldFormat { if (isNaN(val)) return ''; const previousLocale = numeral.language(); - const defaultLocale = (this.getConfig && this.getConfig('format:number:defaultLocale')) || 'en'; - numeral.language(defaultLocale); + if (this.param('localeOverride')) { + numeral.language(this.param('localeOverride')); + } else { + const defaultLocale = + (this.getConfig && this.getConfig('format:number:defaultLocale')) || 'en'; + numeral.language(defaultLocale); + } - const formatted = numeralInst.set(val).format(this.param('pattern')); + const formatted = numeralInst.set(val).format(pattern || this.param('pattern')); numeral.language(previousLocale); diff --git a/src/plugins/data/common/field_formats/converters/percent.ts b/src/plugins/data/common/field_formats/converters/percent.ts index 2ae32c7c77f07..6f63c9f6a24ce 100644 --- a/src/plugins/data/common/field_formats/converters/percent.ts +++ b/src/plugins/data/common/field_formats/converters/percent.ts @@ -17,28 +17,18 @@ * under the License. */ -import { NumeralFormat } from './numeral'; -import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; +import { i18n } from '@kbn/i18n'; +import { IntlNumberFormat } from './intl_number_format'; +import { FIELD_FORMAT_IDS } from '../types'; -export class PercentFormat extends NumeralFormat { +export class PercentFormat extends IntlNumberFormat { static id = FIELD_FORMAT_IDS.PERCENT; - static title = 'Percentage'; + static title = i18n.translate('data.common.fieldFormats.percent.title', { + defaultMessage: 'Percent', + }); id = PercentFormat.id; title = PercentFormat.title; - getParamDefaults = () => ({ - pattern: this.getConfig!('format:percent:defaultPattern'), - fractional: true, - }); - - textConvert: TextContextTypeConvert = val => { - const formatted = super.getConvertedValue(val); - - if (this.param('fractional')) { - return formatted; - } - - return String(Number(formatted) / 100); - }; + getArguments = () => ({ style: 'percent' }); } diff --git a/src/plugins/data/common/field_formats/converters/relative_date.ts b/src/plugins/data/common/field_formats/converters/relative_date.ts index 273b2cef28a03..f3aa8cac821e0 100644 --- a/src/plugins/data/common/field_formats/converters/relative_date.ts +++ b/src/plugins/data/common/field_formats/converters/relative_date.ts @@ -17,6 +17,7 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import moment from 'moment'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; @@ -24,7 +25,9 @@ import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; export class RelativeDateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.RELATIVE_DATE; - static title = 'Relative Date'; + static title = i18n.translate('data.common.fieldFormats.relative_date.title', { + defaultMessage: 'Relative date', + }); static fieldType = KBN_FIELD_TYPES.DATE; textConvert: TextContextTypeConvert = val => { diff --git a/src/plugins/data/common/field_formats/converters/static_lookup.ts b/src/plugins/data/common/field_formats/converters/static_lookup.ts index 419e7c786640b..719a1c2c75cce 100644 --- a/src/plugins/data/common/field_formats/converters/static_lookup.ts +++ b/src/plugins/data/common/field_formats/converters/static_lookup.ts @@ -17,6 +17,7 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; import { TextContextTypeConvert, FIELD_FORMAT_IDS } from '../types'; @@ -33,7 +34,9 @@ function convertLookupEntriesToMap(lookupEntries: any[]) { export class StaticLookupFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.STATIC_LOOKUP; - static title = 'Static Lookup'; + static title = i18n.translate('data.common.fieldFormats.static_lookup.title', { + defaultMessage: 'Static lookup', + }); static fieldType = [ KBN_FIELD_TYPES.STRING, KBN_FIELD_TYPES.NUMBER, diff --git a/src/plugins/data/common/field_formats/converters/string.ts b/src/plugins/data/common/field_formats/converters/string.ts index b2d92cf475a16..203d2d74993e4 100644 --- a/src/plugins/data/common/field_formats/converters/string.ts +++ b/src/plugins/data/common/field_formats/converters/string.ts @@ -72,7 +72,9 @@ const DEFAULT_TRANSFORM_OPTION = false; export class StringFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.STRING; - static title = 'String'; + static title = i18n.translate('data.common.fieldFormats.string.title', { + defaultMessage: 'String', + }); static fieldType = [ KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.BOOLEAN, diff --git a/src/plugins/data/common/field_formats/converters/truncate.ts b/src/plugins/data/common/field_formats/converters/truncate.ts index dc25d71ec95d7..ac8401ded25d5 100644 --- a/src/plugins/data/common/field_formats/converters/truncate.ts +++ b/src/plugins/data/common/field_formats/converters/truncate.ts @@ -17,6 +17,7 @@ * under the License. */ +import { i18n } from '@kbn/i18n'; import { trunc } from 'lodash'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; import { FieldFormat } from '../field_format'; @@ -26,7 +27,9 @@ const omission = '...'; export class TruncateFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.TRUNCATE; - static title = 'Truncated String'; + static title = i18n.translate('data.common.fieldFormats.truncated_string.title', { + defaultMessage: 'Truncated string', + }); static fieldType = KBN_FIELD_TYPES.STRING; textConvert: TextContextTypeConvert = val => { diff --git a/src/plugins/data/common/field_formats/converters/url.ts b/src/plugins/data/common/field_formats/converters/url.ts index 21688dd8d1138..e4f7a06412afb 100644 --- a/src/plugins/data/common/field_formats/converters/url.ts +++ b/src/plugins/data/common/field_formats/converters/url.ts @@ -51,7 +51,9 @@ const DEFAULT_URL_TYPE = 'a'; export class UrlFormat extends FieldFormat { static id = FIELD_FORMAT_IDS.URL; - static title = 'Url'; + static title = i18n.translate('data.common.fieldFormats.url.title', { + defaultMessage: 'Url', + }); static fieldType = [ KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.BOOLEAN, diff --git a/src/plugins/data/common/field_formats/types.ts b/src/plugins/data/common/field_formats/types.ts index dce3c66b0f886..64b3794688063 100644 --- a/src/plugins/data/common/field_formats/types.ts +++ b/src/plugins/data/common/field_formats/types.ts @@ -54,11 +54,14 @@ export enum FIELD_FORMAT_IDS { BYTES = 'bytes', COLOR = 'color', CUSTOM = 'custom', + CUSTOM_NUMBER = 'number', // Backwards compatible + DEFAULT_NUMBER = 'default_number', + CURRENCY = 'currency', DATE = 'date', DATE_NANOS = 'date_nanos', DURATION = 'duration', IP = 'ip', - NUMBER = 'number', + LARGE_NUMBER = 'large_number', PERCENT = 'percent', RELATIVE_DATE = 'relative_date', STATIC_LOOKUP = 'static_lookup', diff --git a/src/plugins/data/public/field_formats_provider/field_formats_service.ts b/src/plugins/data/public/field_formats_provider/field_formats_service.ts index 42abeecc6fda0..b986fab315ebf 100644 --- a/src/plugins/data/public/field_formats_provider/field_formats_service.ts +++ b/src/plugins/data/public/field_formats_provider/field_formats_service.ts @@ -24,11 +24,14 @@ import { BoolFormat, BytesFormat, ColorFormat, + CurrencyFormat, DateFormat, DateNanosFormat, DurationFormat, IpFormat, - NumberFormat, + LargeNumberFormat, + DefaultNumberFormat, + CustomNumberFormat, PercentFormat, RelativeDateFormat, SourceFormat, @@ -48,11 +51,14 @@ export class FieldFormatsService { BoolFormat, BytesFormat, ColorFormat, + CurrencyFormat, DateFormat, DateNanosFormat, DurationFormat, IpFormat, - NumberFormat, + LargeNumberFormat, + DefaultNumberFormat, + CustomNumberFormat, PercentFormat, RelativeDateFormat, SourceFormat, diff --git a/src/test_utils/public/stub_field_formats.ts b/src/test_utils/public/stub_field_formats.ts index ea46710c0dc84..55e790b470286 100644 --- a/src/test_utils/public/stub_field_formats.ts +++ b/src/test_utils/public/stub_field_formats.ts @@ -27,7 +27,9 @@ import { DateNanosFormat, DurationFormat, IpFormat, - NumberFormat, + LargeNumberFormat, + DefaultNumberFormat, + CustomNumberFormat, PercentFormat, RelativeDateFormat, SourceFormat, @@ -44,11 +46,13 @@ export const getFieldFormatsRegistry = (core: CoreSetup) => { BoolFormat, BytesFormat, ColorFormat, + LargeNumberFormat, + DefaultNumberFormat, + CustomNumberFormat, DateFormat, DateNanosFormat, DurationFormat, IpFormat, - NumberFormat, PercentFormat, RelativeDateFormat, SourceFormat, From 5682ade5bf775a5e14fed223029b5e20c6a570cd Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Thu, 9 Jan 2020 17:33:37 -0500 Subject: [PATCH 02/23] Remove locale override. Support browser locale. --- docs/management/advanced-options.asciidoc | 7 +- package.json | 2 +- packages/kbn-i18n/src/core/i18n.ts | 19 +- packages/kbn-i18n/src/core/known_locales.ts | 85 +++++++ .../core/{locales.js => register_locales.js} | 0 .../kibana/ui_setting_defaults.js | 176 +++++++------ .../lib/__tests__/tick_formatter.js | 28 +-- .../mixin/field_formats_service.test.ts | 4 +- .../ui/public/agg_types/buckets/range.test.ts | 9 +- .../editors/bytes/bytes.js | 7 - .../editors/currency/currency.js | 50 +++- .../editors/default_number/number.js | 233 +----------------- .../editors/number/number.js | 75 +----- .../editors/number/number.test.js | 6 +- .../editors/percent/percent.js | 2 +- .../{large_number => short_number}/index.js | 2 +- .../short_number.js} | 6 +- .../field_format_editor/get_editors.js | 4 +- .../common/field_formats/converters/bytes.ts | 4 + .../field_formats/converters/currency.ts | 7 +- .../converters/default_number.ts | 1 - .../common/field_formats/converters/index.ts | 2 +- .../converters/intl_number_format.ts | 22 +- .../common/field_formats/converters/number.ts | 2 +- .../field_formats/converters/numeral.ts | 28 ++- .../field_formats/converters/percent.test.ts | 4 +- .../field_formats/converters/percent.ts | 11 +- .../{large_number.ts => short_number.ts} | 12 +- .../data/common/field_formats/types.ts | 2 +- .../field_formats_service.ts | 4 +- src/plugins/data/public/index.ts | 4 +- src/plugins/data/server/index.ts | 3 +- src/test_utils/public/stub_field_formats.ts | 4 +- x-pack/package.json | 2 +- .../translations/translations/ja-JP.json | 2 - .../translations/translations/zh-CN.json | 2 - yarn.lock | 8 +- 37 files changed, 365 insertions(+), 474 deletions(-) create mode 100644 packages/kbn-i18n/src/core/known_locales.ts rename packages/kbn-i18n/src/core/{locales.js => register_locales.js} (100%) rename src/legacy/ui/public/field_editor/components/field_format_editor/editors/{large_number => short_number}/index.js (93%) rename src/legacy/ui/public/field_editor/components/field_format_editor/editors/{large_number/large_number.js => short_number/short_number.js} (90%) rename src/plugins/data/common/field_formats/converters/{large_number.ts => short_number.ts} (79%) diff --git a/docs/management/advanced-options.asciidoc b/docs/management/advanced-options.asciidoc index 977a65f62202d..1d94bae0b9958 100644 --- a/docs/management/advanced-options.asciidoc +++ b/docs/management/advanced-options.asciidoc @@ -50,13 +50,12 @@ adapt to the interval between measurements. Keys are http://en.wikipedia.org/wik `fields:popularLimit`:: The top N most popular fields to show. `filterEditor:suggestValues`:: Set this property to `false` to prevent the filter editor from suggesting values for fields. `filters:pinnedByDefault`:: Set this property to `true` to make filters have a global state (be pinned) by default. -`format:bytes:defaultPattern`:: The default http://numeraljs.com/[numeral format] for the "bytes" format. -`format:currency:defaultPattern`:: The default http://numeraljs.com/[numeral format] for the "currency" format. `format:defaultTypeMap`:: A map of the default format name for each field type. Field types that are not explicitly mentioned use "\_default_". -`format:number:defaultLocale`:: The http://numeraljs.com/[numeral language] locale. +`format:currency:defaultCurrency`:: The currency used for all currency formatting by default. Currency can be changed for a single field using the index management section. If a currency is unlisted here, it can also be set in the index management section. +`format:defaultLocale`:: Default number display locale. This locale will affect number formatting for most formatters. When "detect" is chosen, it will use the locale preference from the browser when formatting numbers. +`format:number:defaultLocale`:: The http://numeraljs.com/[numeral language] locale. This affects the custom numeral.js formatter and the bytes formatter. `format:number:defaultPattern`:: The default http://numeraljs.com/[numeral format] for the "number" format. -`format:percent:defaultPattern`:: The default http://numeraljs.com/[numeral format] for the "percent" format. `histogram:barTarget`:: When date histograms use the `auto` interval, Kibana attempts to generate this number of bars. `histogram:maxBars`:: Date histograms are not generated with more bars than the value of this property, scaling values when necessary. diff --git a/package.json b/package.json index db5764e6e91ba..cfe75dae105a1 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "@elastic/eui": "17.3.1", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", - "@elastic/numeral": "2.3.3", + "@elastic/numeral": "2.3.5", "@elastic/request-crypto": "^1.0.2", "@elastic/ui-ace": "0.2.3", "@hapi/wreck": "^15.0.1", diff --git a/packages/kbn-i18n/src/core/i18n.ts b/packages/kbn-i18n/src/core/i18n.ts index c66555ab015dc..175507ae3b5ff 100644 --- a/packages/kbn-i18n/src/core/i18n.ts +++ b/packages/kbn-i18n/src/core/i18n.ts @@ -25,9 +25,11 @@ import { Translation } from '../translation'; import { Formats, formats as EN_FORMATS } from './formats'; import { hasValues, isObject, isString, mergeAll } from './helper'; import { isPseudoLocale, translateUsingPseudoLocale } from './pseudo_locale'; +import { knownLocales } from './known_locales'; // Add all locale data to `IntlMessageFormat`. -import './locales.js'; +// This has a side effect./register_localest of registering locale data to numeral-js +import './register_locales'; const EN_LOCALE = 'en'; const translationsForLocale: Record = {}; @@ -163,6 +165,21 @@ export function getRegisteredLocales() { return Object.keys(translationsForLocale); } +/** + * Returns array of locales that can be used with i18n, even if there is no translation. + * This could be used for number formatting. + */ +export function getKnownLocales() { + return Object.keys(knownLocales); +} + +/** + * Returns { [locale]: { name: [display], native: '', currency: '' } } map. + */ +export function getKnownLocalesWithDisplay() { + return knownLocales; +} + interface TranslateArguments { values?: Record; defaultMessage: string; diff --git a/packages/kbn-i18n/src/core/known_locales.ts b/packages/kbn-i18n/src/core/known_locales.ts new file mode 100644 index 0000000000000..6cf37c81ca8ab --- /dev/null +++ b/packages/kbn-i18n/src/core/known_locales.ts @@ -0,0 +1,85 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const knownLocales = { + // Default + en: { name: 'English' }, + // Americas + 'en-US': { name: 'English (US)', currency: 'USD' }, + 'en-CA': { name: 'English (Canadian)', currency: 'CAD' }, + 'es-419': { name: 'Spanish (Latin America)', native: 'Español (Latinoamérica)' }, + 'fr-CA': { name: 'French (Canada)', native: 'Français (Canadien)', currency: 'CAD' }, + 'pt-BR': { name: 'Portuguese (Brazil)', native: 'Português (do Brasil)', currency: 'BRL' }, + // Asia Pacific + bn: { name: 'Bengali', native: 'বাংলা', currency: 'BDT' }, + fil: { name: 'Filipino', native: 'Pilipino', currency: 'PHP' }, + gu: { name: 'Gujarati', native: 'ગુજરાતી', currency: 'INR' }, + hi: { name: 'Hindi', native: 'हिन्दी', currency: 'INR' }, + id: { name: 'Indonesian', native: 'Bahasa Indonesia', currency: 'IDR' }, + ja: { name: 'Japanese', native: '日本語', currency: 'JPY' }, + kn: { name: 'Kannada', native: 'ಕನ್ನಡ', currency: 'INR' }, + ko: { name: 'Korean', native: '한국어', currency: 'KRW' }, + ml: { name: 'Malayalam', native: 'മലയാളം', currency: 'INR' }, + mr: { name: 'Marathi', native: 'मराठी', currency: 'INR' }, + ms: { name: 'Malay', native: 'Bahasa Melayu', currency: 'MYR' }, + si: { name: 'Sinhalese', native: 'සිංහල', currency: 'LKR' }, + ta: { name: 'Tamil', native: 'தமிழ்', currency: 'INR' }, + 'ta-LK': { name: 'Tamil (Sri Lanka)', native: 'தமிழ் (இலங்கை)', currency: 'LKR' }, + te: { name: 'Telugu', native: 'తెలుగు', currency: 'INR' }, + th: { name: 'Thai', native: 'ไทย', currency: 'THB' }, + vi: { name: 'Vietnamese', native: 'Tiếng Việt', currency: 'VND' }, + 'zh-CN': { name: 'Chinese (Simplified)', native: '中文 (简体)', currency: 'CNY' }, + 'zh-TW': { name: 'Chinese (Traditional)', native: '正體中文 (繁體)', currency: 'CNY' }, + // Europe + 'en-GB': { name: 'English (British)', native: '', currency: 'GBP' }, + bg: { name: 'Bulgarian', native: 'Български', currency: 'BGN' }, + ca: { name: 'Catalan', native: 'català', currency: 'EUR' }, + cs: { name: 'Czech', native: 'Čeština', currency: 'CZK' }, + da: { name: 'Danish', native: 'Dansk', currency: 'DKK' }, + de: { name: 'German', native: 'Deutsch', currency: 'EUR' }, + el: { name: 'Greek', native: 'Ελληνικά', currency: 'EUR' }, + es: { name: 'Spanish', native: 'Español', currency: 'EUR' }, + et: { name: 'Estonian', native: 'eesti keel', currency: 'EUR' }, + fi: { name: 'Finnish', native: 'suomi', currency: 'EUR' }, + fr: { name: 'French', native: 'Français', currency: 'EUR' }, + hr: { name: 'Croatian', native: 'Hrvatski', currency: 'HRK' }, + hu: { name: 'Hungarian', native: 'Magyar', currency: 'HUF' }, + it: { name: 'Italian', native: 'Italiano', currency: 'EUR' }, + lt: { name: 'Lithuanian', native: 'lietuvių kalba', currency: 'EUR' }, + lv: { name: 'Latvian', native: 'Latv', currency: 'EUR' }, + no: { name: 'Norwegian', native: 'Norsk', currency: 'NOK' }, + 'nl-NL': { name: 'Dutch (Netherlands)', native: 'Nederlands', currency: 'EUR' }, + // Dutch (Belgium) has the wrong code in numeral.js, indicating the locale is in Belarus instead of Belgium + 'nl-BE': { name: 'Dutch (Belgium)', native: 'Belgisch-Nederlands', currency: 'EUR' }, + pl: { name: 'Polish', native: 'Polski', currency: 'PLN' }, + 'pt-PT': { name: 'Portuguese (Portugal)', native: 'Português (Europeu)', currency: 'EUR' }, + ro: { name: 'Romanian', native: 'română', currency: 'RON' }, + ru: { name: 'Russian', native: 'Русский', currency: 'RUB' }, + sk: { name: 'Slovak', native: 'slovenčina', currency: 'EUR' }, + sl: { name: 'Slovenian', native: 'slovenščina', currency: 'EUR' }, + sr: { name: 'Serbian', native: 'Српски', currency: 'RSD' }, + sv: { name: 'Swedish', native: 'Svenska', currency: 'SEK' }, + tr: { name: 'Turkish', native: 'Türkçe', currency: 'TRY' }, + uk: { name: 'Ukrainian', native: 'Українська', currency: 'UAH' }, + // Middle East and Africa + ar: { name: 'Arabic', native: 'عربي' }, + fa: { name: 'Persian', native: 'فارسی' }, + he: { name: 'Hebrew', native: 'עברית', currency: 'ILS' }, + sw: { name: 'Swahili', native: 'Kiswahili' }, +}; diff --git a/packages/kbn-i18n/src/core/locales.js b/packages/kbn-i18n/src/core/register_locales.js similarity index 100% rename from packages/kbn-i18n/src/core/locales.js rename to packages/kbn-i18n/src/core/register_locales.js diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js index 1cea84d19b85d..cd1d6d0e7a818 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js @@ -22,6 +22,44 @@ import numeralLanguages from '@elastic/numeral/languages'; import { i18n } from '@kbn/i18n'; import { DEFAULT_QUERY_LANGUAGE } from '../../../plugins/data/common'; +const topCurrencies = [ + { name: 'United States dollar', code: 'USD', symbol: 'US$' }, + { name: 'Euro', code: 'EUR', symbol: '€' }, + { name: 'Japanese yen', code: 'JPY', symbol: '¥' }, + { name: 'Pound sterling', code: 'GBP', symbol: '£' }, + { name: 'Australian dollar', code: 'AUD', symbol: 'A$' }, + { name: 'Canadian dollar', code: 'CAD', symbol: 'C$' }, + { name: 'Swiss franc', code: 'CHF', symbol: 'CHF' }, + { name: 'Renminbi', code: 'CNY', symbol: '元' }, + { name: 'Hong Kong dollar', code: 'HKD', symbol: 'HK$' }, + { name: 'New Zealand dollar', code: 'NZD', symbol: 'NZ$' }, + { name: 'Swedish krona', code: 'SEK', symbol: 'kr' }, + { name: 'South Korean won', code: 'KRW', symbol: '₩' }, + { name: 'Singapore dollar', code: 'SGD', symbol: 'S$' }, + { name: 'Norwegian krone', code: 'NOK', symbol: 'kr' }, + { name: 'Mexican peso', code: 'MXN', symbol: '$' }, + { name: 'Indian rupee', code: 'INR', symbol: '₹' }, + { name: 'Russian ruble', code: 'RUB', symbol: '₽' }, + { name: 'South African rand', code: 'ZAR', symbol: 'R' }, + { name: 'Turkish lira', code: 'TRY', symbol: '₺' }, + { name: 'Brazilian real', code: 'BRL', symbol: 'R$' }, + { name: 'New Taiwan dollar', code: 'TWD', symbol: 'NT$' }, + { name: 'Danish krone', code: 'DKK', symbol: 'kr' }, + { name: 'Polish zloty', code: 'PLN', symbol: 'zł' }, + { name: 'Thai baht', code: 'THB', symbol: '฿' }, + { name: 'Indonesian rupiah', code: 'IDR', symbol: 'Rp' }, + { name: 'Hungarian forint', code: 'HUF', symbol: 'Ft' }, + { name: 'Czech koruna', code: 'CZK', symbol: 'Kč' }, + { name: 'Israeli new shekel', code: 'ILS', symbol: '₪' }, + { name: 'Chilean peso', code: 'CLP', symbol: 'CLP$' }, + { name: 'Philippine peso', code: 'PHP', symbol: '₱' }, + { name: 'UAE dirham', code: 'AED', symbol: 'د.إ' }, + { name: 'Colombian peso', code: 'COP', symbol: 'COL$' }, + { name: 'Saudi riyal', code: 'SAR', symbol: '﷼' }, + { name: 'Malaysian ringgit', code: 'MYR', symbol: 'RM' }, + { name: 'Romanian leu', code: 'RON', symbol: 'L' }, +]; + export function getUiSettingDefaults() { const weekdays = moment.weekdays().slice(); const [defaultWeekday] = weekdays; @@ -34,6 +72,22 @@ export function getUiSettingDefaults() { }), ]; + const locales = ['detect', ...i18n.getKnownLocales()]; + const localeDisplay = Object.fromEntries( + [ + [ + 'detect', + i18n.translate('kbn.advancedSettings.locale.detectFromBrowserLabel', { + defaultMessage: 'Detect locale from browser locale', + }), + ], + ].concat( + Object.entries(i18n.getKnownLocalesWithDisplay()).map(([id, { name, native }]) => { + return [id, native ? `${native} - ${name}` : name]; + }) + ) + ); + const luceneQueryLanguageLabel = i18n.translate( 'kbn.advancedSettings.searchQueryLanguageLucene', { @@ -717,110 +771,76 @@ export function getUiSettingDefaults() { }, }), }, - 'format:number:defaultPattern': { - name: i18n.translate('kbn.advancedSettings.format.numberFormatTitle', { - defaultMessage: 'Number format', - }), - value: '0,0.[000]', - type: 'string', - description: i18n.translate('kbn.advancedSettings.format.numberFormatText', { - defaultMessage: 'Default {numeralFormatLink} for the "number" format', - description: - 'Part of composite text: kbn.advancedSettings.format.numberFormatText + ' + - 'kbn.advancedSettings.format.numberFormat.numeralFormatLinkText', - values: { - numeralFormatLink: - '' + - i18n.translate('kbn.advancedSettings.format.numberFormat.numeralFormatLinkText', { - defaultMessage: 'numeral format', - }) + - '', - }, - }), - }, - 'format:bytes:defaultPattern': { - name: i18n.translate('kbn.advancedSettings.format.bytesFormatTitle', { - defaultMessage: 'Bytes format', + 'format:defaultLocale': { + name: i18n.translate('kbn.advancedSettings.format.formattingLocaleTitle', { + defaultMessage: 'Default number display locale', }), - value: '0,0.[0]b', - type: 'string', - description: i18n.translate('kbn.advancedSettings.format.bytesFormatText', { - defaultMessage: 'Default {numeralFormatLink} for the "bytes" format', - description: - 'Part of composite text: kbn.advancedSettings.format.bytesFormatText + ' + - 'kbn.advancedSettings.format.bytesFormat.numeralFormatLinkText', - values: { - numeralFormatLink: - '' + - i18n.translate('kbn.advancedSettings.format.bytesFormat.numeralFormatLinkText', { - defaultMessage: 'numeral format', - }) + - '', - }, + value: 'detect', + type: 'select', + options: locales, + optionLabels: localeDisplay, + description: i18n.translate('kbn.advancedSettings.format.formattingLocaleText', { + defaultMessage: `This locale will affect number formatting for most formatters.`, }), }, - 'format:percent:defaultPattern': { - name: i18n.translate('kbn.advancedSettings.format.percentFormatTitle', { - defaultMessage: 'Percent format', + 'format:number:defaultLocale': { + name: i18n.translate('kbn.advancedSettings.format.formattingLocaleTitle', { + defaultMessage: 'Numeral.js formatting locale', }), - value: '0,0.[000]%', - type: 'string', - description: i18n.translate('kbn.advancedSettings.format.percentFormatText', { - defaultMessage: 'Default {numeralFormatLink} for the "percent" format', + value: 'en', + type: 'select', + options: numeralLanguageIds, + optionLabels: Object.fromEntries( + numeralLanguages.map(language => [language.id, language.name]) + ), + description: i18n.translate('kbn.advancedSettings.format.formattingLocaleText', { + defaultMessage: `{numeralLanguageLink} locale. This affects the custom numeral.js formatter and the bytes formatter.`, description: - 'Part of composite text: kbn.advancedSettings.format.percentFormatText + ' + - 'kbn.advancedSettings.format.percentFormat.numeralFormatLinkText', + 'Part of composite text: kbn.advancedSettings.format.formattingLocale.numeralLanguageLinkText + ' + + 'kbn.advancedSettings.format.formattingLocaleText', values: { - numeralFormatLink: + numeralLanguageLink: '' + - i18n.translate('kbn.advancedSettings.format.percentFormat.numeralFormatLinkText', { - defaultMessage: 'numeral format', + i18n.translate('kbn.advancedSettings.format.formattingLocale.numeralLanguageLinkText', { + defaultMessage: 'Numeral language', }) + '', }, }), }, - 'format:currency:defaultPattern': { - name: i18n.translate('kbn.advancedSettings.format.currencyFormatTitle', { - defaultMessage: 'Currency format', + 'format:number:defaultPattern': { + name: i18n.translate('kbn.advancedSettings.format.numberFormatTitle', { + defaultMessage: 'numeral.js default pattern', }), - value: '($0,0.[00])', + value: '0,0.[000]', type: 'string', - description: i18n.translate('kbn.advancedSettings.format.currencyFormatText', { - defaultMessage: 'Default {numeralFormatLink} for the "currency" format', + description: i18n.translate('kbn.advancedSettings.format.numberFormatText', { + defaultMessage: + 'Default {numeralFormatLink} for the "Custom numeral.js" format. Does not affect other formats.', description: - 'Part of composite text: kbn.advancedSettings.format.currencyFormatText + ' + - 'kbn.advancedSettings.format.currencyFormat.numeralFormatLinkText', + 'Part of composite text: kbn.advancedSettings.format.numberFormatText + ' + + 'kbn.advancedSettings.format.numberFormat.numeralFormatLinkText', values: { numeralFormatLink: '' + - i18n.translate('kbn.advancedSettings.format.currencyFormat.numeralFormatLinkText', { + i18n.translate('kbn.advancedSettings.format.numberFormat.numeralFormatLinkText', { defaultMessage: 'numeral format', }) + '', }, }), }, - 'format:number:defaultLocale': { - name: i18n.translate('kbn.advancedSettings.format.formattingLocaleTitle', { - defaultMessage: 'Formatting locale', + 'format:currency:defaultCurrency': { + name: i18n.translate('kbn.advancedSettings.format.currencyTitle', { + defaultMessage: 'Default currency', }), - value: 'en', + value: 'USD', type: 'select', - options: numeralLanguageIds, - description: i18n.translate('kbn.advancedSettings.format.formattingLocaleText', { - defaultMessage: `{numeralLanguageLink} locale`, - description: - 'Part of composite text: kbn.advancedSettings.format.formattingLocale.numeralLanguageLinkText + ' + - 'kbn.advancedSettings.format.formattingLocaleText', - values: { - numeralLanguageLink: - '' + - i18n.translate('kbn.advancedSettings.format.formattingLocale.numeralLanguageLinkText', { - defaultMessage: 'Numeral language', - }) + - '', - }, + options: topCurrencies.map(c => c.code), + optionLabels: Object.fromEntries(topCurrencies.map(c => [c.code, `${c.name} (${c.symbol})`])), + description: i18n.translate('kbn.advancedSettings.format.currencyDescription', { + defaultMessage: + 'The currency used for all currency formatting by default. Currency can be changed for a single field using the index management section. If a currency is unlisted here, it can also be set in the index management section.', }), }, 'savedObjects:perPage': { diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/__tests__/tick_formatter.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/__tests__/tick_formatter.js index f31af4e846305..20d8b9391b01a 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/__tests__/tick_formatter.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/__tests__/tick_formatter.js @@ -26,21 +26,21 @@ describe('createTickFormatter(format, template)', () => { expect(fn(1.5556)).to.equal('1.56'); }); - it('returns a percent with percent formatter', () => { - const config = { - 'format:percent:defaultPattern': '0.[00]%', - }; - const fn = createTickFormatter('percent', null, key => config[key]); - expect(fn(0.5556)).to.equal('55.56%'); - }); + // it('returns a percent with percent formatter', () => { + // const config = { + // 'format:percent:defaultPattern': '0.[00]%', + // }; + // const fn = createTickFormatter('percent', null, key => config[key]); + // expect(fn(0.5556)).to.equal('55.56%'); + // }); - it('returns a byte formatted string with byte formatter', () => { - const config = { - 'format:bytes:defaultPattern': '0.0b', - }; - const fn = createTickFormatter('bytes', null, key => config[key]); - expect(fn(1500 ^ 10)).to.equal('1.5KB'); - }); + // it('returns a byte formatted string with byte formatter', () => { + // const config = { + // 'format:bytes:defaultPattern': '0.0b', + // }; + // const fn = createTickFormatter('bytes', null, key => config[key]); + // expect(fn(1500 ^ 10)).to.equal('1.5KB'); + // }); it('returns a custom formatted string with custom formatter', () => { const fn = createTickFormatter('0.0a'); diff --git a/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts b/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts index 4ca181d3f69d4..2b66574896302 100644 --- a/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts +++ b/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts @@ -18,7 +18,7 @@ */ import { FieldFormatsService } from './field_formats_service'; -import { NumberFormat } from '../../../../plugins/data/public'; +import { DefaultNumberFormat } from '../../../../plugins/data/public'; const getConfig = (key: string) => { switch (key) { @@ -36,7 +36,7 @@ describe('FieldFormatsService', () => { let fieldFormatsService: FieldFormatsService; beforeEach(() => { - const fieldFormatClasses = [NumberFormat]; + const fieldFormatClasses = [DefaultNumberFormat]; fieldFormatsService = new FieldFormatsService(fieldFormatClasses, getConfig); }); diff --git a/src/legacy/ui/public/agg_types/buckets/range.test.ts b/src/legacy/ui/public/agg_types/buckets/range.test.ts index 5db7eb3c2d8e9..22b92874f1f1a 100644 --- a/src/legacy/ui/public/agg_types/buckets/range.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/range.test.ts @@ -19,7 +19,7 @@ import { AggConfigs } from '../agg_configs'; import { BUCKET_TYPES } from './bucket_agg_types'; -import { NumberFormat } from '../../../../../plugins/data/public'; +import { BytesFormat } from '../../../../../plugins/data/public'; jest.mock('ui/new_platform'); @@ -47,12 +47,7 @@ describe('Range Agg', () => { const getAggConfigs = () => { const field = { name: 'bytes', - format: new NumberFormat( - { - pattern: '0,0.[000] b', - }, - () => {} - ), + format: new BytesFormat({}, () => {}), }; const indexPattern = { diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/bytes.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/bytes.js index 497795eb88e2a..dcfc3a3061274 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/bytes.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/bytes.js @@ -18,7 +18,6 @@ */ import React, { Fragment } from 'react'; -import { FormattedMessage } from '@kbn/i18n/react'; import { DefaultFormatEditor } from '../default'; import { FormatEditorSamples } from '../../samples'; @@ -36,15 +35,9 @@ export class BytesFormatEditor extends DefaultFormatEditor { render() { const { samples } = this.state; - const locale = this.props.format.getConfig('format:number:defaultLocale'); return ( - ); diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js index 6b4f85f0a52fb..7d842cccd60c5 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js @@ -18,8 +18,9 @@ */ import React, { Fragment } from 'react'; +import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFormRow, EuiComboBox } from '@elastic/eui'; +import { EuiFormRow, EuiComboBox, EuiFieldText } from '@elastic/eui'; import { DefaultNumberFormatEditor } from '../default_number'; import { FormatEditorSamples } from '../../samples'; @@ -76,6 +77,7 @@ export class CurrencyFormatEditor extends DefaultNumberFormatEditor { this.state = { ...this.state, sampleInputs: [1234, 99.9999, 5150000.0001, 0.00005], + hasOther: false, }; } @@ -89,6 +91,13 @@ export class CurrencyFormatEditor extends DefaultNumberFormatEditor { currencyLabel = `${currencyMatch.name} (${currencyMatch.code}) ${currencyMatch.symbol}`; } + const otherLabel = { + value: null, + label: i18n.translate('common.ui.fieldEditor.currency.otherCurrencyLabel', { + defaultMessage: 'Other currency', + }), + }; + return ( ({ - value: cur.code, - label: `${cur.name} (${cur.code}) ${cur.symbol}`, - }))} + options={topCurrencies + .map(cur => ({ + value: cur.code, + label: `${cur.name} (${cur.code}) ${cur.symbol}`, + })) + .concat([otherLabel])} onChange={choices => { - this.onChange({ currencyCode: choices[0].value }); + if (choices[0].value) { + // There is no value for the "Other" currency + this.onChange({ currencyCode: choices[0].value }); + this.setState({ hasOther: false }); + } else { + this.setState({ hasOther: true }); + } }} /> - {this.renderLocaleOverride()} + {this.state.hasOther ? ( + + { + this.onChange({ currencyCode: e.target.value ? e.target.value.toUpperCase() : '' }); + }} + /> + + ) : null} + + {this.renderDecimalSelector()} diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/number.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/number.js index 7c61e16796845..bfc4f4421e228 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/number.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/number.js @@ -19,155 +19,13 @@ import React, { Fragment } from 'react'; -import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; -import { EuiFormRow, EuiFieldNumber, EuiText, EuiSpacer, EuiSwitch, EuiSelect } from '@elastic/eui'; +import { EuiFormRow, EuiFieldNumber } from '@elastic/eui'; import { DefaultFormatEditor } from '../default'; import { FormatEditorSamples } from '../../samples'; -// List of supported Chrome locales -const locales = { - af: 'Afrikaans', - am: 'Amharic', - ar: 'Arabic', - az: 'Azerbaijani', - be: 'Belarusian', - bg: 'Bulgarian', - bh: 'Bihari', - bn: 'Bengali', - br: 'Breton', - bs: 'Bosnian', - ca: 'Catalan', - co: 'Corsican', - cs: 'Czech', - cy: 'Welsh', - da: 'Danish', - de: 'German', - 'de-AT': 'German (Austria)', - 'de-CH': 'German (Switzerland)', - 'de-DE': 'German (Germany)', - el: 'Greek', - en: 'English', - 'en-US': 'English (US)', - 'en-IN': 'English (India)', - 'en-NG': 'English (Nigeria)', - 'en-PK': 'English (Pakistan)', - 'en-PH': 'English (Philippines)', - 'en-GB': 'English (UK)', - 'en-CA': 'English (Canada)', - 'en-AU': 'English (Australia)', - 'en-NZ': 'English (New Zealand)', - 'en-ZA': 'English (South Africa)', - eo: 'Esperanto', - es: 'Spanish', - 'es-419': 'Spanish (Latin America)', - et: 'Estonian', - eu: 'Basque', - fa: 'Persian', - fi: 'Finnish', - fil: 'Filipino', - fo: 'Faroese', - fr: 'French', - 'fr-CA': 'French (Canada)', - 'fr-CH': 'French (Switzerland)', - 'fr-FR': 'French (France)', - fy: 'Frisian', - ga: 'Irish', - gd: 'Scots Gaelic', - gl: 'Galician', - gn: 'Guarani', - gu: 'Gujarati', - ha: 'Hausa', - haw: 'Hawaiian', - he: 'Hebrew', - hi: 'Hindi', - hr: 'Croatian', - hu: 'Hungarian', - hy: 'Armenian', - ia: 'Interlingua', - id: 'Indonesian', - is: 'Icelandic', - it: 'Italian', - 'it-CH': 'Italian (Switzerland)', - 'it-IT': 'Italian (Italy)', - ja: 'Japanese', - jw: 'Javanese', - ka: 'Georgian', - kk: 'Kazakh', - km: 'Cambodian', - kn: 'Kannada', - ko: 'Korean', - ku: 'Kurdish', - ky: 'Kyrgyz', - la: 'Latin', - ln: 'Lingala', - lo: 'Laothian', - lt: 'Lithuanian', - lv: 'Latvian', - mk: 'Macedonian', - ml: 'Malayalam', - mn: 'Mongolian', - mo: 'Moldavian', - mr: 'Marathi', - ms: 'Malay', - mt: 'Maltese', - nb: 'Norwegian (Bokmal)', - ne: 'Nepali', - nl: 'Dutch', - nn: 'Norwegian (Nynorsk)', - no: 'Norwegian', - oc: 'Occitan', - om: 'Oromo', - or: 'Oriya', - pa: 'Punjabi', - pl: 'Polish', - ps: 'Pashto', - pt: 'Portuguese', - 'pt-BR': 'Portuguese (Brazil)', - 'pt-PT': 'Portuguese (Portugal)', - qu: 'Quechua', - rm: 'Romansh', - ro: 'Romanian', - ru: 'Russian', - sd: 'Sindhi', - sh: 'Serbo-Croatian', - si: 'Sinhalese', - sk: 'Slovak', - sl: 'Slovenian', - sn: 'Shona', - so: 'Somali', - sq: 'Albanian', - sr: 'Serbian', - st: 'Sesotho', - su: 'Sundanese', - sv: 'Swedish', - sw: 'Swahili', - ta: 'Tamil', - te: 'Telugu', - tg: 'Tajik', - th: 'Thai', - ti: 'Tigrinya', - tk: 'Turkmen', - to: 'Tonga', - tr: 'Turkish', - tt: 'Tatar', - tw: 'Twi', - ug: 'Uighur', - uk: 'Ukrainian', - ur: 'Urdu', - uz: 'Uzbek', - vi: 'Vietnamese', - xh: 'Xhosa', - yi: 'Yiddish', - yo: 'Yoruba', - zh: 'Chinese', - 'zh-CN': 'Chinese (Simplified)', - 'zh-TW': 'Chinese (Traditional)', - zu: 'Zulu', -}; - export class DefaultNumberFormatEditor extends DefaultFormatEditor { static formatId = 'default_number'; @@ -184,89 +42,14 @@ export class DefaultNumberFormatEditor extends DefaultFormatEditor { 0.00000000000000123456789, 19900000000000000000000, ], - supportedLocales: Intl.NumberFormat.supportedLocalesOf(Object.keys(locales)), }; } - renderLocaleOverride = () => { - const { formatParams } = this.props; - - const defaultLocale = this.props.format.getConfig('format:number:defaultLocale'); - - return ( - - - } - > - <> - - {i18n.translate('common.ui.fieldEditor.numberLocaleLabel', { - defaultMessage: - 'Number formatting is set to ({locale}) by the Kibana advanced setting "format:number:defaultLocale".', - values: { locale: defaultLocale }, - })} - - - - - { - this.onChange({ localeOverride: e.target.checked }); - }} - /> - - - - {formatParams.localeOverride ? ( - - } - > - ({ - value: localeId, - text: locales[localeId], - })) - )} - onChange={e => { - this.onChange({ localeOverride: e.target.value }); - }} - value={formatParams.localeOverride || null} - /> - - ) : null} - - ); - }; - - render() { + renderDecimalSelector = () => { const { formatParams } = this.props; - const { samples } = this.state; return ( - + <> + + ); + }; + + render() { + const { samples } = this.state; - {this.renderLocaleOverride()} + return ( + + {this.renderDecimalSelector()} diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.js index e5c2ca91d936f..99f21f580852a 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.js @@ -17,23 +17,9 @@ * under the License. */ -import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; -// @ts-ignore -import numeralLanguages from '@elastic/numeral/languages'; - -import { - EuiCode, - EuiText, - EuiFieldText, - EuiFormRow, - EuiIcon, - EuiLink, - EuiSelect, - EuiSwitch, - EuiSpacer, -} from '@elastic/eui'; +import { EuiCode, EuiFieldText, EuiFormRow, EuiIcon, EuiLink } from '@elastic/eui'; import { DefaultFormatEditor } from '../default'; @@ -61,8 +47,6 @@ export class CustomNumberFormatEditor extends DefaultFormatEditor { const { format, formatParams } = this.props; const { error, samples } = this.state; const defaultPattern = format.getParamDefaults().pattern; - const overrideLocale = formatParams.localeOverride; - const locale = format.getConfig('format:number:defaultLocale'); return ( @@ -99,63 +83,6 @@ export class CustomNumberFormatEditor extends DefaultFormatEditor { /> - - } - > - <> - - {i18n.translate('common.ui.fieldEditor.numberLocaleLabel', { - defaultMessage: - 'Number formatting is set to ({locale}) by the Kibana advanced setting "format:number:defaultLocale".', - values: { locale }, - })} - - - - - { - if (e.target.checked) { - this.onChange({ localeOverride: locale }); - } else { - this.onChange({ localeOverride: false }); - } - }} - /> - - - - {overrideLocale ? ( - - } - > - ({ - value: lang.id, - text: lang.name || lang.id, - }))} - onChange={e => { - this.onChange({ localeOverride: e.target.value }); - }} - value={overrideLocale} - /> - - ) : null} - ); diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.test.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.test.js index 9d97feaa75a6a..e568004b150ae 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.test.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/number.test.js @@ -20,7 +20,7 @@ import React from 'react'; import { shallow } from 'enzyme'; -import { NumberFormatEditor } from './number'; +import { CustomNumberFormatEditor } from './number'; const fieldType = 'number'; const format = { @@ -35,12 +35,12 @@ const onError = jest.fn(); describe('NumberFormatEditor', () => { it('should have a formatId', () => { - expect(NumberFormatEditor.formatId).toEqual('number'); + expect(CustomNumberFormatEditor.formatId).toEqual('number'); }); it('should render normally', async () => { const component = shallow( - - {this.renderLocaleOverride()} + {this.renderDecimalSelector()} diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/index.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/short_number/index.js similarity index 93% rename from src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/index.js rename to src/legacy/ui/public/field_editor/components/field_format_editor/editors/short_number/index.js index 9a9aedd660797..0e0bf0a691087 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/index.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/short_number/index.js @@ -17,4 +17,4 @@ * under the License. */ -export { LargeNumberFormatEditor } from './large_number'; +export { ShortNumberFormatEditor } from './short_number'; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/large_number.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/short_number/short_number.js similarity index 90% rename from src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/large_number.js rename to src/legacy/ui/public/field_editor/components/field_format_editor/editors/short_number/short_number.js index f0e0e3c53c33f..a60f7151b34b5 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/large_number/large_number.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/short_number/short_number.js @@ -21,8 +21,8 @@ import React, { Fragment } from 'react'; import { DefaultNumberFormatEditor } from '../default_number'; import { FormatEditorSamples } from '../../samples'; -export class LargeNumberFormatEditor extends DefaultNumberFormatEditor { - static formatId = 'large_number'; +export class ShortNumberFormatEditor extends DefaultNumberFormatEditor { + static formatId = 'short_number'; constructor(props) { super(props); @@ -38,8 +38,6 @@ export class LargeNumberFormatEditor extends DefaultNumberFormatEditor { return ( - {this.renderLocaleOverride()} - ); diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js b/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js index fce19c0add571..7c3000747950f 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js @@ -25,7 +25,7 @@ import { DateNanosFormatEditor } from './editors/date_nanos'; import { DefaultNumberFormatEditor } from './editors/default_number'; import { CustomNumberFormatEditor } from './editors/number'; import { DurationFormatEditor } from './editors/duration'; -import { LargeNumberFormatEditor } from './editors/large_number'; +import { ShortNumberFormatEditor } from './editors/short_number'; import { PercentFormatEditor } from './editors/percent'; import { StaticLookupFormatEditor } from './editors/static_lookup'; import { StringFormatEditor } from './editors/string'; @@ -41,7 +41,7 @@ export const editors = [ CustomNumberFormatEditor, DefaultNumberFormatEditor, DurationFormatEditor, - LargeNumberFormatEditor, + ShortNumberFormatEditor, PercentFormatEditor, StaticLookupFormatEditor, StringFormatEditor, diff --git a/src/plugins/data/common/field_formats/converters/bytes.ts b/src/plugins/data/common/field_formats/converters/bytes.ts index 20f49975c30ab..435c415cb7480 100644 --- a/src/plugins/data/common/field_formats/converters/bytes.ts +++ b/src/plugins/data/common/field_formats/converters/bytes.ts @@ -29,4 +29,8 @@ export class BytesFormat extends NumeralFormat { id = BytesFormat.id; title = BytesFormat.title; + + getParamDefaults = () => ({ + pattern: '0,0.0b', + }); } diff --git a/src/plugins/data/common/field_formats/converters/currency.ts b/src/plugins/data/common/field_formats/converters/currency.ts index 83d45639bc854..fead370e7bab3 100644 --- a/src/plugins/data/common/field_formats/converters/currency.ts +++ b/src/plugins/data/common/field_formats/converters/currency.ts @@ -30,8 +30,9 @@ export class CurrencyFormat extends IntlNumberFormat { static fieldType = KBN_FIELD_TYPES.NUMBER; getParamDefaults = () => ({ - currencyCode: 'USD', - localeOverride: false, + currencyCode: this.getConfig!('format:currency:defaultCurrency'), + minDecimals: 0, + maxDecimals: 2, }); id = CurrencyFormat.id; @@ -41,6 +42,8 @@ export class CurrencyFormat extends IntlNumberFormat { return { style: 'currency', currency: this.param('currencyCode'), + minimumFractionDigits: this.param('minDecimals'), + maximumFractionDigits: this.param('maxDecimals'), }; }; } diff --git a/src/plugins/data/common/field_formats/converters/default_number.ts b/src/plugins/data/common/field_formats/converters/default_number.ts index 3f31e2102cfbe..4e60f9aa03fc6 100644 --- a/src/plugins/data/common/field_formats/converters/default_number.ts +++ b/src/plugins/data/common/field_formats/converters/default_number.ts @@ -32,7 +32,6 @@ export class DefaultNumberFormat extends IntlNumberFormat { title = DefaultNumberFormat.title; getParamDefaults = () => ({ - localeOverride: false, minDecimals: 0, maxDecimals: 3, }); diff --git a/src/plugins/data/common/field_formats/converters/index.ts b/src/plugins/data/common/field_formats/converters/index.ts index f8d42a7c0383b..e7bb0a4f8c180 100644 --- a/src/plugins/data/common/field_formats/converters/index.ts +++ b/src/plugins/data/common/field_formats/converters/index.ts @@ -27,7 +27,7 @@ export { DateNanosFormat } from './date_nanos'; export { RelativeDateFormat } from './relative_date'; export { DurationFormat } from './duration'; export { IpFormat } from './ip'; -export { LargeNumberFormat } from './large_number'; +export { ShortNumberFormat } from './short_number'; export { PercentFormat } from './percent'; export { StringFormat } from './string'; export { SourceFormat } from './source'; diff --git a/src/plugins/data/common/field_formats/converters/intl_number_format.ts b/src/plugins/data/common/field_formats/converters/intl_number_format.ts index 40e189ecbb4e3..182120a8b7524 100644 --- a/src/plugins/data/common/field_formats/converters/intl_number_format.ts +++ b/src/plugins/data/common/field_formats/converters/intl_number_format.ts @@ -19,7 +19,6 @@ // import { i18n } from '@kbn/i18n'; import { FieldFormat } from '../field_format'; -// import { FIELD_FORMAT_IDS } from '../types'; import { TextContextTypeConvert } from '../types'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; @@ -29,9 +28,7 @@ export abstract class IntlNumberFormat extends FieldFormat { abstract id: string; abstract title: string; - getParamDefaults = () => ({ - localeOverride: false, - }); + getParamDefaults = () => ({}); abstract getArguments: () => Record; @@ -44,12 +41,19 @@ export abstract class IntlNumberFormat extends FieldFormat { if (isNaN(val)) return ''; - const locale = - this.param('localeOverride') || - (this.getConfig && this.getConfig('format:number:defaultLocale')) || - 'en'; + const defaultLocale = this.getConfig && this.getConfig('format:defaultLocale'); + let locales = [defaultLocale, 'en']; + if (defaultLocale === 'detect') { + locales = navigator.languages + ? navigator.languages.concat(['en']) + : [navigator.language].concat(locales); + } + // const locale = (this.getConfig && this.getConfig('format:defaultLocale')) || 'en'; + // if (locale === 'detect') { + // locale = + // } - const inst = new Intl.NumberFormat(locale, this.getArguments()); + const inst = new Intl.NumberFormat(locales, this.getArguments()); return inst.format(val); } diff --git a/src/plugins/data/common/field_formats/converters/number.ts b/src/plugins/data/common/field_formats/converters/number.ts index 865cc6226c178..55c149bc063dc 100644 --- a/src/plugins/data/common/field_formats/converters/number.ts +++ b/src/plugins/data/common/field_formats/converters/number.ts @@ -24,7 +24,7 @@ import { FIELD_FORMAT_IDS } from '../types'; export class CustomNumberFormat extends NumeralFormat { static id = FIELD_FORMAT_IDS.CUSTOM_NUMBER; static title = i18n.translate('data.common.fieldFormats.number.title', { - defaultMessage: 'Custom numeral.js format string', + defaultMessage: 'Custom numeral.js format', }); id = CustomNumberFormat.id; diff --git a/src/plugins/data/common/field_formats/converters/numeral.ts b/src/plugins/data/common/field_formats/converters/numeral.ts index 2cfe776a1871d..c93ff15633503 100644 --- a/src/plugins/data/common/field_formats/converters/numeral.ts +++ b/src/plugins/data/common/field_formats/converters/numeral.ts @@ -31,6 +31,12 @@ numeralLanguages.forEach((numeralLanguage: Record) => { numeral.language(numeralLanguage.id, numeralLanguage.lang); }); +const supportedNumeralLanguages = Object.fromEntries( + numeralLanguages.map((numeralLanguage: Record) => { + return [numeralLanguage.id, true]; + }) +); + export abstract class NumeralFormat extends FieldFormat { static fieldType = KBN_FIELD_TYPES.NUMBER; @@ -39,7 +45,6 @@ export abstract class NumeralFormat extends FieldFormat { getParamDefaults = () => ({ pattern: this.getConfig!(`format:${this.id}:defaultPattern`), - localeOverride: false, }); protected getConvertedValue(val: any, pattern?: string): string { @@ -52,14 +57,23 @@ export abstract class NumeralFormat extends FieldFormat { if (isNaN(val)) return ''; const previousLocale = numeral.language(); - if (this.param('localeOverride')) { - numeral.language(this.param('localeOverride')); - } else { - const defaultLocale = - (this.getConfig && this.getConfig('format:number:defaultLocale')) || 'en'; - numeral.language(defaultLocale); + const defaultLocale = this.getConfig && this.getConfig('format:number:defaultLocale'); + let locale = 'en'; + if (defaultLocale === 'detect') { + if (navigator.languages && navigator.languages.length) { + const match = navigator.languages.find(lang => { + return !!supportedNumeralLanguages[lang]; + }); + if (match) { + locale = match; + } + } else if (navigator.language && !!supportedNumeralLanguages[navigator.language]) { + locale = navigator.language; + } } + numeral.language(locale); + const formatted = numeralInst.set(val).format(pattern || this.param('pattern')); numeral.language(previousLocale); diff --git a/src/plugins/data/common/field_formats/converters/percent.test.ts b/src/plugins/data/common/field_formats/converters/percent.test.ts index 8b26564814af3..3af8aefefa6ba 100644 --- a/src/plugins/data/common/field_formats/converters/percent.test.ts +++ b/src/plugins/data/common/field_formats/converters/percent.test.ts @@ -32,8 +32,8 @@ describe('PercentFormat', () => { expect(formatter.convert(0.99999)).toBe('99.999%'); }); - test('custom pattern', () => { - const formatter = new PercentFormat({ pattern: '0,0%' }, getConfig); + test('custom number of digits', () => { + const formatter = new PercentFormat({ maxDecimals: 0 }, getConfig); expect(formatter.convert('0.99999')).toBe('100%'); }); diff --git a/src/plugins/data/common/field_formats/converters/percent.ts b/src/plugins/data/common/field_formats/converters/percent.ts index 6f63c9f6a24ce..913623b2c6b93 100644 --- a/src/plugins/data/common/field_formats/converters/percent.ts +++ b/src/plugins/data/common/field_formats/converters/percent.ts @@ -30,5 +30,14 @@ export class PercentFormat extends IntlNumberFormat { id = PercentFormat.id; title = PercentFormat.title; - getArguments = () => ({ style: 'percent' }); + getParamDefaults = () => ({ + minDecimals: 0, + maxDecimals: 3, + }); + + getArguments = () => ({ + style: 'percent', + minimumFractionDigits: this.param('minDecimals'), + maximumFractionDigits: this.param('maxDecimals'), + }); } diff --git a/src/plugins/data/common/field_formats/converters/large_number.ts b/src/plugins/data/common/field_formats/converters/short_number.ts similarity index 79% rename from src/plugins/data/common/field_formats/converters/large_number.ts rename to src/plugins/data/common/field_formats/converters/short_number.ts index 1edcf5fdc06f0..a79d274c7c716 100644 --- a/src/plugins/data/common/field_formats/converters/large_number.ts +++ b/src/plugins/data/common/field_formats/converters/short_number.ts @@ -21,14 +21,14 @@ import { i18n } from '@kbn/i18n'; import { IntlNumberFormat } from './intl_number_format'; import { FIELD_FORMAT_IDS } from '../types'; -export class LargeNumberFormat extends IntlNumberFormat { - static id = FIELD_FORMAT_IDS.LARGE_NUMBER; - static title = i18n.translate('data.common.fieldFormats.large_number.title', { - defaultMessage: 'Shorter number', +export class ShortNumberFormat extends IntlNumberFormat { + static id = FIELD_FORMAT_IDS.SHORT_NUMBER; + static title = i18n.translate('data.common.fieldFormats.short_number.title', { + defaultMessage: 'Short number', }); - id = LargeNumberFormat.id; - title = LargeNumberFormat.title; + id = ShortNumberFormat.id; + title = ShortNumberFormat.title; getArguments = () => ({ style: 'decimal', diff --git a/src/plugins/data/common/field_formats/types.ts b/src/plugins/data/common/field_formats/types.ts index 64b3794688063..2fdb84f359ef6 100644 --- a/src/plugins/data/common/field_formats/types.ts +++ b/src/plugins/data/common/field_formats/types.ts @@ -61,7 +61,7 @@ export enum FIELD_FORMAT_IDS { DATE_NANOS = 'date_nanos', DURATION = 'duration', IP = 'ip', - LARGE_NUMBER = 'large_number', + SHORT_NUMBER = 'short_number', PERCENT = 'percent', RELATIVE_DATE = 'relative_date', STATIC_LOOKUP = 'static_lookup', diff --git a/src/plugins/data/public/field_formats_provider/field_formats_service.ts b/src/plugins/data/public/field_formats_provider/field_formats_service.ts index b986fab315ebf..a5ca4d8b50f18 100644 --- a/src/plugins/data/public/field_formats_provider/field_formats_service.ts +++ b/src/plugins/data/public/field_formats_provider/field_formats_service.ts @@ -29,7 +29,7 @@ import { DateNanosFormat, DurationFormat, IpFormat, - LargeNumberFormat, + ShortNumberFormat, DefaultNumberFormat, CustomNumberFormat, PercentFormat, @@ -56,7 +56,7 @@ export class FieldFormatsService { DateNanosFormat, DurationFormat, IpFormat, - LargeNumberFormat, + ShortNumberFormat, DefaultNumberFormat, CustomNumberFormat, PercentFormat, diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts index 967887764237d..6517498168e7c 100644 --- a/src/plugins/data/public/index.ts +++ b/src/plugins/data/public/index.ts @@ -75,7 +75,9 @@ export { FieldFormat, getHighlightRequest, // only used in search source IpFormat, - NumberFormat, + DefaultNumberFormat, + CustomNumberFormat, + ShortNumberFormat, PercentFormat, RelativeDateFormat, SourceFormat, diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index fe96c494bd9ff..bedb29dd1badd 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -75,7 +75,8 @@ export { DurationFormat, FieldFormat, IpFormat, - NumberFormat, + DefaultNumberFormat, + CustomNumberFormat, PercentFormat, RelativeDateFormat, SourceFormat, diff --git a/src/test_utils/public/stub_field_formats.ts b/src/test_utils/public/stub_field_formats.ts index 55e790b470286..40111aa75f0c8 100644 --- a/src/test_utils/public/stub_field_formats.ts +++ b/src/test_utils/public/stub_field_formats.ts @@ -27,7 +27,7 @@ import { DateNanosFormat, DurationFormat, IpFormat, - LargeNumberFormat, + ShortNumberFormat, DefaultNumberFormat, CustomNumberFormat, PercentFormat, @@ -46,7 +46,7 @@ export const getFieldFormatsRegistry = (core: CoreSetup) => { BoolFormat, BytesFormat, ColorFormat, - LargeNumberFormat, + ShortNumberFormat, DefaultNumberFormat, CustomNumberFormat, DateFormat, diff --git a/x-pack/package.json b/x-pack/package.json index d513e4ed34965..e85567fb7cd45 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -182,7 +182,7 @@ "@elastic/filesaver": "1.1.2", "@elastic/maki": "6.1.0", "@elastic/node-crypto": "^1.0.0", - "@elastic/numeral": "2.3.3", + "@elastic/numeral": "2.3.5", "@kbn/babel-preset": "1.0.0", "@kbn/config-schema": "1.0.0", "@kbn/i18n": "1.0.0", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 09d71814e5bf0..b7749e7effb9e 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1368,8 +1368,6 @@ "kbn.advancedSettings.format.defaultTypeMapText": "各フィールドタイプにデフォルトで使用するフォーマット名のマップです。フィールドタイプが特に指定されていない場合は {defaultFormat} が使用されます", "kbn.advancedSettings.format.defaultTypeMapTitle": "フィールドタイプフォーマット名", "kbn.advancedSettings.format.formattingLocale.numeralLanguageLinkText": "数字言語", - "kbn.advancedSettings.format.formattingLocaleText": "{numeralLanguageLink} ロケール", - "kbn.advancedSettings.format.formattingLocaleTitle": "フォーマットロケール", "kbn.advancedSettings.format.numberFormat.numeralFormatLinkText": "数字フォーマット", "kbn.advancedSettings.format.numberFormatText": "「数字」フォーマットのデフォルト {numeralFormatLink} です", "kbn.advancedSettings.format.numberFormatTitle": "数字フォーマット", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index e221cba874bcd..bf0abea419d61 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1369,8 +1369,6 @@ "kbn.advancedSettings.format.defaultTypeMapText": "要默认用于每个字段类型的格式名称的映射。如果未显式提及字段类型,则将使用{defaultFormat}", "kbn.advancedSettings.format.defaultTypeMapTitle": "字段类型格式名称", "kbn.advancedSettings.format.formattingLocale.numeralLanguageLinkText": "数值语言", - "kbn.advancedSettings.format.formattingLocaleText": "{numeralLanguageLink}区域设置", - "kbn.advancedSettings.format.formattingLocaleTitle": "格式区域设置", "kbn.advancedSettings.format.numberFormat.numeralFormatLinkText": "数值格式", "kbn.advancedSettings.format.numberFormatText": "“数字”格式的默认{numeralFormatLink}", "kbn.advancedSettings.format.numberFormatTitle": "数字格式", diff --git a/yarn.lock b/yarn.lock index 9631ca271295e..c03d71831b315 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1514,10 +1514,10 @@ resolved "https://registry.yarnpkg.com/@elastic/node-crypto/-/node-crypto-1.0.0.tgz#4d325df333fe1319556bb4d54214098ada1171d4" integrity sha512-bbjbEyILPRTRt0xnda18OttLtlkJBPuXx3CjISUSn9jhWqHoFMzfOaZ73D5jxZE2SaFZUrJYfPpqXP6qqPufAQ== -"@elastic/numeral@2.3.3": - version "2.3.3" - resolved "https://registry.yarnpkg.com/@elastic/numeral/-/numeral-2.3.3.tgz#94d38a35bd315efa7a6918b22695128fc40a885e" - integrity sha512-0OyB9oztlYIq8F1LHjcNf+T089PKfYw78tgUY+q2dtox/jmb4xzFKtI9kv1hwAt5tcgBUTtUMK9kszpSh1UZaQ== +"@elastic/numeral@2.3.5": + version "2.3.5" + resolved "https://registry.yarnpkg.com/@elastic/numeral/-/numeral-2.3.5.tgz#fcaeac57ddc55cd4b7f0b9c7e070c242dd5d0600" + integrity sha512-lJVZHPuI2csrfwDIEdKedFqNIF+5YsyqvX2soAqhu49iKOd9n7tifLRn30vP6D7oKd+6HsiGfPzT0nzdJWsexQ== "@elastic/request-crypto@^1.0.2": version "1.0.2" From c600fee653d89a76bcc87144cf8472190b5bf681 Mon Sep 17 00:00:00 2001 From: Tim Roes Date: Fri, 10 Jan 2020 17:15:02 +0100 Subject: [PATCH 03/23] i18n currency names in field format editor --- .../editors/currency/currency.js | 280 +++++++++++++++--- 1 file changed, 245 insertions(+), 35 deletions(-) diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js index 7d842cccd60c5..663caa96305bc 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js @@ -31,41 +31,251 @@ import { FormatEditorSamples } from '../../samples'; // to provide country name, currency name, and currency symbol. // The full ISO reference: https://www.currency-iso.org/en/home/tables/table-a1.html const topCurrencies = [ - { name: 'United States dollar', code: 'USD', symbol: 'US$' }, - { name: 'Euro', code: 'EUR', symbol: '€' }, - { name: 'Japanese yen', code: 'JPY', symbol: '¥' }, - { name: 'Pound sterling', code: 'GBP', symbol: '£' }, - { name: 'Australian dollar', code: 'AUD', symbol: 'A$' }, - { name: 'Canadian dollar', code: 'CAD', symbol: 'C$' }, - { name: 'Swiss franc', code: 'CHF', symbol: 'CHF' }, - { name: 'Renminbi', code: 'CNY', symbol: '元' }, - { name: 'Hong Kong dollar', code: 'HKD', symbol: 'HK$' }, - { name: 'New Zealand dollar', code: 'NZD', symbol: 'NZ$' }, - { name: 'Swedish krona', code: 'SEK', symbol: 'kr' }, - { name: 'South Korean won', code: 'KRW', symbol: '₩' }, - { name: 'Singapore dollar', code: 'SGD', symbol: 'S$' }, - { name: 'Norwegian krone', code: 'NOK', symbol: 'kr' }, - { name: 'Mexican peso', code: 'MXN', symbol: '$' }, - { name: 'Indian rupee', code: 'INR', symbol: '₹' }, - { name: 'Russian ruble', code: 'RUB', symbol: '₽' }, - { name: 'South African rand', code: 'ZAR', symbol: 'R' }, - { name: 'Turkish lira', code: 'TRY', symbol: '₺' }, - { name: 'Brazilian real', code: 'BRL', symbol: 'R$' }, - { name: 'New Taiwan dollar', code: 'TWD', symbol: 'NT$' }, - { name: 'Danish krone', code: 'DKK', symbol: 'kr' }, - { name: 'Polish zloty', code: 'PLN', symbol: 'zł' }, - { name: 'Thai baht', code: 'THB', symbol: '฿' }, - { name: 'Indonesian rupiah', code: 'IDR', symbol: 'Rp' }, - { name: 'Hungarian forint', code: 'HUF', symbol: 'Ft' }, - { name: 'Czech koruna', code: 'CZK', symbol: 'Kč' }, - { name: 'Israeli new shekel', code: 'ILS', symbol: '₪' }, - { name: 'Chilean peso', code: 'CLP', symbol: 'CLP$' }, - { name: 'Philippine peso', code: 'PHP', symbol: '₱' }, - { name: 'UAE dirham', code: 'AED', symbol: 'د.إ' }, - { name: 'Colombian peso', code: 'COP', symbol: 'COL$' }, - { name: 'Saudi riyal', code: 'SAR', symbol: '﷼' }, - { name: 'Malaysian ringgit', code: 'MYR', symbol: 'RM' }, - { name: 'Romanian leu', code: 'RON', symbol: 'L' }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.USD', { + defaultMessage: 'United States dollar', + }), + code: 'USD', + symbol: 'US$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.EUR', { + defaultMessage: 'Euro', + }), + code: 'EUR', + symbol: '€', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.JPY', { + defaultMessage: 'Japanese yen', + }), + code: 'JPY', + symbol: '¥', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.GBP', { + defaultMessage: 'Pound sterling', + }), + code: 'GBP', + symbol: '£', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.AUD', { + defaultMessage: 'Australian dollar', + }), + code: 'AUD', + symbol: 'A$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.CAD', { + defaultMessage: 'Canadian dollar', + }), + code: 'CAD', + symbol: 'C$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.CHF', { + defaultMessage: 'Swiss franc', + }), + code: 'CHF', + symbol: 'CHF', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.CNY', { + defaultMessage: 'Renminbi', + }), + code: 'CNY', + symbol: '元', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.HKD', { + defaultMessage: 'Hong Kong dollar', + }), + code: 'HKD', + symbol: 'HK$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.NZD', { + defaultMessage: 'New Zealand dollar', + }), + code: 'NZD', + symbol: 'NZ$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.SEK', { + defaultMessage: 'Swedish krona', + }), + code: 'SEK', + symbol: 'kr', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.KRW', { + defaultMessage: 'South Korean won', + }), + code: 'KRW', + symbol: '₩', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.SGD', { + defaultMessage: 'Singapore dollar', + }), + code: 'SGD', + symbol: 'S$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.NOK', { + defaultMessage: 'Norwegian krone', + }), + code: 'NOK', + symbol: 'kr', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.MXN', { + defaultMessage: 'Mexican peso', + }), + code: 'MXN', + symbol: '$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.INR', { + defaultMessage: 'Indian rupee', + }), + code: 'INR', + symbol: '₹', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.RUB', { + defaultMessage: 'Russian ruble', + }), + code: 'RUB', + symbol: '₽', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.ZAR', { + defaultMessage: 'South African rand', + }), + code: 'ZAR', + symbol: 'R', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.TRY', { + defaultMessage: 'Turkish lira', + }), + code: 'TRY', + symbol: '₺', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.BRL', { + defaultMessage: 'Brazilian real', + }), + code: 'BRL', + symbol: 'R$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.TWD', { + defaultMessage: 'New Taiwan dollar', + }), + code: 'TWD', + symbol: 'NT$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.DKK', { + defaultMessage: 'Danish krone', + }), + code: 'DKK', + symbol: 'kr', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.PLN', { + defaultMessage: 'Polish zloty', + }), + code: 'PLN', + symbol: 'zł', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.THB', { + defaultMessage: 'Thai baht', + }), + code: 'THB', + symbol: '฿', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.IDR', { + defaultMessage: 'Indonesian rupiah', + }), + code: 'IDR', + symbol: 'Rp', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.HUF', { + defaultMessage: 'Hungarian forint', + }), + code: 'HUF', + symbol: 'Ft', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.CZK', { + defaultMessage: 'Czech koruna', + }), + code: 'CZK', + symbol: 'Kč', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.ILS', { + defaultMessage: 'Israeli new shekel', + }), + code: 'ILS', + symbol: '₪', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.CLP', { + defaultMessage: 'Chilean peso', + }), + code: 'CLP', + symbol: 'CLP$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.PHP', { + defaultMessage: 'Philippine peso', + }), + code: 'PHP', + symbol: '₱', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.AED', { + defaultMessage: 'UAE dirham', + }), + code: 'AED', + symbol: 'د.إ', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.COP', { + defaultMessage: 'Colombian peso', + }), + code: 'COP', + symbol: 'COL$', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.SAR', { + defaultMessage: 'Saudi riyal', + }), + code: 'SAR', + symbol: '﷼', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.MYR', { + defaultMessage: 'Malaysian ringgit', + }), + code: 'MYR', + symbol: 'RM', + }, + { + name: i18n.translate('common.ui.fieldEditor.currency.currencies.RON', { + defaultMessage: 'Romanian leu', + }), + code: 'RON', + symbol: 'L', + }, ]; export class CurrencyFormatEditor extends DefaultNumberFormatEditor { From aba8e7c67fd4e813296ba3ab0e2028d436810b32 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Fri, 10 Jan 2020 14:03:18 -0500 Subject: [PATCH 04/23] Add migration and update tests --- docs/management/managing-fields.asciidoc | 24 +++--- .../kibana/migrations/migrations.js | 78 ++++++++++--------- .../kibana/migrations/migrations.test.js | 30 +++++++ .../field_format_editor/get_editors.js | 4 +- .../field_formats/converters/bytes.test.ts | 15 ++-- .../common/field_formats/converters/bytes.ts | 2 +- .../field_formats/converters/currency.test.ts | 57 ++++++++++++++ .../converters/default_number.test.ts | 48 ++++++++++++ .../converters/intl_number_format.ts | 9 ++- .../field_formats/converters/numeral.ts | 18 +---- .../field_formats/converters/percent.test.ts | 2 - .../converters/short_number.test.ts | 34 ++++++++ .../legacy/plugins/siem/common/constants.ts | 1 - .../components/formatted_bytes/index.tsx | 5 +- .../plugins/siem/public/mock/kibana_react.ts | 2 - 15 files changed, 248 insertions(+), 81 deletions(-) create mode 100644 src/plugins/data/common/field_formats/converters/currency.test.ts create mode 100644 src/plugins/data/common/field_formats/converters/default_number.test.ts create mode 100644 src/plugins/data/common/field_formats/converters/short_number.test.ts diff --git a/docs/management/managing-fields.asciidoc b/docs/management/managing-fields.asciidoc index 308e61abf70e5..536c9537dd477 100644 --- a/docs/management/managing-fields.asciidoc +++ b/docs/management/managing-fields.asciidoc @@ -5,8 +5,9 @@ The fields for the index pattern are listed in a table. Click a column header to the *Controls* button in the rightmost column for a given field to edit the field's properties. You can manually set the field's format from the *Format* drop-down. Format options vary based on the field's type. -You can also set the field's popularity value in the *Popularity* text entry box to any desired value. Click the -*Update Field* button to confirm your changes or *Cancel* to return to the list of fields. +You can also set the field's popularity value in the *Popularity* text entry box to any desired value. If a field has a +popularity value, it will be sorted higher in Discover. Click the *Update Field* button to confirm your changes or +*Cancel* to return to the list of fields. Kibana has field formatters for the following field types: @@ -46,18 +47,23 @@ include::field-formatters/string-formatter.asciidoc[] [[field-formatters-numeric]] === Numeric Field Formatters -Numeric fields support the `Url`, `Bytes`, `Duration`, `Number`, `Percentage`, `String`, and `Color` formatters. +Numeric fields support the `Default Number`, `Percent`, `Bytes`, `Short Number`, `Currency`, `Custom Numeral.js format`, +`Duration`, `String`, `Url`, and `Color` formatters. -include::field-formatters/url-formatter.asciidoc[] +The advanced setting `format:defaultLocale` affects the localization of numbers with these formats: `Default Number`, +`Percent`, `Short Number`, and `Currency`. The advanced setting `format:number:defaultLocale` affects the localization of +numbers with the `Bytes` and `Custom Numeral.js format`. -include::field-formatters/string-formatter.asciidoc[] +The `Custom Numeral.js format` enables customization of display using the https://adamwdraper.github.io/Numeral-js/[numeral.js] +standard format definitions. include::field-formatters/duration-formatter.asciidoc[] -include::field-formatters/color-formatter.asciidoc[] +include::field-formatters/url-formatter.asciidoc[] + +include::field-formatters/string-formatter.asciidoc[] -The `Bytes`, `Number`, and `Percentage` formatters enable you to choose the display formats of numbers in this field using -the https://adamwdraper.github.io/Numeral-js/[numeral.js] standard format definitions. +include::field-formatters/color-formatter.asciidoc[] [[scripted-fields]] === Scripted Fields @@ -65,7 +71,7 @@ the https://adamwdraper.github.io/Numeral-js/[numeral.js] standard format defini Scripted fields compute data on the fly from the data in your Elasticsearch indices. Scripted field data is shown on the Discover tab as part of the document data, and you can use scripted fields in your visualizations. Scripted field values are computed at query time so they aren't indexed and cannot be searched using Kibana's default -query language. However they can be queried using Kibana's new <>. Scripted +query language. However they can be queried using Kibana's new <>. Scripted fields are also supported in the filter bar. WARNING: Computing data on the fly with scripted fields can be very resource intensive and can have a direct impact on diff --git a/src/legacy/core_plugins/kibana/migrations/migrations.js b/src/legacy/core_plugins/kibana/migrations/migrations.js index 9ba5fa65df87c..64895a2580a42 100644 --- a/src/legacy/core_plugins/kibana/migrations/migrations.js +++ b/src/legacy/core_plugins/kibana/migrations/migrations.js @@ -463,50 +463,58 @@ function migrateSubTypeAndParentFieldProperties(doc) { if (!doc.attributes.fields) return doc; const fieldsString = doc.attributes.fields; - const fields = JSON.parse(fieldsString); - const migratedFields = fields.map(field => { - if (field.subType === 'multi') { - return { - ...omit(field, 'parent'), - subType: { multi: { parent: field.parent } }, - }; - } + try { + const fields = JSON.parse(fieldsString); + const migratedFields = fields.map(field => { + if (field.subType === 'multi') { + return { + ...omit(field, 'parent'), + subType: { multi: { parent: field.parent } }, + }; + } - return field; - }); + return field; + }); - return { - ...doc, - attributes: { - ...doc.attributes, - fields: JSON.stringify(migratedFields), - }, - }; + return { + ...doc, + attributes: { + ...doc.attributes, + fields: JSON.stringify(migratedFields), + }, + }; + } catch (e) { + return doc; + } } function migrateNumeralFieldFormatters(doc) { if (!doc.attributes.fieldFormatMap) return doc; const fieldFormatMap = doc.attributes.fieldFormatMap; - const fieldFormats = JSON.parse(fieldFormatMap); - // const migratedFields = fields.map(field => { - // if (field.subType === 'multi') { - // return { - // ...omit(field, 'parent'), - // subType: { multi: { parent: field.parent } }, - // }; - // } - - // return field; - // }); + try { + const fieldFormats = JSON.parse(fieldFormatMap); - return { - ...doc, - attributes: { - ...doc.attributes, - fieldFormatMap: JSON.stringify(fieldFormats), - }, - }; + const newFormats = Object.fromEntries( + Object.entries(fieldFormats).map(([field, format]) => { + if (format.pattern && (format.id === 'bytes' || format.id === 'percent')) { + // Migrate custom numeral.js patterns to 'number' + return [field, { ...format, id: 'number' }]; + } + return [field, format]; + }) + ); + + return { + ...doc, + attributes: { + ...doc.attributes, + fieldFormatMap: JSON.stringify(newFormats), + }, + }; + } catch (e) { + return doc; + } } const executeMigrations720 = flow( diff --git a/src/legacy/core_plugins/kibana/migrations/migrations.test.js b/src/legacy/core_plugins/kibana/migrations/migrations.test.js index e39bc59201e7f..fa940e0dbf866 100644 --- a/src/legacy/core_plugins/kibana/migrations/migrations.test.js +++ b/src/legacy/core_plugins/kibana/migrations/migrations.test.js @@ -78,6 +78,36 @@ Object { expect(migrate(input)).toEqual(expected); }); + + it('should handle malformed fieldFormatMap', () => { + const input = { + attributes: { + title: 'test', + fieldFormatMap: '{"nochange":{"id":"percen', + }, + }; + + expect(migrate(input)).toEqual(input); + }); + + it('should rewrite fieldFormatMap if there are custom numeraljs patterns', () => { + const input = { + attributes: { + title: 'test', + fieldFormatMap: + '{"nochange":{"id":"percent"},"change":{"id":"bytes","pattern":"0.[000]b"}}', + }, + }; + const expected = { + attributes: { + title: 'test', + fieldFormatMap: + '{"nochange":{"id":"percent"},"change":{"id":"number","pattern":"0.[000]b"}}', + }, + }; + + expect(migrate(input)).toEqual(expected); + }); }); }); diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js b/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js index 7c3000747950f..66dc70157d0c2 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js @@ -33,15 +33,15 @@ import { TruncateFormatEditor } from './editors/truncate'; import { UrlFormatEditor } from './editors/url/url'; export const editors = [ + DefaultNumberFormatEditor, BytesFormatEditor, ColorFormatEditor, CurrencyFormatEditor, DateFormatEditor, DateNanosFormatEditor, CustomNumberFormatEditor, - DefaultNumberFormatEditor, - DurationFormatEditor, ShortNumberFormatEditor, + DurationFormatEditor, PercentFormatEditor, StaticLookupFormatEditor, StringFormatEditor, diff --git a/src/plugins/data/common/field_formats/converters/bytes.test.ts b/src/plugins/data/common/field_formats/converters/bytes.test.ts index 8dad9fc206e72..5ad3886267018 100644 --- a/src/plugins/data/common/field_formats/converters/bytes.test.ts +++ b/src/plugins/data/common/field_formats/converters/bytes.test.ts @@ -20,21 +20,24 @@ import { BytesFormat } from './bytes'; describe('BytesFormat', () => { - const config: Record = {}; - - config['format:bytes:defaultPattern'] = '0,0.[000]b'; + let config: Record = {}; const getConfig = (key: string) => config[key]; + beforeEach(() => { + config = {}; + }); + test('default pattern', () => { const formatter = new BytesFormat({}, getConfig); expect(formatter.convert(5150000)).toBe('4.911MB'); }); - test('custom pattern', () => { - const formatter = new BytesFormat({ pattern: '0,0b' }, getConfig); + test('custom pattern and locale', () => { + config['format:number:defaultLocale'] = 'de'; + const formatter = new BytesFormat({ pattern: '0,0.[0]b' }, getConfig); - expect(formatter.convert('5150000')).toBe('5MB'); + expect(formatter.convert('10500')).toBe('10,3KB'); }); }); diff --git a/src/plugins/data/common/field_formats/converters/bytes.ts b/src/plugins/data/common/field_formats/converters/bytes.ts index ecabd170cd63c..857097242ec48 100644 --- a/src/plugins/data/common/field_formats/converters/bytes.ts +++ b/src/plugins/data/common/field_formats/converters/bytes.ts @@ -31,7 +31,7 @@ export class BytesFormat extends NumeralFormat { title = BytesFormat.title; getParamDefaults = () => ({ - pattern: '0,0.0b', + pattern: '0,0.[000]b', }); allowsNumericalAggregations = true; diff --git a/src/plugins/data/common/field_formats/converters/currency.test.ts b/src/plugins/data/common/field_formats/converters/currency.test.ts new file mode 100644 index 0000000000000..5ac4bb65a428b --- /dev/null +++ b/src/plugins/data/common/field_formats/converters/currency.test.ts @@ -0,0 +1,57 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import 'intl'; +import { CurrencyFormat } from './currency'; + +describe('CurrencyFormat', () => { + let config: Record = {}; + + const getConfig = (key: string) => config[key]; + + beforeEach(() => { + config = {}; + }); + + test('default currency', () => { + // This locale is not supported in node + config['format:defaultLocale'] = 'pt-PT'; + config['format:currency:defaultCurrency'] = 'EUR'; + + const formatter = new CurrencyFormat({}, getConfig); + + expect(formatter.convert(5150000)).toBe('€5,150,000'); + }); + + test('decimals', () => { + config['format:currency:defaultCurrency'] = 'USD'; + + const formatter = new CurrencyFormat({}, getConfig); + + expect(formatter.convert(1234.56789)).toBe('$1,234.57'); + }); + + test('2 decimal places', () => { + config['format:currency:defaultCurrency'] = 'USD'; + + const formatter = new CurrencyFormat({ minDecimals: 2 }, getConfig); + + expect(formatter.convert(1234)).toBe('$1,234.00'); + }); +}); diff --git a/src/plugins/data/common/field_formats/converters/default_number.test.ts b/src/plugins/data/common/field_formats/converters/default_number.test.ts new file mode 100644 index 0000000000000..b58b0d4860d34 --- /dev/null +++ b/src/plugins/data/common/field_formats/converters/default_number.test.ts @@ -0,0 +1,48 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { DefaultNumberFormat } from './default_number'; + +describe('DefaultNumberFormat', () => { + let config: Record = {}; + + const getConfig = (key: string) => config[key]; + + beforeEach(() => { + config = {}; + }); + + test('default number', () => { + // Node only contains locale data for `en`, so this is ignored. The fallback is tested here + config['format:defaultLocale'] = 'ch-DE'; + + const formatter = new DefaultNumberFormat({}, getConfig); + + expect(formatter.convert(5150000)).toBe(`5,150,000`); + }); + + test('number of decimals', () => { + // Node only contains locale data for `en`, so this is ignored. The fallback is tested here + config['format:defaultLocale'] = 'ch-DE'; + + const formatter = new DefaultNumberFormat({ minDecimals: 2 }, getConfig); + + expect(formatter.convert(5150000)).toBe(`5,150,000.00`); + }); +}); diff --git a/src/plugins/data/common/field_formats/converters/intl_number_format.ts b/src/plugins/data/common/field_formats/converters/intl_number_format.ts index ad73e1addbb05..aba659fb67d41 100644 --- a/src/plugins/data/common/field_formats/converters/intl_number_format.ts +++ b/src/plugins/data/common/field_formats/converters/intl_number_format.ts @@ -17,7 +17,6 @@ * under the License. */ -// import { i18n } from '@kbn/i18n'; import { FieldFormat } from '../field_format'; import { TextContextTypeConvert } from '../types'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; @@ -43,11 +42,15 @@ export abstract class IntlNumberFormat extends FieldFormat { if (isNaN(val)) return ''; const defaultLocale = this.getConfig && this.getConfig('format:defaultLocale'); - let locales = [defaultLocale, 'en']; + let locales; if (defaultLocale === 'detect') { locales = navigator.languages ? navigator.languages.concat(['en']) - : [navigator.language].concat(locales); + : [navigator.language, defaultLocale, 'en']; + } else if (defaultLocale) { + locales = [defaultLocale, 'en']; + } else { + locales = ['en']; } const inst = new Intl.NumberFormat(locales, this.getArguments()); diff --git a/src/plugins/data/common/field_formats/converters/numeral.ts b/src/plugins/data/common/field_formats/converters/numeral.ts index c93ff15633503..0666e8c13ba64 100644 --- a/src/plugins/data/common/field_formats/converters/numeral.ts +++ b/src/plugins/data/common/field_formats/converters/numeral.ts @@ -57,22 +57,8 @@ export abstract class NumeralFormat extends FieldFormat { if (isNaN(val)) return ''; const previousLocale = numeral.language(); - const defaultLocale = this.getConfig && this.getConfig('format:number:defaultLocale'); - let locale = 'en'; - if (defaultLocale === 'detect') { - if (navigator.languages && navigator.languages.length) { - const match = navigator.languages.find(lang => { - return !!supportedNumeralLanguages[lang]; - }); - if (match) { - locale = match; - } - } else if (navigator.language && !!supportedNumeralLanguages[navigator.language]) { - locale = navigator.language; - } - } - - numeral.language(locale); + const defaultLocale = (this.getConfig && this.getConfig('format:number:defaultLocale')) || 'en'; + numeral.language(defaultLocale); const formatted = numeralInst.set(val).format(pattern || this.param('pattern')); diff --git a/src/plugins/data/common/field_formats/converters/percent.test.ts b/src/plugins/data/common/field_formats/converters/percent.test.ts index 3af8aefefa6ba..d6154392b9226 100644 --- a/src/plugins/data/common/field_formats/converters/percent.test.ts +++ b/src/plugins/data/common/field_formats/converters/percent.test.ts @@ -22,8 +22,6 @@ import { PercentFormat } from './percent'; describe('PercentFormat', () => { const config: Record = {}; - config['format:percent:defaultPattern'] = '0,0.[000]%'; - const getConfig = (key: string) => config[key]; test('default pattern', () => { diff --git a/src/plugins/data/common/field_formats/converters/short_number.test.ts b/src/plugins/data/common/field_formats/converters/short_number.test.ts new file mode 100644 index 0000000000000..b275a0d9ec25a --- /dev/null +++ b/src/plugins/data/common/field_formats/converters/short_number.test.ts @@ -0,0 +1,34 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ShortNumberFormat } from './short_number'; + +describe('ShortNumberFormat', () => { + const config: Record = {}; + + const getConfig = (key: string) => config[key]; + + test('default', () => { + const formatter = new ShortNumberFormat({}, getConfig); + + // This expectation is only for node, in the browser the same number is formatted as '1.234M' + // This is because of limited ICU support in node + expect(formatter.convert(1234567)).toBe('1,234,567'); + }); +}); diff --git a/x-pack/legacy/plugins/siem/common/constants.ts b/x-pack/legacy/plugins/siem/common/constants.ts index 5116416b527a5..6b3991c06e289 100644 --- a/x-pack/legacy/plugins/siem/common/constants.ts +++ b/x-pack/legacy/plugins/siem/common/constants.ts @@ -6,7 +6,6 @@ export const APP_ID = 'siem'; export const APP_NAME = 'SIEM'; -export const DEFAULT_BYTES_FORMAT = 'format:bytes:defaultPattern'; export const DEFAULT_DATE_FORMAT = 'dateFormat'; export const DEFAULT_DATE_FORMAT_TZ = 'dateFormat:tz'; export const DEFAULT_DARK_MODE = 'theme:darkMode'; diff --git a/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx b/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx index 98a1acf471629..8657a8831ed44 100644 --- a/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx @@ -7,7 +7,6 @@ import React from 'react'; import numeral from '@elastic/numeral'; -import { DEFAULT_BYTES_FORMAT } from '../../../common/constants'; import { useUiSetting$ } from '../../lib/kibana'; type Bytes = string | number; @@ -17,9 +16,7 @@ export const formatBytes = (value: Bytes, format: string) => { }; export const useFormatBytes = () => { - const [bytesFormat] = useUiSetting$(DEFAULT_BYTES_FORMAT); - - return (value: Bytes) => formatBytes(value, bytesFormat); + return (value: Bytes) => formatBytes(value, '0,0.0b'); }; export const PreferenceFormattedBytesComponent = ({ value }: { value: Bytes }) => ( diff --git a/x-pack/legacy/plugins/siem/public/mock/kibana_react.ts b/x-pack/legacy/plugins/siem/public/mock/kibana_react.ts index 7d843977d1f32..ec5ca9922985e 100644 --- a/x-pack/legacy/plugins/siem/public/mock/kibana_react.ts +++ b/x-pack/legacy/plugins/siem/public/mock/kibana_react.ts @@ -22,7 +22,6 @@ import { DEFAULT_TO, DEFAULT_INTERVAL_PAUSE, DEFAULT_INTERVAL_VALUE, - DEFAULT_BYTES_FORMAT, } from '../../common/constants'; import { defaultIndexPattern } from '../../default_index_pattern'; import { createKibanaCoreStartMock, createKibanaPluginsStartMock } from './kibana_core'; @@ -40,7 +39,6 @@ export const mockUiSettings: Record = { value: DEFAULT_INTERVAL_VALUE, }, [DEFAULT_INDEX_KEY]: defaultIndexPattern, - [DEFAULT_BYTES_FORMAT]: '0,0.[0]b', [DEFAULT_DATE_FORMAT_TZ]: 'UTC', [DEFAULT_DATE_FORMAT]: 'MMM D, YYYY @ HH:mm:ss.SSS', [DEFAULT_DARK_MODE]: false, From 2a6ecedbd8f7f6ab7de3b6c054ce93f416c82a76 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Fri, 10 Jan 2020 14:07:36 -0500 Subject: [PATCH 05/23] Remove unused --- src/plugins/data/common/field_formats/converters/numeral.ts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/plugins/data/common/field_formats/converters/numeral.ts b/src/plugins/data/common/field_formats/converters/numeral.ts index 0666e8c13ba64..f8aea1a695227 100644 --- a/src/plugins/data/common/field_formats/converters/numeral.ts +++ b/src/plugins/data/common/field_formats/converters/numeral.ts @@ -31,12 +31,6 @@ numeralLanguages.forEach((numeralLanguage: Record) => { numeral.language(numeralLanguage.id, numeralLanguage.lang); }); -const supportedNumeralLanguages = Object.fromEntries( - numeralLanguages.map((numeralLanguage: Record) => { - return [numeralLanguage.id, true]; - }) -); - export abstract class NumeralFormat extends FieldFormat { static fieldType = KBN_FIELD_TYPES.NUMBER; From 47ec99638ff43328d13dc82bbe975662c65f4145 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Fri, 10 Jan 2020 14:57:22 -0500 Subject: [PATCH 06/23] Remove deprecated settings use --- .../lib/__tests__/tick_formatter.js | 24 ++++++++----------- test/functional/apps/visualize/index.ts | 1 - .../uis/arguments/number_format/index.ts | 9 ++++--- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/__tests__/tick_formatter.js b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/__tests__/tick_formatter.js index 20d8b9391b01a..45c41bd649497 100644 --- a/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/__tests__/tick_formatter.js +++ b/src/legacy/core_plugins/vis_type_timeseries/public/components/lib/__tests__/tick_formatter.js @@ -26,21 +26,17 @@ describe('createTickFormatter(format, template)', () => { expect(fn(1.5556)).to.equal('1.56'); }); - // it('returns a percent with percent formatter', () => { - // const config = { - // 'format:percent:defaultPattern': '0.[00]%', - // }; - // const fn = createTickFormatter('percent', null, key => config[key]); - // expect(fn(0.5556)).to.equal('55.56%'); - // }); + it('returns a percent with percent formatter', () => { + const config = {}; + const fn = createTickFormatter('percent', null, key => config[key]); + expect(fn(0.5556)).to.equal('55.56%'); + }); - // it('returns a byte formatted string with byte formatter', () => { - // const config = { - // 'format:bytes:defaultPattern': '0.0b', - // }; - // const fn = createTickFormatter('bytes', null, key => config[key]); - // expect(fn(1500 ^ 10)).to.equal('1.5KB'); - // }); + it('returns a byte formatted string with byte formatter', () => { + const config = {}; + const fn = createTickFormatter('bytes', null, key => config[key]); + expect(fn(1500 ^ 10)).to.equal('1.5KB'); + }); it('returns a custom formatted string with custom formatter', () => { const fn = createTickFormatter('0.0a'); diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index 2a13b6fea9158..085a7edfe6ef7 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -35,7 +35,6 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { await esArchiver.load('visualize'); await kibanaServer.uiSettings.replace({ defaultIndex: 'logstash-*', - 'format:bytes:defaultPattern': '0,0.[000]b', }); }); diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts index 7654774901ff0..29eae0876d10c 100644 --- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts +++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/uis/arguments/number_format/index.ts @@ -6,7 +6,6 @@ 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'; import { ArgumentFactory } from '../../../../types/arguments'; @@ -15,11 +14,11 @@ import { ArgumentStrings } from '../../../../i18n'; const { NumberFormat: strings } = ArgumentStrings; const formatMap = { - NUMBER: AdvancedSettings.get('format:number:defaultPattern'), - PERCENT: AdvancedSettings.get('format:percent:defaultPattern'), - CURRENCY: AdvancedSettings.get('format:currency:defaultPattern'), + NUMBER: '0,0.[000]', + PERCENT: '0,0.[000]%', + CURRENCY: '$0,0.[00]', DURATION: '00:00:00', - BYTES: AdvancedSettings.get('format:bytes:defaultPattern'), + BYTES: '0,0.[000]b', }; const numberFormats = [ From 4f3175a72cdd323a6d15b256f86c65e28addbb1a Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Fri, 10 Jan 2020 15:59:36 -0500 Subject: [PATCH 07/23] Fix lint error --- .../plugins/siem/public/components/formatted_bytes/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx b/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx index 8657a8831ed44..83a8cf6da5547 100644 --- a/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx @@ -7,8 +7,6 @@ import React from 'react'; import numeral from '@elastic/numeral'; -import { useUiSetting$ } from '../../lib/kibana'; - type Bytes = string | number; export const formatBytes = (value: Bytes, format: string) => { From 4e434652e27d9e8a3812b82d83da2db01b2162f6 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Mon, 13 Jan 2020 15:50:45 -0500 Subject: [PATCH 08/23] Stop formatting numbers in CSV export --- .../kibana/server/field_formats/register.js | 10 ++++- .../kibana/ui_setting_defaults.js | 8 ++-- .../mixin/field_formats_service.ts | 1 - .../field_formats/converters/numeral.ts | 4 +- src/plugins/data/server/index.ts | 1 + .../server/lib/__tests__/field_format_map.js | 39 +++++++++++++------ .../csv/server/lib/field_format_map.js | 4 ++ .../components/formatted_bytes/index.test.tsx | 27 ------------- .../components/formatted_bytes/index.tsx | 2 +- .../translations/translations/ja-JP.json | 10 ----- .../translations/translations/zh-CN.json | 10 ----- 11 files changed, 47 insertions(+), 69 deletions(-) diff --git a/src/legacy/core_plugins/kibana/server/field_formats/register.js b/src/legacy/core_plugins/kibana/server/field_formats/register.js index 34dc06aab44ac..2813b81994e89 100644 --- a/src/legacy/core_plugins/kibana/server/field_formats/register.js +++ b/src/legacy/core_plugins/kibana/server/field_formats/register.js @@ -20,7 +20,10 @@ import { UrlFormat, StringFormat, - NumberFormat, + DefaultNumberFormat, + CustomNumberFormat, + CurrencyFormat, + ShortNumberFormat, BytesFormat, TruncateFormat, RelativeDateFormat, @@ -43,8 +46,11 @@ export function registerFieldFormats(server) { server.registerFieldFormat(RelativeDateFormat); server.registerFieldFormat(DurationFormat); server.registerFieldFormat(IpFormat); - server.registerFieldFormat(NumberFormat); + server.registerFieldFormat(DefaultNumberFormat); + server.registerFieldFormat(CustomNumberFormat); server.registerFieldFormat(PercentFormat); + server.registerFieldFormat(CurrencyFormat); + server.registerFieldFormat(ShortNumberFormat); server.registerFieldFormat(StringFormat); server.registerFieldFormat(SourceFormat); server.registerFieldFormat(ColorFormat); diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js index cd1d6d0e7a818..eecd6fda1e0f8 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js @@ -784,7 +784,7 @@ export function getUiSettingDefaults() { }), }, 'format:number:defaultLocale': { - name: i18n.translate('kbn.advancedSettings.format.formattingLocaleTitle', { + name: i18n.translate('kbn.advancedSettings.format.number.formattingLocaleTitle', { defaultMessage: 'Numeral.js formatting locale', }), value: 'en', @@ -793,11 +793,11 @@ export function getUiSettingDefaults() { optionLabels: Object.fromEntries( numeralLanguages.map(language => [language.id, language.name]) ), - description: i18n.translate('kbn.advancedSettings.format.formattingLocaleText', { + description: i18n.translate('kbn.advancedSettings.format.number.formattingLocaleText', { defaultMessage: `{numeralLanguageLink} locale. This affects the custom numeral.js formatter and the bytes formatter.`, description: - 'Part of composite text: kbn.advancedSettings.format.formattingLocale.numeralLanguageLinkText + ' + - 'kbn.advancedSettings.format.formattingLocaleText', + 'Part of composite text: kbn.advancedSettings.format.number.formattingLocaleText + ' + + 'kbn.advancedSettings.format.formattingLocale.numeralLanguageLinkText', values: { numeralLanguageLink: '' + diff --git a/src/legacy/ui/field_formats/mixin/field_formats_service.ts b/src/legacy/ui/field_formats/mixin/field_formats_service.ts index c5bc25333985b..6c00df854271d 100644 --- a/src/legacy/ui/field_formats/mixin/field_formats_service.ts +++ b/src/legacy/ui/field_formats/mixin/field_formats_service.ts @@ -63,7 +63,6 @@ export class FieldFormatsService { * @return {FieldFormat} */ getInstance(conf: FieldFormatConfig): FieldFormat { - // @ts-ignore return new this._fieldFormats[conf.id](conf.params, this.getConfig); } diff --git a/src/plugins/data/common/field_formats/converters/numeral.ts b/src/plugins/data/common/field_formats/converters/numeral.ts index f8aea1a695227..d8e46a480294f 100644 --- a/src/plugins/data/common/field_formats/converters/numeral.ts +++ b/src/plugins/data/common/field_formats/converters/numeral.ts @@ -41,7 +41,7 @@ export abstract class NumeralFormat extends FieldFormat { pattern: this.getConfig!(`format:${this.id}:defaultPattern`), }); - protected getConvertedValue(val: any, pattern?: string): string { + protected getConvertedValue(val: any): string { if (val === -Infinity) return '-∞'; if (val === +Infinity) return '+∞'; if (typeof val !== 'number') { @@ -54,7 +54,7 @@ export abstract class NumeralFormat extends FieldFormat { const defaultLocale = (this.getConfig && this.getConfig('format:number:defaultLocale')) || 'en'; numeral.language(defaultLocale); - const formatted = numeralInst.set(val).format(pattern || this.param('pattern')); + const formatted = numeralInst.set(val).format(this.param('pattern')); numeral.language(previousLocale); diff --git a/src/plugins/data/server/index.ts b/src/plugins/data/server/index.ts index 2d6f197cf28f9..e62d2e1dad5d2 100644 --- a/src/plugins/data/server/index.ts +++ b/src/plugins/data/server/index.ts @@ -71,6 +71,7 @@ export { BoolFormat, BytesFormat, ColorFormat, + CurrencyFormat, DateFormat, DateNanosFormat, DEFAULT_CONVERTER_COLOR, diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/__tests__/field_format_map.js b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/__tests__/field_format_map.js index 5f6bc32f10d8b..089afcb3702cf 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/__tests__/field_format_map.js +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/__tests__/field_format_map.js @@ -8,8 +8,13 @@ import expect from '@kbn/expect'; import { FieldFormatsService } from '../../../../../../../../../src/legacy/ui/field_formats/mixin/field_formats_service'; // Reporting uses an unconventional directory structure so the linter marks this as a violation -// eslint-disable-next-line @kbn/eslint/no-restricted-paths -import { BytesFormat, NumberFormat } from '../../../../../../../../../src/plugins/data/server'; +import { + DateFormat, + BytesFormat, + DefaultNumberFormat, + StaticLookupFormat, + // eslint-disable-next-line @kbn/eslint/no-restricted-paths +} from '../../../../../../../../../src/plugins/data/server'; import { fieldFormatMapFactory } from '../field_format_map'; @@ -22,33 +27,43 @@ describe('field format map', function() { title: 'logstash-*', timeFieldName: '@timestamp', notExpandable: true, - fields: '[{"name":"field1","type":"number"}, {"name":"field2","type":"number"}]', - fieldFormatMap: '{"field1":{"id":"bytes","params":{"pattern":"0,0.[0]b"}}}', + fields: + '[{"name":"field1","type":"number"}, {"name":"field2","type":"date"}, {"name":"field3","type":"string"}]', + fieldFormatMap: + '{"field1":{"id":"bytes"},"field3":{"id":"static_lookup","params":{"lookupEntries":[{"key":"test","value":"tested"}]}}}', }, }; const configMock = {}; + configMock.dateFormat = 'YYYY-MM-DD'; + configMock['dateFormat:tz'] = 'Europe/London'; configMock['format:defaultTypeMap'] = { - number: { id: 'number', params: {} }, + number: { id: 'default_number', params: {} }, + string: { id: 'string', params: {} }, + date: { id: 'date', params: {} }, + _default_: { id: 'string', params: {} }, }; - configMock['format:number:defaultPattern'] = '0,0.[000]'; const getConfig = key => configMock[key]; - const testValue = '4000'; + const testValue = 'test'; - const fieldFormats = new FieldFormatsService([BytesFormat, NumberFormat], getConfig); + const fieldFormats = new FieldFormatsService( + [DefaultNumberFormat, BytesFormat, StaticLookupFormat, DateFormat], + getConfig + ); const formatMap = fieldFormatMapFactory(indexPatternSavedObject, fieldFormats); - it('should build field format map with entry per index pattern field', function() { - expect(formatMap.has('field1')).to.be(true); + it('should build field format map with entry per index pattern field, excluding numbers', function() { + expect(formatMap.has('field1')).to.be(false); expect(formatMap.has('field2')).to.be(true); + expect(formatMap.has('field3')).to.be(true); expect(formatMap.has('field_not_in_index')).to.be(false); }); it('should create custom FieldFormat for fields with configured field formatter', function() { - expect(formatMap.get('field1').convert(testValue)).to.be('3.9KB'); + expect(formatMap.get('field3').convert(testValue)).to.be('tested'); }); it('should create default FieldFormat for fields with no field formatter', function() { - expect(formatMap.get('field2').convert(testValue)).to.be('4,000'); + expect(formatMap.get('field2').convert(1578948388064)).to.be('2020-01-13'); }); }); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.js b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.js index 17b7361ca07b0..1d330a4af8388 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.js +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.js @@ -34,6 +34,10 @@ export function fieldFormatMapFactory(indexPatternSavedObject, fieldFormats) { if (!formatsMap.has(field.name)) { formatsMap.set(field.name, fieldFormats.getDefaultInstance(field.type)); } + + if (field.type === 'number') { + formatsMap.delete(field.name); + } }); return formatsMap; diff --git a/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.test.tsx b/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.test.tsx index 914d233bccc1f..f9c19b6404ada 100644 --- a/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.test.tsx +++ b/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.test.tsx @@ -7,49 +7,22 @@ import { mount, shallow } from 'enzyme'; import React from 'react'; -import { useUiSetting$ } from '../../lib/kibana'; - import { PreferenceFormattedBytesComponent } from '.'; jest.mock('../../lib/kibana'); -const mockUseUiSetting$ = useUiSetting$ as jest.Mock; -const DEFAULT_BYTES_FORMAT_VALUE = '0,0.[0]b'; // kibana's default for this setting const bytes = '2806422'; describe('PreferenceFormattedBytes', () => { test('renders correctly against snapshot', () => { - mockUseUiSetting$.mockImplementation(() => [DEFAULT_BYTES_FORMAT_VALUE]); const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); - test('it renders bytes to Numeral formatting when no format setting exists', () => { - mockUseUiSetting$.mockImplementation(() => [null]); - const wrapper = mount(); - - expect(wrapper.text()).toEqual('2,806,422'); - }); - - test('it renders bytes according to the default format', () => { - mockUseUiSetting$.mockImplementation(() => [DEFAULT_BYTES_FORMAT_VALUE]); - const wrapper = mount(); - - expect(wrapper.text()).toEqual('2.7MB'); - }); - test('it renders bytes supplied as a number according to the default format', () => { - mockUseUiSetting$.mockImplementation(() => [DEFAULT_BYTES_FORMAT_VALUE]); const wrapper = mount(); expect(wrapper.text()).toEqual('2.7MB'); }); - - test('it renders bytes according to new format', () => { - mockUseUiSetting$.mockImplementation(() => ['0b']); - const wrapper = mount(); - - expect(wrapper.text()).toEqual('3MB'); - }); }); diff --git a/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx b/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx index 83a8cf6da5547..bc260b0ce8e00 100644 --- a/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/formatted_bytes/index.tsx @@ -14,7 +14,7 @@ export const formatBytes = (value: Bytes, format: string) => { }; export const useFormatBytes = () => { - return (value: Bytes) => formatBytes(value, '0,0.0b'); + return (value: Bytes) => formatBytes(value, '0,0.[0]b'); }; export const PreferenceFormattedBytesComponent = ({ value }: { value: Bytes }) => ( diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index a414af1f95d7f..1fe4589fbb3d8 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -1359,21 +1359,11 @@ "kbn.advancedSettings.docTableHighlightTitle": "結果をハイライト", "kbn.advancedSettings.fieldsPopularLimitText": "最も頻繁に使用されるフィールドのトップ N を表示します", "kbn.advancedSettings.fieldsPopularLimitTitle": "頻繁に使用されるフィールドの制限", - "kbn.advancedSettings.format.bytesFormat.numeralFormatLinkText": "数字フォーマット", - "kbn.advancedSettings.format.bytesFormatText": "「バイト」フォーマットのデフォルト {numeralFormatLink} です", - "kbn.advancedSettings.format.bytesFormatTitle": "バイトフォーマット", - "kbn.advancedSettings.format.currencyFormat.numeralFormatLinkText": "数字フォーマット", - "kbn.advancedSettings.format.currencyFormatText": "「通貨」フォーマットのデフォルト {numeralFormatLink} です", - "kbn.advancedSettings.format.currencyFormatTitle": "通貨フォーマット", "kbn.advancedSettings.format.defaultTypeMapText": "各フィールドタイプにデフォルトで使用するフォーマット名のマップです。フィールドタイプが特に指定されていない場合は {defaultFormat} が使用されます", "kbn.advancedSettings.format.defaultTypeMapTitle": "フィールドタイプフォーマット名", - "kbn.advancedSettings.format.formattingLocale.numeralLanguageLinkText": "数字言語", "kbn.advancedSettings.format.numberFormat.numeralFormatLinkText": "数字フォーマット", "kbn.advancedSettings.format.numberFormatText": "「数字」フォーマットのデフォルト {numeralFormatLink} です", "kbn.advancedSettings.format.numberFormatTitle": "数字フォーマット", - "kbn.advancedSettings.format.percentFormat.numeralFormatLinkText": "数字フォーマット", - "kbn.advancedSettings.format.percentFormatText": "「パーセント」フォーマットのデフォルト {numeralFormatLink} です", - "kbn.advancedSettings.format.percentFormatTitle": "パーセントフォーマット", "kbn.advancedSettings.histogram.barTargetText": "日付ヒストグラムで「自動」間隔を使用する際、この数に近いバーの作成を試みます", "kbn.advancedSettings.histogram.barTargetTitle": "目標バー数", "kbn.advancedSettings.histogram.maxBarsText": "日付ヒストグラムに表示されるバーの数の上限です。必要に応じて値をスケーリングしてください", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index deb8f0b36d2aa..50b94aad5fcba 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -1360,21 +1360,11 @@ "kbn.advancedSettings.docTableHighlightTitle": "突出显示结果", "kbn.advancedSettings.fieldsPopularLimitText": "要显示的排名前 N 最常见字段", "kbn.advancedSettings.fieldsPopularLimitTitle": "常见字段限制", - "kbn.advancedSettings.format.bytesFormat.numeralFormatLinkText": "数值格式", - "kbn.advancedSettings.format.bytesFormatText": "“字节”格式的默认{numeralFormatLink}", - "kbn.advancedSettings.format.bytesFormatTitle": "字节格式", - "kbn.advancedSettings.format.currencyFormat.numeralFormatLinkText": "数值格式", - "kbn.advancedSettings.format.currencyFormatText": "“货币”格式的默认{numeralFormatLink}", - "kbn.advancedSettings.format.currencyFormatTitle": "货币格式", "kbn.advancedSettings.format.defaultTypeMapText": "要默认用于每个字段类型的格式名称的映射。如果未显式提及字段类型,则将使用{defaultFormat}", "kbn.advancedSettings.format.defaultTypeMapTitle": "字段类型格式名称", - "kbn.advancedSettings.format.formattingLocale.numeralLanguageLinkText": "数值语言", "kbn.advancedSettings.format.numberFormat.numeralFormatLinkText": "数值格式", "kbn.advancedSettings.format.numberFormatText": "“数字”格式的默认{numeralFormatLink}", "kbn.advancedSettings.format.numberFormatTitle": "数字格式", - "kbn.advancedSettings.format.percentFormat.numeralFormatLinkText": "数值格式", - "kbn.advancedSettings.format.percentFormatText": "“百分比”格式的默认{numeralFormatLink}", - "kbn.advancedSettings.format.percentFormatTitle": "百分比格式", "kbn.advancedSettings.histogram.barTargetText": "在日期直方图中使用“auto”时尝试生成大约此数目的条形", "kbn.advancedSettings.histogram.barTargetTitle": "目标条形数", "kbn.advancedSettings.histogram.maxBarsText": "在日期直方图中不要显示超过该数目的条形", From e217a48ffbfff11945471cf9b4ae04209b9ce33e Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Mon, 13 Jan 2020 16:20:06 -0500 Subject: [PATCH 09/23] Add advanced setting for max decimals --- .../kibana/ui_setting_defaults.js | 20 +++++++++++++++++++ .../field_formats/converters/currency.test.ts | 9 +++++---- .../field_formats/converters/currency.ts | 4 ++-- .../converters/default_number.test.ts | 10 +++++++++- .../converters/default_number.ts | 2 +- .../converters/intl_number_format.ts | 2 +- 6 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js index eecd6fda1e0f8..0f06ebddd6fcf 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js @@ -783,6 +783,26 @@ export function getUiSettingDefaults() { defaultMessage: `This locale will affect number formatting for most formatters.`, }), }, + 'format:default_number:maxDecimals': { + name: i18n.translate('kbn.advancedSettings.format.default_number.maxDecimalsNumber', { + defaultMessage: 'Number maximum decimal places', + }), + value: 3, + type: 'number', + description: i18n.translate( + 'kbn.advancedSettings.format.default_number.maxDecimalsDescription', + { + defaultMessage: `Does not affect currency, bytes, or any formatter using numeral.js`, + } + ), + }, + 'format:currency:maxDecimals': { + name: i18n.translate('kbn.advancedSettings.format.currency.maxDecimalsNumber', { + defaultMessage: 'Currency maximum decimal places', + }), + value: 2, + type: 'number', + }, 'format:number:defaultLocale': { name: i18n.translate('kbn.advancedSettings.format.number.formattingLocaleTitle', { defaultMessage: 'Numeral.js formatting locale', diff --git a/src/plugins/data/common/field_formats/converters/currency.test.ts b/src/plugins/data/common/field_formats/converters/currency.test.ts index 5ac4bb65a428b..849cd26bbf358 100644 --- a/src/plugins/data/common/field_formats/converters/currency.test.ts +++ b/src/plugins/data/common/field_formats/converters/currency.test.ts @@ -39,7 +39,7 @@ describe('CurrencyFormat', () => { expect(formatter.convert(5150000)).toBe('€5,150,000'); }); - test('decimals', () => { + test('default currency decimals', () => { config['format:currency:defaultCurrency'] = 'USD'; const formatter = new CurrencyFormat({}, getConfig); @@ -47,11 +47,12 @@ describe('CurrencyFormat', () => { expect(formatter.convert(1234.56789)).toBe('$1,234.57'); }); - test('2 decimal places', () => { + test('config for decimal places', () => { config['format:currency:defaultCurrency'] = 'USD'; + config['format:currency:maxDecimals'] = 0; - const formatter = new CurrencyFormat({ minDecimals: 2 }, getConfig); + const formatter = new CurrencyFormat({}, getConfig); - expect(formatter.convert(1234)).toBe('$1,234.00'); + expect(formatter.convert(1234.5678)).toBe('$1,235'); }); }); diff --git a/src/plugins/data/common/field_formats/converters/currency.ts b/src/plugins/data/common/field_formats/converters/currency.ts index fead370e7bab3..6ada47376ccfd 100644 --- a/src/plugins/data/common/field_formats/converters/currency.ts +++ b/src/plugins/data/common/field_formats/converters/currency.ts @@ -30,9 +30,9 @@ export class CurrencyFormat extends IntlNumberFormat { static fieldType = KBN_FIELD_TYPES.NUMBER; getParamDefaults = () => ({ - currencyCode: this.getConfig!('format:currency:defaultCurrency'), + currencyCode: this.getConfig?.('format:currency:defaultCurrency'), minDecimals: 0, - maxDecimals: 2, + maxDecimals: this.getConfig?.('format:currency:maxDecimals'), }); id = CurrencyFormat.id; diff --git a/src/plugins/data/common/field_formats/converters/default_number.test.ts b/src/plugins/data/common/field_formats/converters/default_number.test.ts index b58b0d4860d34..3227fcad2154f 100644 --- a/src/plugins/data/common/field_formats/converters/default_number.test.ts +++ b/src/plugins/data/common/field_formats/converters/default_number.test.ts @@ -37,7 +37,7 @@ describe('DefaultNumberFormat', () => { expect(formatter.convert(5150000)).toBe(`5,150,000`); }); - test('number of decimals', () => { + test('decimal parameter', () => { // Node only contains locale data for `en`, so this is ignored. The fallback is tested here config['format:defaultLocale'] = 'ch-DE'; @@ -45,4 +45,12 @@ describe('DefaultNumberFormat', () => { expect(formatter.convert(5150000)).toBe(`5,150,000.00`); }); + + test('max decimals setting', () => { + config['format:default_number:maxDecimals'] = 0; + + const formatter = new DefaultNumberFormat({}, getConfig); + + expect(formatter.convert(10.123456)).toBe(`10`); + }); }); diff --git a/src/plugins/data/common/field_formats/converters/default_number.ts b/src/plugins/data/common/field_formats/converters/default_number.ts index 4e60f9aa03fc6..d8c25a97b591c 100644 --- a/src/plugins/data/common/field_formats/converters/default_number.ts +++ b/src/plugins/data/common/field_formats/converters/default_number.ts @@ -33,7 +33,7 @@ export class DefaultNumberFormat extends IntlNumberFormat { getParamDefaults = () => ({ minDecimals: 0, - maxDecimals: 3, + maxDecimals: this.getConfig?.('format:default_number:maxDecimals'), }); getArguments = () => ({ diff --git a/src/plugins/data/common/field_formats/converters/intl_number_format.ts b/src/plugins/data/common/field_formats/converters/intl_number_format.ts index aba659fb67d41..1bd4facc01dd4 100644 --- a/src/plugins/data/common/field_formats/converters/intl_number_format.ts +++ b/src/plugins/data/common/field_formats/converters/intl_number_format.ts @@ -41,7 +41,7 @@ export abstract class IntlNumberFormat extends FieldFormat { if (isNaN(val)) return ''; - const defaultLocale = this.getConfig && this.getConfig('format:defaultLocale'); + const defaultLocale = this.getConfig?.('format:defaultLocale'); let locales; if (defaultLocale === 'detect') { locales = navigator.languages From 3af88d1a1d4b222daf3db9382e43f2db761e0744 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Mon, 13 Jan 2020 17:10:54 -0500 Subject: [PATCH 10/23] Move currency to i18n --- packages/kbn-i18n/src/core/i18n.ts | 257 ++++++++++++++++++ .../kbn-i18n/src/core/known_currencies.ts | 166 +++++++++++ .../kibana/ui_setting_defaults.js | 38 +-- .../editors/currency/currency.js | 253 +---------------- .../field_format_editor/get_editors.js | 6 +- 5 files changed, 428 insertions(+), 292 deletions(-) create mode 100644 packages/kbn-i18n/src/core/known_currencies.ts diff --git a/packages/kbn-i18n/src/core/i18n.ts b/packages/kbn-i18n/src/core/i18n.ts index 175507ae3b5ff..b21e93d3cc310 100644 --- a/packages/kbn-i18n/src/core/i18n.ts +++ b/packages/kbn-i18n/src/core/i18n.ts @@ -165,6 +165,263 @@ export function getRegisteredLocales() { return Object.keys(translationsForLocale); } +/** + * Top 35 most traded currencies per https://en.wikipedia.org/wiki/Template:Most_traded_currencies + * This list is not a full list of currencies, but the ISO standard full list of currencies + * does not provide some of the amenities of this Wiki list- a mashup of sources would be required + * to provide country name, currency name, and currency symbol. + * The full ISO reference: https://www.currency-iso.org/en/home/tables/table-a1.html + */ +export function getKnownCurrencies() { + return [ + { + name: translate('common.ui.fieldEditor.currency.currencies.USD', { + defaultMessage: 'United States dollar', + }), + code: 'USD', + symbol: 'US$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.EUR', { + defaultMessage: 'Euro', + }), + code: 'EUR', + symbol: '€', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.JPY', { + defaultMessage: 'Japanese yen', + }), + code: 'JPY', + symbol: '¥', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.GBP', { + defaultMessage: 'Pound sterling', + }), + code: 'GBP', + symbol: '£', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.AUD', { + defaultMessage: 'Australian dollar', + }), + code: 'AUD', + symbol: 'A$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.CAD', { + defaultMessage: 'Canadian dollar', + }), + code: 'CAD', + symbol: 'C$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.CHF', { + defaultMessage: 'Swiss franc', + }), + code: 'CHF', + symbol: 'CHF', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.CNY', { + defaultMessage: 'Renminbi', + }), + code: 'CNY', + symbol: '元', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.HKD', { + defaultMessage: 'Hong Kong dollar', + }), + code: 'HKD', + symbol: 'HK$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.NZD', { + defaultMessage: 'New Zealand dollar', + }), + code: 'NZD', + symbol: 'NZ$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.SEK', { + defaultMessage: 'Swedish krona', + }), + code: 'SEK', + symbol: 'kr', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.KRW', { + defaultMessage: 'South Korean won', + }), + code: 'KRW', + symbol: '₩', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.SGD', { + defaultMessage: 'Singapore dollar', + }), + code: 'SGD', + symbol: 'S$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.NOK', { + defaultMessage: 'Norwegian krone', + }), + code: 'NOK', + symbol: 'kr', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.MXN', { + defaultMessage: 'Mexican peso', + }), + code: 'MXN', + symbol: '$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.INR', { + defaultMessage: 'Indian rupee', + }), + code: 'INR', + symbol: '₹', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.RUB', { + defaultMessage: 'Russian ruble', + }), + code: 'RUB', + symbol: '₽', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.ZAR', { + defaultMessage: 'South African rand', + }), + code: 'ZAR', + symbol: 'R', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.TRY', { + defaultMessage: 'Turkish lira', + }), + code: 'TRY', + symbol: '₺', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.BRL', { + defaultMessage: 'Brazilian real', + }), + code: 'BRL', + symbol: 'R$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.TWD', { + defaultMessage: 'New Taiwan dollar', + }), + code: 'TWD', + symbol: 'NT$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.DKK', { + defaultMessage: 'Danish krone', + }), + code: 'DKK', + symbol: 'kr', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.PLN', { + defaultMessage: 'Polish zloty', + }), + code: 'PLN', + symbol: 'zł', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.THB', { + defaultMessage: 'Thai baht', + }), + code: 'THB', + symbol: '฿', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.IDR', { + defaultMessage: 'Indonesian rupiah', + }), + code: 'IDR', + symbol: 'Rp', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.HUF', { + defaultMessage: 'Hungarian forint', + }), + code: 'HUF', + symbol: 'Ft', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.CZK', { + defaultMessage: 'Czech koruna', + }), + code: 'CZK', + symbol: 'Kč', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.ILS', { + defaultMessage: 'Israeli new shekel', + }), + code: 'ILS', + symbol: '₪', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.CLP', { + defaultMessage: 'Chilean peso', + }), + code: 'CLP', + symbol: 'CLP$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.PHP', { + defaultMessage: 'Philippine peso', + }), + code: 'PHP', + symbol: '₱', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.AED', { + defaultMessage: 'UAE dirham', + }), + code: 'AED', + symbol: 'د.إ', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.COP', { + defaultMessage: 'Colombian peso', + }), + code: 'COP', + symbol: 'COL$', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.SAR', { + defaultMessage: 'Saudi riyal', + }), + code: 'SAR', + symbol: '﷼', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.MYR', { + defaultMessage: 'Malaysian ringgit', + }), + code: 'MYR', + symbol: 'RM', + }, + { + name: translate('common.ui.fieldEditor.currency.currencies.RON', { + defaultMessage: 'Romanian leu', + }), + code: 'RON', + symbol: 'L', + }, + ]; +} + /** * Returns array of locales that can be used with i18n, even if there is no translation. * This could be used for number formatting. diff --git a/packages/kbn-i18n/src/core/known_currencies.ts b/packages/kbn-i18n/src/core/known_currencies.ts new file mode 100644 index 0000000000000..50c079fa1ab2c --- /dev/null +++ b/packages/kbn-i18n/src/core/known_currencies.ts @@ -0,0 +1,166 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// Top 35 most traded currencies per https://en.wikipedia.org/wiki/Template:Most_traded_currencies +// This list is not a full list of currencies, but the ISO standard full list of currencies +// does not provide some of the amenities of this Wiki list- a mashup of sources would be required +// to provide country name, currency name, and currency symbol. +// The full ISO reference: https://www.currency-iso.org/en/home/tables/table-a1.html +export const topCurrencies = [ + { + code: 'USD', + symbol: 'US$', + }, + { + code: 'EUR', + symbol: '€', + }, + { + code: 'JPY', + symbol: '¥', + }, + { + code: 'GBP', + symbol: '£', + }, + { + code: 'AUD', + symbol: 'A$', + }, + { + code: 'CAD', + symbol: 'C$', + }, + { + code: 'CHF', + symbol: 'CHF', + }, + { + code: 'CNY', + symbol: '元', + }, + { + code: 'HKD', + symbol: 'HK$', + }, + { + code: 'NZD', + symbol: 'NZ$', + }, + { + code: 'SEK', + symbol: 'kr', + }, + { + code: 'KRW', + symbol: '₩', + }, + { + code: 'SGD', + symbol: 'S$', + }, + { + code: 'NOK', + symbol: 'kr', + }, + { + code: 'MXN', + symbol: '$', + }, + { + code: 'INR', + symbol: '₹', + }, + { + code: 'RUB', + symbol: '₽', + }, + { + code: 'ZAR', + symbol: 'R', + }, + { + code: 'TRY', + symbol: '₺', + }, + { + code: 'BRL', + symbol: 'R$', + }, + { + code: 'TWD', + symbol: 'NT$', + }, + { + code: 'DKK', + symbol: 'kr', + }, + { + code: 'PLN', + symbol: 'zł', + }, + { + code: 'THB', + symbol: '฿', + }, + { + code: 'IDR', + symbol: 'Rp', + }, + { + code: 'HUF', + symbol: 'Ft', + }, + { + code: 'CZK', + symbol: 'Kč', + }, + { + code: 'ILS', + symbol: '₪', + }, + { + code: 'CLP', + symbol: 'CLP$', + }, + { + code: 'PHP', + symbol: '₱', + }, + { + code: 'AED', + symbol: 'د.إ', + }, + { + code: 'COP', + symbol: 'COL$', + }, + { + code: 'SAR', + symbol: '﷼', + }, + { + code: 'MYR', + symbol: 'RM', + }, + { + code: 'RON', + symbol: 'L', + }, +]; diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js index 0f06ebddd6fcf..902671c09a4da 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js @@ -22,43 +22,7 @@ import numeralLanguages from '@elastic/numeral/languages'; import { i18n } from '@kbn/i18n'; import { DEFAULT_QUERY_LANGUAGE } from '../../../plugins/data/common'; -const topCurrencies = [ - { name: 'United States dollar', code: 'USD', symbol: 'US$' }, - { name: 'Euro', code: 'EUR', symbol: '€' }, - { name: 'Japanese yen', code: 'JPY', symbol: '¥' }, - { name: 'Pound sterling', code: 'GBP', symbol: '£' }, - { name: 'Australian dollar', code: 'AUD', symbol: 'A$' }, - { name: 'Canadian dollar', code: 'CAD', symbol: 'C$' }, - { name: 'Swiss franc', code: 'CHF', symbol: 'CHF' }, - { name: 'Renminbi', code: 'CNY', symbol: '元' }, - { name: 'Hong Kong dollar', code: 'HKD', symbol: 'HK$' }, - { name: 'New Zealand dollar', code: 'NZD', symbol: 'NZ$' }, - { name: 'Swedish krona', code: 'SEK', symbol: 'kr' }, - { name: 'South Korean won', code: 'KRW', symbol: '₩' }, - { name: 'Singapore dollar', code: 'SGD', symbol: 'S$' }, - { name: 'Norwegian krone', code: 'NOK', symbol: 'kr' }, - { name: 'Mexican peso', code: 'MXN', symbol: '$' }, - { name: 'Indian rupee', code: 'INR', symbol: '₹' }, - { name: 'Russian ruble', code: 'RUB', symbol: '₽' }, - { name: 'South African rand', code: 'ZAR', symbol: 'R' }, - { name: 'Turkish lira', code: 'TRY', symbol: '₺' }, - { name: 'Brazilian real', code: 'BRL', symbol: 'R$' }, - { name: 'New Taiwan dollar', code: 'TWD', symbol: 'NT$' }, - { name: 'Danish krone', code: 'DKK', symbol: 'kr' }, - { name: 'Polish zloty', code: 'PLN', symbol: 'zł' }, - { name: 'Thai baht', code: 'THB', symbol: '฿' }, - { name: 'Indonesian rupiah', code: 'IDR', symbol: 'Rp' }, - { name: 'Hungarian forint', code: 'HUF', symbol: 'Ft' }, - { name: 'Czech koruna', code: 'CZK', symbol: 'Kč' }, - { name: 'Israeli new shekel', code: 'ILS', symbol: '₪' }, - { name: 'Chilean peso', code: 'CLP', symbol: 'CLP$' }, - { name: 'Philippine peso', code: 'PHP', symbol: '₱' }, - { name: 'UAE dirham', code: 'AED', symbol: 'د.إ' }, - { name: 'Colombian peso', code: 'COP', symbol: 'COL$' }, - { name: 'Saudi riyal', code: 'SAR', symbol: '﷼' }, - { name: 'Malaysian ringgit', code: 'MYR', symbol: 'RM' }, - { name: 'Romanian leu', code: 'RON', symbol: 'L' }, -]; +const topCurrencies = i18n.getKnownCurrencies(); export function getUiSettingDefaults() { const weekdays = moment.weekdays().slice(); diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js index 663caa96305bc..38c40a3d93531 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js @@ -25,258 +25,7 @@ import { EuiFormRow, EuiComboBox, EuiFieldText } from '@elastic/eui'; import { DefaultNumberFormatEditor } from '../default_number'; import { FormatEditorSamples } from '../../samples'; -// Top 35 most traded currencies per https://en.wikipedia.org/wiki/Template:Most_traded_currencies -// This list is not a full list of currencies, but the ISO standard full list of currencies -// does not provide some of the amenities of this Wiki list- a mashup of sources would be required -// to provide country name, currency name, and currency symbol. -// The full ISO reference: https://www.currency-iso.org/en/home/tables/table-a1.html -const topCurrencies = [ - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.USD', { - defaultMessage: 'United States dollar', - }), - code: 'USD', - symbol: 'US$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.EUR', { - defaultMessage: 'Euro', - }), - code: 'EUR', - symbol: '€', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.JPY', { - defaultMessage: 'Japanese yen', - }), - code: 'JPY', - symbol: '¥', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.GBP', { - defaultMessage: 'Pound sterling', - }), - code: 'GBP', - symbol: '£', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.AUD', { - defaultMessage: 'Australian dollar', - }), - code: 'AUD', - symbol: 'A$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.CAD', { - defaultMessage: 'Canadian dollar', - }), - code: 'CAD', - symbol: 'C$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.CHF', { - defaultMessage: 'Swiss franc', - }), - code: 'CHF', - symbol: 'CHF', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.CNY', { - defaultMessage: 'Renminbi', - }), - code: 'CNY', - symbol: '元', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.HKD', { - defaultMessage: 'Hong Kong dollar', - }), - code: 'HKD', - symbol: 'HK$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.NZD', { - defaultMessage: 'New Zealand dollar', - }), - code: 'NZD', - symbol: 'NZ$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.SEK', { - defaultMessage: 'Swedish krona', - }), - code: 'SEK', - symbol: 'kr', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.KRW', { - defaultMessage: 'South Korean won', - }), - code: 'KRW', - symbol: '₩', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.SGD', { - defaultMessage: 'Singapore dollar', - }), - code: 'SGD', - symbol: 'S$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.NOK', { - defaultMessage: 'Norwegian krone', - }), - code: 'NOK', - symbol: 'kr', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.MXN', { - defaultMessage: 'Mexican peso', - }), - code: 'MXN', - symbol: '$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.INR', { - defaultMessage: 'Indian rupee', - }), - code: 'INR', - symbol: '₹', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.RUB', { - defaultMessage: 'Russian ruble', - }), - code: 'RUB', - symbol: '₽', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.ZAR', { - defaultMessage: 'South African rand', - }), - code: 'ZAR', - symbol: 'R', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.TRY', { - defaultMessage: 'Turkish lira', - }), - code: 'TRY', - symbol: '₺', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.BRL', { - defaultMessage: 'Brazilian real', - }), - code: 'BRL', - symbol: 'R$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.TWD', { - defaultMessage: 'New Taiwan dollar', - }), - code: 'TWD', - symbol: 'NT$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.DKK', { - defaultMessage: 'Danish krone', - }), - code: 'DKK', - symbol: 'kr', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.PLN', { - defaultMessage: 'Polish zloty', - }), - code: 'PLN', - symbol: 'zł', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.THB', { - defaultMessage: 'Thai baht', - }), - code: 'THB', - symbol: '฿', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.IDR', { - defaultMessage: 'Indonesian rupiah', - }), - code: 'IDR', - symbol: 'Rp', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.HUF', { - defaultMessage: 'Hungarian forint', - }), - code: 'HUF', - symbol: 'Ft', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.CZK', { - defaultMessage: 'Czech koruna', - }), - code: 'CZK', - symbol: 'Kč', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.ILS', { - defaultMessage: 'Israeli new shekel', - }), - code: 'ILS', - symbol: '₪', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.CLP', { - defaultMessage: 'Chilean peso', - }), - code: 'CLP', - symbol: 'CLP$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.PHP', { - defaultMessage: 'Philippine peso', - }), - code: 'PHP', - symbol: '₱', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.AED', { - defaultMessage: 'UAE dirham', - }), - code: 'AED', - symbol: 'د.إ', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.COP', { - defaultMessage: 'Colombian peso', - }), - code: 'COP', - symbol: 'COL$', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.SAR', { - defaultMessage: 'Saudi riyal', - }), - code: 'SAR', - symbol: '﷼', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.MYR', { - defaultMessage: 'Malaysian ringgit', - }), - code: 'MYR', - symbol: 'RM', - }, - { - name: i18n.translate('common.ui.fieldEditor.currency.currencies.RON', { - defaultMessage: 'Romanian leu', - }), - code: 'RON', - symbol: 'L', - }, -]; +const topCurrencies = i18n.getKnownCurrencies(); export class CurrencyFormatEditor extends DefaultNumberFormatEditor { static formatId = 'currency'; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js b/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js index 66dc70157d0c2..33eba864711d0 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/get_editors.js @@ -33,16 +33,16 @@ import { TruncateFormatEditor } from './editors/truncate'; import { UrlFormatEditor } from './editors/url/url'; export const editors = [ - DefaultNumberFormatEditor, BytesFormatEditor, ColorFormatEditor, CurrencyFormatEditor, + CustomNumberFormatEditor, DateFormatEditor, DateNanosFormatEditor, - CustomNumberFormatEditor, - ShortNumberFormatEditor, + DefaultNumberFormatEditor, DurationFormatEditor, PercentFormatEditor, + ShortNumberFormatEditor, StaticLookupFormatEditor, StringFormatEditor, TruncateFormatEditor, From fa1797cf36391c04dca178d07208fc877fb1ae21 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Mon, 13 Jan 2020 22:51:35 -0500 Subject: [PATCH 11/23] Add snapshots for format editors --- .../bytes/__snapshots__/bytes.test.js.snap | 50 ---- .../__snapshots__/currency.test.js.snap | 255 ++++++++++++++++++ .../editors/currency/currency.js | 6 +- .../editors/currency/currency.test.js | 77 ++++++ .../__snapshots__/default_number.test.js.snap | 83 ++++++ .../{number.js => default_number.js} | 0 .../default_number/default_number.test.js | 53 ++++ .../editors/default_number/index.js | 2 +- .../number/__snapshots__/number.test.js.snap | 8 + .../__snapshots__/percent.test.js.snap | 60 ++--- .../field_formats/converters/currency.test.ts | 8 + 11 files changed, 516 insertions(+), 86 deletions(-) create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/__snapshots__/currency.test.js.snap create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.test.js create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/__snapshots__/default_number.test.js.snap rename src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/{number.js => default_number.js} (100%) create mode 100644 src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/default_number.test.js diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.js.snap index 463c1bfb975f5..9d60e0142a713 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.js.snap @@ -2,56 +2,6 @@ exports[`BytesFormatEditor should render normally 1`] = ` - - - -   - - - - } - isInvalid={false} - label={ - - 0,0.[000]b - , - } - } - /> - } - labelType="label" - > - - + + } + labelType="label" + > + + + + } + labelType="label" + > + + + + } + labelType="label" + > + + + + +`; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js index 38c40a3d93531..7421b84764434 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js @@ -68,11 +68,14 @@ export class CurrencyFormatEditor extends DefaultNumberFormatEditor { } > { this.onChange({ currencyCode: e.target.value ? e.target.value.toUpperCase() : '' }); diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.test.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.test.js new file mode 100644 index 0000000000000..07c2831cc6fa9 --- /dev/null +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.test.js @@ -0,0 +1,77 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { CurrencyFormatEditor } from './currency'; + +const fieldType = 'number'; +const format = { + getConverterFor: jest.fn().mockImplementation(() => input => input * 2), + getParamDefaults: jest.fn().mockImplementation(() => { + // return { pattern: '0,0.[000]b' }; + return { currencyCode: 'EUR' }; + }), +}; +const formatParams = {}; +const onChange = jest.fn(); +const onError = jest.fn(); + +describe('CurrencyFormatEditor', () => { + it('should have a formatId', () => { + expect(CurrencyFormatEditor.formatId).toEqual('currency'); + }); + + it('should render normally', async () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); + + it('should error if an incomplete other currency is added', async () => { + const component = shallow( + + ); + const options = component.find('[data-test-subj="fieldEditor-currency-currencies"]'); + options.prop('onChange')([{ value: null }]); + + const otherInput = component.find('[data-test-subj="fieldEditor-currency-otherInput"]'); + otherInput.prop('onChange')({ target: { value: 'AB' } }); + expect(onError).toHaveBeenCalled(); + + onError.mockClear(); + + otherInput.prop('onChange')({ target: { value: 'ABC' } }); + expect(onError).not.toHaveBeenCalled(); + }); +}); diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/__snapshots__/default_number.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/__snapshots__/default_number.test.js.snap new file mode 100644 index 0000000000000..85f0931715bf8 --- /dev/null +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/__snapshots__/default_number.test.js.snap @@ -0,0 +1,83 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DefaultNumberFormatEditor should render normally 1`] = ` + + + } + labelType="label" + > + + + + } + labelType="label" + > + + + + +`; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/number.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/default_number.js similarity index 100% rename from src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/number.js rename to src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/default_number.js diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/default_number.test.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/default_number.test.js new file mode 100644 index 0000000000000..0f850c85254b3 --- /dev/null +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/default_number.test.js @@ -0,0 +1,53 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { DefaultNumberFormatEditor } from './default_number'; + +const fieldType = 'number'; +const format = { + getConverterFor: jest.fn().mockImplementation(() => input => input * 2), + getParamDefaults: jest.fn().mockImplementation(() => { + return {}; + }), +}; +const formatParams = {}; +const onChange = jest.fn(); +const onError = jest.fn(); + +describe('DefaultNumberFormatEditor', () => { + it('should have a formatId', () => { + expect(DefaultNumberFormatEditor.formatId).toEqual('default_number'); + }); + + it('should render normally', async () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/index.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/index.js index abee50aaa303e..e5c11a1752585 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/index.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/default_number/index.js @@ -17,4 +17,4 @@ * under the License. */ -export { DefaultNumberFormatEditor } from './number'; +export { DefaultNumberFormatEditor } from './default_number'; diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/__snapshots__/number.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/__snapshots__/number.test.js.snap index 42346f022181c..e1d8fdab866a3 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/__snapshots__/number.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/number/__snapshots__/number.test.js.snap @@ -76,6 +76,14 @@ exports[`NumberFormatEditor should render normally 1`] = ` "input": 0.52, "output": 1.04, }, + Object { + "input": 1.23456789e-15, + "output": 2.46913578e-15, + }, + Object { + "input": 1.99e+22, + "output": 3.98e+22, + }, ] } /> diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.js.snap b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.js.snap index fea665a918f06..0b989c2b8dcaa 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.js.snap +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.js.snap @@ -5,51 +5,43 @@ exports[`PercentFormatEditor should render normally 1`] = ` - - -   - - - + label={ + } - isInvalid={false} + labelType="label" + > + + + - 0,0.[000]% - , - } - } + defaultMessage="Maximum decimal places" + id="common.ui.fieldEditor.defaultNumber.maxDecimalPlacesLabel" + values={Object {}} /> } labelType="label" > - { expect(formatter.convert(1234.56789)).toBe('$1,234.57'); }); + test('override default currency', () => { + config['format:currency:defaultCurrency'] = 'USD'; + + const formatter = new CurrencyFormat({ currencyCode: 'EUR' }, getConfig); + + expect(formatter.convert(1234.56789)).toBe('€1,234.57'); + }); + test('config for decimal places', () => { config['format:currency:defaultCurrency'] = 'USD'; config['format:currency:maxDecimals'] = 0; From a7c3d17495ed7d5be173960bdaf5b913767dafe1 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Tue, 14 Jan 2020 09:47:08 -0500 Subject: [PATCH 12/23] Fix tests --- packages/kbn-i18n/src/angular/provider.ts | 3 +++ .../ui/field_formats/mixin/field_formats_service.test.ts | 8 ++++---- .../agg_types/buckets/create_filter/histogram.test.ts | 2 +- .../public/agg_types/buckets/create_filter/range.test.ts | 2 +- src/legacy/ui/public/agg_types/buckets/range.test.ts | 6 +++--- .../field_editor/lib/__tests__/get_default_format.test.js | 8 ++++---- 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/packages/kbn-i18n/src/angular/provider.ts b/packages/kbn-i18n/src/angular/provider.ts index 7333cf4bb5865..c0ba951e35385 100644 --- a/packages/kbn-i18n/src/angular/provider.ts +++ b/packages/kbn-i18n/src/angular/provider.ts @@ -31,6 +31,9 @@ export class I18nProvider implements angular.IServiceProvider { public setFormats = i18n.setFormats; public getFormats = i18n.getFormats; public getRegisteredLocales = i18n.getRegisteredLocales; + public getKnownCurrencies = i18n.getKnownCurrencies; + public getKnownLocales = i18n.getKnownLocales; + public getKnownLocalesWithDisplay = i18n.getKnownLocalesWithDisplay; public init = i18n.init; public load = i18n.load; public $get = () => i18n.translate; diff --git a/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts b/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts index 2b66574896302..10e3012f7e0e7 100644 --- a/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts +++ b/src/legacy/ui/field_formats/mixin/field_formats_service.test.ts @@ -18,13 +18,13 @@ */ import { FieldFormatsService } from './field_formats_service'; -import { DefaultNumberFormat } from '../../../../plugins/data/public'; +import { DefaultNumberFormat, CustomNumberFormat } from '../../../../plugins/data/public'; const getConfig = (key: string) => { switch (key) { case 'format:defaultTypeMap': return { - number: { id: 'number', params: {} }, + number: { id: 'default_number', params: {} }, _default_: { id: 'string', params: {} }, }; case 'format:number:defaultPattern': @@ -36,7 +36,7 @@ describe('FieldFormatsService', () => { let fieldFormatsService: FieldFormatsService; beforeEach(() => { - const fieldFormatClasses = [DefaultNumberFormat]; + const fieldFormatClasses = [DefaultNumberFormat, CustomNumberFormat]; fieldFormatsService = new FieldFormatsService(fieldFormatClasses, getConfig); }); @@ -50,7 +50,7 @@ describe('FieldFormatsService', () => { test('getDefaultInstance returns default FieldFormat instance for fieldType', () => { const instance = fieldFormatsService.getDefaultInstance('number'); - expect(instance.type.id).toBe('number'); + expect(instance.type.id).toBe('default_number'); expect(instance.convert('0.33333')).toBe('0.333'); }); }); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts index d07cf84aef4d9..a967ce6ab16f0 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/histogram.test.ts @@ -68,7 +68,7 @@ describe('AggConfig Filters', () => { expect(filter.range).toHaveProperty('bytes'); expect(filter.range.bytes).toHaveProperty('gte', 2048); expect(filter.range.bytes).toHaveProperty('lt', 3072); - expect(filter.meta).toHaveProperty('formattedValue', '2,048'); + expect(filter.meta).toHaveProperty('formattedValue', '2KB'); }); }); }); diff --git a/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts b/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts index dc02b773edc42..d8ada0b9544f1 100644 --- a/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/create_filter/range.test.ts @@ -72,7 +72,7 @@ describe('AggConfig Filters', () => { expect(filter.range).toHaveProperty('bytes'); expect(filter.range.bytes).toHaveProperty('gte', 1024.0); expect(filter.range.bytes).toHaveProperty('lt', 2048.0); - expect(filter.meta).toHaveProperty('formattedValue', '≥ 1,024 and < 2,048'); + expect(filter.meta).toHaveProperty('formattedValue', '≥ 1KB and < 2KB'); }); }); }); diff --git a/src/legacy/ui/public/agg_types/buckets/range.test.ts b/src/legacy/ui/public/agg_types/buckets/range.test.ts index 22b92874f1f1a..6acdff76d24af 100644 --- a/src/legacy/ui/public/agg_types/buckets/range.test.ts +++ b/src/legacy/ui/public/agg_types/buckets/range.test.ts @@ -85,9 +85,9 @@ describe('Range Agg', () => { const format = (val: any) => agg.fieldFormatter()(agg.getKey(val)); - expect(format(buckets[0])).toBe('≥ -∞ and < 1 KB'); - expect(format(buckets[1])).toBe('≥ 1 KB and < 2.5 KB'); - expect(format(buckets[2])).toBe('≥ 2.5 KB and < +∞'); + expect(format(buckets[0])).toBe('≥ -∞ and < 1KB'); + expect(format(buckets[1])).toBe('≥ 1KB and < 2.5KB'); + expect(format(buckets[2])).toBe('≥ 2.5KB and < +∞'); }); }); }); diff --git a/src/legacy/ui/public/field_editor/lib/__tests__/get_default_format.test.js b/src/legacy/ui/public/field_editor/lib/__tests__/get_default_format.test.js index e4084797c1b1e..e7bf431032dc2 100644 --- a/src/legacy/ui/public/field_editor/lib/__tests__/get_default_format.test.js +++ b/src/legacy/ui/public/field_editor/lib/__tests__/get_default_format.test.js @@ -18,7 +18,7 @@ */ import { getDefaultFormat } from '../get_default_format'; -import { NumberFormat } from '../../../../../../plugins/data/public'; +import { DefaultNumberFormat } from '../../../../../../plugins/data/public'; const getConfig = () => { return '0,0.[000]'; @@ -26,12 +26,12 @@ const getConfig = () => { describe('getDefaultFormat', () => { it('should create default format', () => { - const DefaultFormat = getDefaultFormat(NumberFormat); + const DefaultFormat = getDefaultFormat(DefaultNumberFormat); const defaultFormatObject = new DefaultFormat(null, getConfig); - const formatObject = new NumberFormat(null, getConfig); + const formatObject = new DefaultNumberFormat(null, getConfig); expect(DefaultFormat.id).toEqual(''); - expect(DefaultFormat.resolvedTitle).toEqual(NumberFormat.title); + expect(DefaultFormat.resolvedTitle).toEqual(DefaultNumberFormat.title); expect(DefaultFormat.title).toEqual('- Default -'); expect(JSON.stringify(defaultFormatObject.params())).toEqual( JSON.stringify(formatObject.params()) From 3fbdfb095199c0c694e06e67fb9b6e35c3d9276e Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Fri, 17 Jan 2020 16:27:45 -0500 Subject: [PATCH 13/23] Update reporting to include locale --- config/kibana.yml | 2 ++ .../development/pdf-integration.asciidoc | 2 ++ .../kibana/ui_setting_defaults.js | 10 ++++++-- .../editors/currency/currency.test.js | 1 - .../field_formats/converters/bytes.test.ts | 6 ++--- .../common/field_formats/converters/bytes.ts | 2 +- .../converters/intl_number_format.ts | 25 ++++++++++++++----- .../workpad_header/workpad_export/utils.ts | 1 + .../common/lib/screenshots/index.ts | 3 ++- .../common/lib/screenshots/types.ts | 1 + .../png/server/create_job/index.ts | 3 ++- .../png/server/execute_job/index.ts | 1 + .../png/server/lib/generate_png.ts | 2 ++ .../reporting/export_types/png/types.d.ts | 2 ++ .../printable_pdf/server/create_job/index.ts | 3 ++- .../server/execute_job/index.test.js | 6 +++-- .../printable_pdf/server/execute_job/index.ts | 3 ++- .../printable_pdf/server/lib/generate_pdf.ts | 2 ++ .../export_types/printable_pdf/types.d.ts | 2 ++ .../public/components/report_info_button.tsx | 4 +++ .../reporting/public/lib/job_queue_client.ts | 1 + .../share_context_menu/register_reporting.tsx | 12 +++++++++ .../browsers/chromium/driver_factory/index.ts | 10 ++++++-- 23 files changed, 83 insertions(+), 21 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index 0780841ca057e..bca9b30118694 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -103,6 +103,8 @@ # and all requests. #logging.verbose: false +logging.events.log: ['warning', 'error', 'fatal', 'info', 'reporting'] + # Set the interval in milliseconds to sample system and process performance # metrics. Minimum is 100ms. Defaults to 5000. #ops.interval: 5000 diff --git a/docs/user/reporting/development/pdf-integration.asciidoc b/docs/user/reporting/development/pdf-integration.asciidoc index dc9e63f34b25e..256477b019823 100644 --- a/docs/user/reporting/development/pdf-integration.asciidoc +++ b/docs/user/reporting/development/pdf-integration.asciidoc @@ -11,6 +11,7 @@ interface jobParameters { objectType: string; title: string; browserTimezone: string; + browserLocales: string[]; relativeUrls: string[]; layout: { id: string; @@ -23,6 +24,7 @@ interface jobParameters { ---- `jobParameters.browserTimezone` is a string that appears in the tz database +`jobParameters.browserLocales` is an ordered array of locale codes such as "en-US" `jobParameters.layout.id` presently only accepts "print" and "preserve_layout" `jobParameters.layout.dimensions` is only currently used by "preserve_layout" diff --git a/src/legacy/core_plugins/kibana/ui_setting_defaults.js b/src/legacy/core_plugins/kibana/ui_setting_defaults.js index 22b90cf26e017..f495eabbe3991 100644 --- a/src/legacy/core_plugins/kibana/ui_setting_defaults.js +++ b/src/legacy/core_plugins/kibana/ui_setting_defaults.js @@ -36,9 +36,15 @@ export function getUiSettingDefaults() { }), ]; - const locales = ['detect', ...i18n.getKnownLocales()]; + const locales = ['numeral', 'detect', ...i18n.getKnownLocales()]; const localeDisplay = Object.fromEntries( [ + [ + 'numeral', + i18n.translate('kbn.advancedSettings.locale.useNumeralLocale', { + defaultMessage: 'Use the same value as format:number:defaultLocale', + }), + ], [ 'detect', i18n.translate('kbn.advancedSettings.locale.detectFromBrowserLabel', { @@ -745,7 +751,7 @@ export function getUiSettingDefaults() { name: i18n.translate('kbn.advancedSettings.format.formattingLocaleTitle', { defaultMessage: 'Default number display locale', }), - value: 'detect', + value: 'numeral', type: 'select', options: locales, optionLabels: localeDisplay, diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.test.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.test.js index 07c2831cc6fa9..736d27bbe2987 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.test.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.test.js @@ -26,7 +26,6 @@ const fieldType = 'number'; const format = { getConverterFor: jest.fn().mockImplementation(() => input => input * 2), getParamDefaults: jest.fn().mockImplementation(() => { - // return { pattern: '0,0.[000]b' }; return { currencyCode: 'EUR' }; }), }; diff --git a/src/plugins/data/common/field_formats/converters/bytes.test.ts b/src/plugins/data/common/field_formats/converters/bytes.test.ts index 5ad3886267018..56b2c48766c95 100644 --- a/src/plugins/data/common/field_formats/converters/bytes.test.ts +++ b/src/plugins/data/common/field_formats/converters/bytes.test.ts @@ -31,13 +31,13 @@ describe('BytesFormat', () => { test('default pattern', () => { const formatter = new BytesFormat({}, getConfig); - expect(formatter.convert(5150000)).toBe('4.911MB'); + expect(formatter.convert(5150123)).toBe('4.9MB'); }); test('custom pattern and locale', () => { config['format:number:defaultLocale'] = 'de'; - const formatter = new BytesFormat({ pattern: '0,0.[0]b' }, getConfig); + const formatter = new BytesFormat({ pattern: '0,0.[000]b' }, getConfig); - expect(formatter.convert('10500')).toBe('10,3KB'); + expect(formatter.convert('10513')).toBe('10,267KB'); }); }); diff --git a/src/plugins/data/common/field_formats/converters/bytes.ts b/src/plugins/data/common/field_formats/converters/bytes.ts index 857097242ec48..e322a836b72e8 100644 --- a/src/plugins/data/common/field_formats/converters/bytes.ts +++ b/src/plugins/data/common/field_formats/converters/bytes.ts @@ -31,7 +31,7 @@ export class BytesFormat extends NumeralFormat { title = BytesFormat.title; getParamDefaults = () => ({ - pattern: '0,0.[000]b', + pattern: '0,0.[0]b', }); allowsNumericalAggregations = true; diff --git a/src/plugins/data/common/field_formats/converters/intl_number_format.ts b/src/plugins/data/common/field_formats/converters/intl_number_format.ts index 1bd4facc01dd4..e670f7b2bf690 100644 --- a/src/plugins/data/common/field_formats/converters/intl_number_format.ts +++ b/src/plugins/data/common/field_formats/converters/intl_number_format.ts @@ -21,6 +21,11 @@ import { FieldFormat } from '../field_format'; import { TextContextTypeConvert } from '../types'; import { KBN_FIELD_TYPES } from '../../kbn_field_types/types'; +const numeralLocaleToBrowser: Record = { + 'be-nl': 'nl-BE', + chs: 'zh-CN', +}; + export abstract class IntlNumberFormat extends FieldFormat { static fieldType = KBN_FIELD_TYPES.NUMBER; @@ -41,9 +46,20 @@ export abstract class IntlNumberFormat extends FieldFormat { if (isNaN(val)) return ''; + const locales = this.getLocales(); + const inst = new Intl.NumberFormat(locales, this.getArguments()); + + return inst.format(val); + } + + getLocales = () => { + const numeralLocale = this.getConfig?.('format:number:defaultLocale'); const defaultLocale = this.getConfig?.('format:defaultLocale'); let locales; - if (defaultLocale === 'detect') { + if (defaultLocale === 'numeral') { + // Special case where numeral uses an invalid locale + locales = [numeralLocaleToBrowser[numeralLocale] || numeralLocale, 'en']; + } else if (defaultLocale === 'detect') { locales = navigator.languages ? navigator.languages.concat(['en']) : [navigator.language, defaultLocale, 'en']; @@ -52,11 +68,8 @@ export abstract class IntlNumberFormat extends FieldFormat { } else { locales = ['en']; } - - const inst = new Intl.NumberFormat(locales, this.getArguments()); - - return inst.format(val); - } + return locales; + }; textConvert: TextContextTypeConvert = val => { return this.getConvertedValue(val); diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts index f7f191a48de82..76e9af4763c0d 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.ts @@ -52,6 +52,7 @@ function getPdfUrlParts( const jobParams = { browserTimezone: 'America/Phoenix', // TODO: get browser timezone, or Kibana setting? + browserLocales: ['en-US', 'en'], // TODO: get browser locales layout: { dimensions: { width, height }, id: PDF_LAYOUT_TYPE, diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts index b83021d5e38dd..801a6666507c9 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/index.ts @@ -32,9 +32,10 @@ export function screenshotsObservableFactory( conditionalHeaders, layout, browserTimezone, + browserLocales, }: ScreenshotObservableOpts): Rx.Observable { const create$ = browserDriverFactory.createPage( - { viewport: layout.getBrowserViewport(), browserTimezone }, + { viewport: layout.getBrowserViewport(), browserTimezone, browserLocales }, logger ); diff --git a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/types.ts b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/types.ts index 78cd42f0cae2f..2b7e446bd416a 100644 --- a/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/types.ts +++ b/x-pack/legacy/plugins/reporting/export_types/common/lib/screenshots/types.ts @@ -14,6 +14,7 @@ export interface ScreenshotObservableOpts { conditionalHeaders: ConditionalHeaders; layout: LayoutInstance; browserTimezone: string; + browserLocales: string[]; } export interface TimeRange { diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts index 3f03246106d3e..8353789263fdc 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/create_job/index.ts @@ -21,7 +21,7 @@ export const createJobFactory: CreateJobFactory { @@ -36,6 +37,7 @@ export function generatePngObservableFactory( conditionalHeaders, layout, browserTimezone, + browserLocales, }).pipe( map(([{ screenshots }]) => { if (screenshots.length !== 1) { diff --git a/x-pack/legacy/plugins/reporting/export_types/png/types.d.ts b/x-pack/legacy/plugins/reporting/export_types/png/types.d.ts index 4f5f295f33f4a..9c24559242454 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/types.d.ts +++ b/x-pack/legacy/plugins/reporting/export_types/png/types.d.ts @@ -13,6 +13,7 @@ export interface JobParamsPNG { title: string; relativeUrl: string; browserTimezone: string; + browserLocales: string[]; layout: LayoutInstance; } @@ -20,6 +21,7 @@ export interface JobParamsPNG { export interface JobDocPayloadPNG extends JobDocPayload { basePath?: string; browserTimezone: string; + browserLocales: string[]; forceNow?: string; layout: LayoutParams; relativeUrl: string; diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts index a8cc71175cffe..0b68a0f14cf8f 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/create_job/index.ts @@ -21,7 +21,7 @@ export const createJobFactory: CreateJobFactory { return await crypto.encrypt(headers); }; -test(`passes browserTimezone to generatePdf`, async () => { +test(`passes browserTimezone and browserLocales to generatePdf`, async () => { const encryptedHeaders = await encryptHeaders({}); const generatePdfObservable = generatePdfObservableFactory(); @@ -70,9 +70,10 @@ test(`passes browserTimezone to generatePdf`, async () => { const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); const browserTimezone = 'UTC'; + const browserLocales = ['es-419', 'es', 'en']; await executeJob( 'pdfJobId', - { relativeUrls: [], browserTimezone, headers: encryptedHeaders }, + { relativeUrls: [], browserTimezone, browserLocales, headers: encryptedHeaders }, cancellationToken ); @@ -82,6 +83,7 @@ test(`passes browserTimezone to generatePdf`, async () => { undefined, [], browserTimezone, + browserLocales, expect.anything(), undefined, undefined diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts index d85207e671212..3cc6681bda074 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/execute_job/index.ts @@ -44,12 +44,13 @@ export const executeJobFactory: QueuedPdfExecutorFactory = function executeJobFa mergeMap(({ logo, conditionalHeaders }) => { const urls = getFullUrls({ server, job }); - const { browserTimezone, layout, title } = job; + const { browserTimezone, browserLocales, layout, title } = job; return generatePdfObservable( jobLogger, title, urls, browserTimezone, + browserLocales, conditionalHeaders, layout, logo diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts index 9a8db308bea79..620ca2036e8c1 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/generate_pdf.ts @@ -37,6 +37,7 @@ export function generatePdfObservableFactory( title: string, urls: string[], browserTimezone: string, + browserLocales: string[], conditionalHeaders: ConditionalHeaders, layoutParams: LayoutParams, logo?: string @@ -48,6 +49,7 @@ export function generatePdfObservableFactory( conditionalHeaders, layout, browserTimezone, + browserLocales, }).pipe( mergeMap(async urlScreenshots => { const pdfOutput = pdf.create(layout, logo); diff --git a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/types.d.ts b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/types.d.ts index 0a9dcfe986ca6..c2eb610f17e41 100644 --- a/x-pack/legacy/plugins/reporting/export_types/printable_pdf/types.d.ts +++ b/x-pack/legacy/plugins/reporting/export_types/printable_pdf/types.d.ts @@ -13,6 +13,7 @@ export interface JobParamsPDF { title: string; relativeUrls: string[]; browserTimezone: string; + browserLocales: string[]; layout: LayoutInstance; } @@ -20,6 +21,7 @@ export interface JobParamsPDF { export interface JobDocPayloadPDF extends JobDocPayload { basePath?: string; browserTimezone: string; + browserLocales: string[]; forceNow?: string; layout: LayoutParams; relativeUrls: string[]; diff --git a/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx b/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx index 77869c40d3577..966c5a5190c6f 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx +++ b/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx @@ -116,6 +116,10 @@ export class ReportInfoButton extends Component { title: 'Browser Timezone', description: get(info, 'payload.browserTimezone') || NA, }, + { + title: 'Browser Locales', + description: get(info, 'payload.browserLocales')?.join(', ') || NA, + }, ], payload: [ { diff --git a/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts b/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts index 173a4e31cfef6..5c8099b444fb9 100644 --- a/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts +++ b/x-pack/legacy/plugins/reporting/public/lib/job_queue_client.ts @@ -42,6 +42,7 @@ export interface JobInfo { title: string; forceNow: string; browserTimezone: string; + browserLocales: string[]; }; meta: { layout: string; diff --git a/x-pack/legacy/plugins/reporting/public/share_context_menu/register_reporting.tsx b/x-pack/legacy/plugins/reporting/public/share_context_menu/register_reporting.tsx index fb5e74664e6c6..66a577e3cc802 100644 --- a/x-pack/legacy/plugins/reporting/public/share_context_menu/register_reporting.tsx +++ b/x-pack/legacy/plugins/reporting/public/share_context_menu/register_reporting.tsx @@ -42,10 +42,16 @@ async function reportingProvider() { ? moment.tz.guess() : chrome.getUiSettingsClient().get('dateFormat:tz'); + const browserLocales = + chrome.getUiSettingsClient().get('format:defaultLocale') === 'detect' + ? navigator.languages?.concat(['en']) || [navigator.language, 'en'] + : [chrome.getUiSettingsClient().get('format:defaultLocale')]; + return { ...sharingData, objectType, browserTimezone, + browserLocales, relativeUrls: [relativeUrl], }; }; @@ -59,10 +65,16 @@ async function reportingProvider() { ? moment.tz.guess() : chrome.getUiSettingsClient().get('dateFormat:tz'); + const browserLocales = + chrome.getUiSettingsClient().get('format:defaultLocale') === 'detect' + ? navigator.languages?.concat(['en']) || [navigator.language, 'en'] + : [chrome.getUiSettingsClient().get('format:defaultLocale')]; + return { ...sharingData, objectType, browserTimezone, + browserLocales, relativeUrl, }; }; diff --git a/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts b/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts index 6fa46b893de8c..92071a50ee01d 100644 --- a/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts +++ b/x-pack/legacy/plugins/reporting/server/browsers/chromium/driver_factory/index.ts @@ -88,7 +88,11 @@ export class HeadlessChromiumDriverFactory { * Return an observable to objects which will drive screenshot capture for a page */ createPage( - { viewport, browserTimezone }: { viewport: BrowserConfig['viewport']; browserTimezone: string }, + { + viewport, + browserTimezone, + browserLocales, + }: { viewport: BrowserConfig['viewport']; browserTimezone: string; browserLocales: string[] }, pLogger: Logger ): Rx.Observable<{ driver: HeadlessChromiumDriver; exit$: Rx.Observable }> { return Rx.Observable.create(async (observer: InnerSubscriber) => { @@ -100,12 +104,13 @@ export class HeadlessChromiumDriverFactory { let browser: Browser; let page: Page; try { + logger.debug(`Browser locales ${browserLocales}`); browser = await puppeteerLaunch({ pipe: !this.browserConfig.inspect, userDataDir: this.userDataDir, executablePath: this.binaryPath, ignoreHTTPSErrors: true, - args: chromiumArgs, + args: [...chromiumArgs, `--lang=${browserLocales.join(',')}`], env: { TZ: browserTimezone, }, @@ -121,6 +126,7 @@ export class HeadlessChromiumDriverFactory { logger.debug(`Browser page driver created`); } catch (err) { + logger.warn(err); observer.error(new Error(`Error spawning Chromium browser: [${err}]`)); throw err; } From 6b34bac7e11f3e6a445046e142e33a7d78fe1565 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Fri, 17 Jan 2020 16:32:03 -0500 Subject: [PATCH 14/23] Remove settings change --- config/kibana.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/config/kibana.yml b/config/kibana.yml index bca9b30118694..0780841ca057e 100644 --- a/config/kibana.yml +++ b/config/kibana.yml @@ -103,8 +103,6 @@ # and all requests. #logging.verbose: false -logging.events.log: ['warning', 'error', 'fatal', 'info', 'reporting'] - # Set the interval in milliseconds to sample system and process performance # metrics. Minimum is 100ms. Defaults to 5000. #ops.interval: 5000 From ec25c3e01ec5ab7cdc636db27498f594c59634cc Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Fri, 17 Jan 2020 16:57:31 -0500 Subject: [PATCH 15/23] Fix integration test that was using custom formatting --- .../editors/currency/currency.js | 8 +++- .../apps/visualize/_embedding_chart.js | 46 +++++++++---------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js index 7421b84764434..3271e416f7ca3 100644 --- a/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js +++ b/src/legacy/ui/public/field_editor/components/field_format_editor/editors/currency/currency.js @@ -40,6 +40,10 @@ export class CurrencyFormatEditor extends DefaultNumberFormatEditor { }; } + getCurrencyName(currency) { + return `${currency.name} (${currency.code}) ${currency.symbol}`; + } + render() { const { formatParams } = this.props; const { samples } = this.state; @@ -47,7 +51,7 @@ export class CurrencyFormatEditor extends DefaultNumberFormatEditor { const currencyMatch = topCurrencies.find(cur => cur.code === currencyCode); let currencyLabel = currencyCode; if (currencyMatch) { - currencyLabel = `${currencyMatch.name} (${currencyMatch.code}) ${currencyMatch.symbol}`; + currencyLabel = this.getCurrencyName(currencyMatch); } const otherLabel = { @@ -81,7 +85,7 @@ export class CurrencyFormatEditor extends DefaultNumberFormatEditor { options={topCurrencies .map(cur => ({ value: cur.code, - label: `${cur.name} (${cur.code}) ${cur.symbol}`, + label: this.getCurrencyName(cur), })) .concat([otherLabel])} onChange={choices => { diff --git a/test/functional/apps/visualize/_embedding_chart.js b/test/functional/apps/visualize/_embedding_chart.js index 940aa3eb5d462..2773f7f4e348e 100644 --- a/test/functional/apps/visualize/_embedding_chart.js +++ b/test/functional/apps/visualize/_embedding_chart.js @@ -61,31 +61,31 @@ export default function({ getService, getPageObjects }) { '0B', '5', '2015-09-20 00:00', - '1.953KB', + '1.9KB', '5', '2015-09-20 00:00', - '3.906KB', + '3.9KB', '9', '2015-09-20 00:00', - '5.859KB', + '5.8KB', '4', '2015-09-20 00:00', - '7.813KB', + '7.8KB', '14', '2015-09-20 03:00', '0B', '32', '2015-09-20 03:00', - '1.953KB', + '1.9KB', '33', '2015-09-20 03:00', - '3.906KB', + '3.9KB', '45', '2015-09-20 03:00', - '5.859KB', + '5.8KB', '31', '2015-09-20 03:00', - '7.813KB', + '7.8KB', '48', ]); }); @@ -102,31 +102,31 @@ export default function({ getService, getPageObjects }) { '0B', '7', '2015-09-21 00:00', - '1.953KB', + '1.9KB', '9', '2015-09-21 00:00', - '3.906KB', + '3.9KB', '9', '2015-09-21 00:00', - '5.859KB', + '5.8KB', '6', '2015-09-21 00:00', - '7.813KB', + '7.8KB', '10', '2015-09-21 00:00', - '11.719KB', + '11.7KB', '1', '2015-09-21 03:00', '0B', '28', '2015-09-21 03:00', - '1.953KB', + '1.9KB', '39', '2015-09-21 03:00', - '3.906KB', + '3.9KB', '36', '2015-09-21 03:00', - '5.859KB', + '5.8KB', '43', ]); }); @@ -143,31 +143,31 @@ export default function({ getService, getPageObjects }) { '0B', '1', '03:00', - '1.953KB', + '1.9KB', '1', '03:00', - '3.906KB', + '3.9KB', '1', '03:00', - '5.859KB', + '5.8KB', '2', '03:10', '0B', '1', '03:10', - '5.859KB', + '5.8KB', '1', '03:10', - '7.813KB', + '7.8KB', '1', '03:15', '0B', '1', '03:15', - '1.953KB', + '1.9KB', '1', '03:20', - '1.953KB', + '1.9KB', '1', ]); }); From 3754f11c9be38a214e8a1040511d2a3bbb00af5f Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Fri, 17 Jan 2020 17:30:40 -0500 Subject: [PATCH 16/23] Fix test again --- test/functional/apps/visualize/_embedding_chart.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/functional/apps/visualize/_embedding_chart.js b/test/functional/apps/visualize/_embedding_chart.js index 2773f7f4e348e..e6332ff48968f 100644 --- a/test/functional/apps/visualize/_embedding_chart.js +++ b/test/functional/apps/visualize/_embedding_chart.js @@ -61,10 +61,10 @@ export default function({ getService, getPageObjects }) { '0B', '5', '2015-09-20 00:00', - '1.9KB', + '2KB', '5', '2015-09-20 00:00', - '3.9KB', + '4KB', '9', '2015-09-20 00:00', '5.8KB', @@ -76,10 +76,10 @@ export default function({ getService, getPageObjects }) { '0B', '32', '2015-09-20 03:00', - '1.9KB', + '2KB', '33', '2015-09-20 03:00', - '3.9KB', + '4KB', '45', '2015-09-20 03:00', '5.8KB', From fa1d6d9a3779ca3f7eca4f342c814551eb57fdcb Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Tue, 21 Jan 2020 13:13:07 -0500 Subject: [PATCH 17/23] Fix tests again --- .../apps/visualize/_embedding_chart.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/test/functional/apps/visualize/_embedding_chart.js b/test/functional/apps/visualize/_embedding_chart.js index e6332ff48968f..1b2dcf7e00069 100644 --- a/test/functional/apps/visualize/_embedding_chart.js +++ b/test/functional/apps/visualize/_embedding_chart.js @@ -64,10 +64,10 @@ export default function({ getService, getPageObjects }) { '2KB', '5', '2015-09-20 00:00', - '4KB', + '3.9KB', '9', '2015-09-20 00:00', - '5.8KB', + '5.9KB', '4', '2015-09-20 00:00', '7.8KB', @@ -79,10 +79,10 @@ export default function({ getService, getPageObjects }) { '2KB', '33', '2015-09-20 03:00', - '4KB', + '3.9KB', '45', '2015-09-20 03:00', - '5.8KB', + '5.9KB', '31', '2015-09-20 03:00', '7.8KB', @@ -102,13 +102,13 @@ export default function({ getService, getPageObjects }) { '0B', '7', '2015-09-21 00:00', - '1.9KB', + '2KB', '9', '2015-09-21 00:00', '3.9KB', '9', '2015-09-21 00:00', - '5.8KB', + '5.9KB', '6', '2015-09-21 00:00', '7.8KB', @@ -120,13 +120,13 @@ export default function({ getService, getPageObjects }) { '0B', '28', '2015-09-21 03:00', - '1.9KB', + '2KB', '39', '2015-09-21 03:00', '3.9KB', '36', '2015-09-21 03:00', - '5.8KB', + '5.9KB', '43', ]); }); @@ -143,19 +143,19 @@ export default function({ getService, getPageObjects }) { '0B', '1', '03:00', - '1.9KB', + '2KB', '1', '03:00', '3.9KB', '1', '03:00', - '5.8KB', + '5.9KB', '2', '03:10', '0B', '1', '03:10', - '5.8KB', + '5.9KB', '1', '03:10', '7.8KB', @@ -164,10 +164,10 @@ export default function({ getService, getPageObjects }) { '0B', '1', '03:15', - '1.9KB', + '2KB', '1', '03:20', - '1.9KB', + '2KB', '1', ]); }); From b96444a247b72183a61a6609411a054f508c80d7 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Tue, 21 Jan 2020 16:13:00 -0500 Subject: [PATCH 18/23] More test fixing --- test/functional/apps/visualize/_area_chart.js | 34 +++++++++---------- .../public/components/report_info_button.tsx | 10 ++---- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/test/functional/apps/visualize/_area_chart.js b/test/functional/apps/visualize/_area_chart.js index e52cfdf478c33..4519e0f3ae292 100644 --- a/test/functional/apps/visualize/_area_chart.js +++ b/test/functional/apps/visualize/_area_chart.js @@ -234,26 +234,26 @@ export default function({ getService, getPageObjects }) { it('does not scale top hit agg', async () => { const expectedTableData = [ - ['2015-09-20 00:00', '6', '9.035KB'], - ['2015-09-20 01:00', '9', '5.854KB'], - ['2015-09-20 02:00', '22', '4.588KB'], - ['2015-09-20 03:00', '31', '8.349KB'], - ['2015-09-20 04:00', '52', '2.637KB'], - ['2015-09-20 05:00', '119', '1.712KB'], - ['2015-09-20 06:00', '181', '9.157KB'], - ['2015-09-20 07:00', '218', '8.192KB'], - ['2015-09-20 08:00', '341', '12.384KB'], - ['2015-09-20 09:00', '440', '4.482KB'], - ['2015-09-20 10:00', '480', '9.449KB'], + ['2015-09-20 00:00', '6', '9.0KB'], + ['2015-09-20 01:00', '9', '5.9'], + ['2015-09-20 02:00', '22', '4.6'], + ['2015-09-20 03:00', '31', '8.3'], + ['2015-09-20 04:00', '52', '2.6KB'], + ['2015-09-20 05:00', '119', '1.7KB'], + ['2015-09-20 06:00', '181', '9.2KB'], + ['2015-09-20 07:00', '218', '8.2KB'], + ['2015-09-20 08:00', '341', '12.4KB'], + ['2015-09-20 09:00', '440', '4.5KB'], + ['2015-09-20 10:00', '480', '9.4KB'], ['2015-09-20 11:00', '517', '213B'], ['2015-09-20 12:00', '522', '638B'], - ['2015-09-20 13:00', '446', '7.421KB'], - ['2015-09-20 14:00', '403', '4.854KB'], - ['2015-09-20 15:00', '321', '4.132KB'], + ['2015-09-20 13:00', '446', '7.4KB'], + ['2015-09-20 14:00', '403', '4.9KB'], + ['2015-09-20 15:00', '321', '4.1KB'], ['2015-09-20 16:00', '258', '601B'], - ['2015-09-20 17:00', '172', '4.239KB'], - ['2015-09-20 18:00', '95', '6.272KB'], - ['2015-09-20 19:00', '55', '2.053KB'], + ['2015-09-20 17:00', '172', '4.2KB'], + ['2015-09-20 18:00', '95', '6.3KB'], + ['2015-09-20 19:00', '55', '2.1KB'], ]; await PageObjects.visEditor.clickBucket('Y-axis', 'metrics'); diff --git a/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx b/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx index 966c5a5190c6f..d0aca7d997c49 100644 --- a/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx +++ b/x-pack/legacy/plugins/reporting/public/components/report_info_button.tsx @@ -73,13 +73,8 @@ export class ReportInfoButton extends Component { const jobType = info.jobtype || NA; - interface JobInfo { - title: string; - description: string; - } - interface JobInfoMap { - [thing: string]: JobInfo[]; + [thing: string]: Array<{ title: string; description: string }>; } const attempts = info.attempts ? info.attempts.toString() : NA; @@ -118,7 +113,8 @@ export class ReportInfoButton extends Component { }, { title: 'Browser Locales', - description: get(info, 'payload.browserLocales')?.join(', ') || NA, + description: + get(info, 'payload.browserLocales')?.join(', ') || NA, }, ], payload: [ From fbbfaf5ebdf20969f5bbd1ef3e26b2dd1b67c434 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Tue, 21 Jan 2020 19:02:21 -0500 Subject: [PATCH 19/23] More test fixes --- test/functional/apps/visualize/_area_chart.js | 8 ++++---- .../export_types/png/server/execute_job/index.test.js | 9 ++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/test/functional/apps/visualize/_area_chart.js b/test/functional/apps/visualize/_area_chart.js index 4519e0f3ae292..804c643b0133a 100644 --- a/test/functional/apps/visualize/_area_chart.js +++ b/test/functional/apps/visualize/_area_chart.js @@ -234,10 +234,10 @@ export default function({ getService, getPageObjects }) { it('does not scale top hit agg', async () => { const expectedTableData = [ - ['2015-09-20 00:00', '6', '9.0KB'], - ['2015-09-20 01:00', '9', '5.9'], - ['2015-09-20 02:00', '22', '4.6'], - ['2015-09-20 03:00', '31', '8.3'], + ['2015-09-20 00:00', '6', '9KB'], + ['2015-09-20 01:00', '9', '5.9KB'], + ['2015-09-20 02:00', '22', '4.6KB'], + ['2015-09-20 03:00', '31', '8.3KB'], ['2015-09-20 04:00', '52', '2.6KB'], ['2015-09-20 05:00', '119', '1.7KB'], ['2015-09-20 06:00', '181', '9.2KB'], diff --git a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js index 1be65722fa668..19bd113b3f0eb 100644 --- a/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js +++ b/x-pack/legacy/plugins/reporting/export_types/png/server/execute_job/index.test.js @@ -70,9 +70,15 @@ test(`passes browserTimezone to generatePng`, async () => { const executeJob = executeJobFactory(mockServer, { browserDriverFactory: {} }); const browserTimezone = 'UTC'; + const browserLocales = ['de']; await executeJob( 'pngJobId', - { relativeUrl: '/app/kibana#/something', browserTimezone, headers: encryptedHeaders }, + { + relativeUrl: '/app/kibana#/something', + browserTimezone, + browserLocales, + headers: encryptedHeaders, + }, cancellationToken ); @@ -80,6 +86,7 @@ test(`passes browserTimezone to generatePng`, async () => { expect.any(LevelLogger), 'http://localhost:5601/sbp/app/kibana#/something', browserTimezone, + browserLocales, expect.anything(), undefined ); From aeefd35e88095c166bd665ef36a5c60e375fe0d1 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Tue, 21 Jan 2020 20:53:40 -0500 Subject: [PATCH 20/23] Hopefully final tweaks --- test/functional/apps/visualize/_data_table.js | 38 +++++++++---------- .../visualize/_data_table_nontimeindex.js | 18 ++++----- .../workpad_export/utils.test.ts | 4 +- x-pack/test/reporting/api/generation_urls.js | 8 ++-- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/test/functional/apps/visualize/_data_table.js b/test/functional/apps/visualize/_data_table.js index 0a9ff1e77a2ef..36efd2f0ecd11 100644 --- a/test/functional/apps/visualize/_data_table.js +++ b/test/functional/apps/visualize/_data_table.js @@ -77,15 +77,15 @@ export default function({ getService, getPageObjects }) { it('should show correct data', function() { const expectedChartData = [ ['0B', '2,088'], - ['1.953KB', '2,748'], - ['3.906KB', '2,707'], - ['5.859KB', '2,876'], - ['7.813KB', '2,863'], - ['9.766KB', '147'], - ['11.719KB', '148'], - ['13.672KB', '129'], - ['15.625KB', '161'], - ['17.578KB', '137'], + ['2KB', '2,748'], + ['3.9KB', '2,707'], + ['5.9KB', '2,876'], + ['7.8KB', '2,863'], + ['9.8KB', '147'], + ['11.7KB', '148'], + ['13.7KB', '129'], + ['15.6KB', '161'], + ['17.6KB', '137'], ]; return retry.try(async function() { @@ -373,16 +373,16 @@ export default function({ getService, getPageObjects }) { await PageObjects.visEditor.clickGo(); const data = await PageObjects.visChart.getTableVisContent(); expect(data).to.be.eql([ - ['jpg', '9,109', '5.469KB', 'CN', '1,718', '5.477KB'], - ['jpg', '9,109', '5.469KB', 'IN', '1,511', '5.456KB'], - ['jpg', '9,109', '5.469KB', 'US', '770', '5.371KB'], - ['jpg', '9,109', '5.469KB', 'ID', '314', '5.424KB'], - ['jpg', '9,109', '5.469KB', 'PK', '244', '5.41KB'], - ['css', '2,159', '5.566KB', 'CN', '422', '5.712KB'], - ['css', '2,159', '5.566KB', 'IN', '346', '5.754KB'], - ['css', '2,159', '5.566KB', 'US', '189', '5.333KB'], - ['css', '2,159', '5.566KB', 'ID', '68', '4.82KB'], - ['css', '2,159', '5.566KB', 'BR', '58', '5.915KB'], + ['jpg', '9,109', '5.5KB', 'CN', '1,718', '5.5KB'], + ['jpg', '9,109', '5.5KB', 'IN', '1,511', '5.5KB'], + ['jpg', '9,109', '5.5KB', 'US', '770', '5.3KB'], + ['jpg', '9,109', '5.5KB', 'ID', '314', '5.4KB'], + ['jpg', '9,109', '5.5KB', 'PK', '244', '5.4KB'], + ['css', '2,159', '5.6KB', 'CN', '422', '5.7KB'], + ['css', '2,159', '5.6KB', 'IN', '346', '5.8KB'], + ['css', '2,159', '5.6KB', 'US', '189', '5.3KB'], + ['css', '2,159', '5.6KB', 'ID', '68', '4.8KB'], + ['css', '2,159', '5.6KB', 'BR', '58', '5.9KB'], ]); }); }); diff --git a/test/functional/apps/visualize/_data_table_nontimeindex.js b/test/functional/apps/visualize/_data_table_nontimeindex.js index 3db3cd094a81b..d20587434607e 100644 --- a/test/functional/apps/visualize/_data_table_nontimeindex.js +++ b/test/functional/apps/visualize/_data_table_nontimeindex.js @@ -78,15 +78,15 @@ export default function({ getService, getPageObjects }) { it('should show correct data', function() { const expectedChartData = [ ['0B', '2,088'], - ['1.953KB', '2,748'], - ['3.906KB', '2,707'], - ['5.859KB', '2,876'], - ['7.813KB', '2,863'], - ['9.766KB', '147'], - ['11.719KB', '148'], - ['13.672KB', '129'], - ['15.625KB', '161'], - ['17.578KB', '137'], + ['2KB', '2,748'], + ['3.9KB', '2,707'], + ['5.9KB', '2,876'], + ['7.8KB', '2,863'], + ['9.8KB', '147'], + ['11.7KB', '148'], + ['13.7KB', '129'], + ['15.6KB', '161'], + ['17.6KB', '137'], ]; return retry.try(async function() { diff --git a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.test.ts b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.test.ts index ceaf82c1c07d6..63a595b3269fb 100644 --- a/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.test.ts +++ b/x-pack/legacy/plugins/canvas/public/components/workpad_header/workpad_export/utils.test.ts @@ -17,7 +17,7 @@ test('getPdfUrl returns the correct url', () => { const url = getPdfUrl(workpad, { pageCount: 2 }, addBasePath); expect(url).toMatchInlineSnapshot( - `"basepath//api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FPhoenix,layout:(dimensions:(height:0,width:0),id:preserve_layout),objectType:'canvas%20workpad',relativeUrls:!(%2Fapp%2Fcanvas%23%2Fexport%2Fworkpad%2Fpdf%2Fbase-workpad%2Fpage%2F1,%2Fapp%2Fcanvas%23%2Fexport%2Fworkpad%2Fpdf%2Fbase-workpad%2Fpage%2F2),title:'base%20workpad')"` + `"basepath//api/reporting/generate/printablePdf?jobParams=(browserLocales:!(en-US,en),browserTimezone:America%2FPhoenix,layout:(dimensions:(height:0,width:0),id:preserve_layout),objectType:'canvas%20workpad',relativeUrls:!(%2Fapp%2Fcanvas%23%2Fexport%2Fworkpad%2Fpdf%2Fbase-workpad%2Fpage%2F1,%2Fapp%2Fcanvas%23%2Fexport%2Fworkpad%2Fpdf%2Fbase-workpad%2Fpage%2F2),title:'base%20workpad')"` ); }); @@ -31,7 +31,7 @@ test('createPdf posts to create the pdf', () => { expect(args[0]).toMatchInlineSnapshot(`"basepath//api/reporting/generate/printablePdf"`); expect(args[1]).toMatchInlineSnapshot(` Object { - "jobParams": "(browserTimezone:America/Phoenix,layout:(dimensions:(height:0,width:0),id:preserve_layout),objectType:'canvas workpad',relativeUrls:!(/app/canvas#/export/workpad/pdf/base-workpad/page/1,/app/canvas#/export/workpad/pdf/base-workpad/page/2),title:'base workpad')", + "jobParams": "(browserLocales:!(en-US,en),browserTimezone:America/Phoenix,layout:(dimensions:(height:0,width:0),id:preserve_layout),objectType:'canvas workpad',relativeUrls:!(/app/canvas#/export/workpad/pdf/base-workpad/page/1,/app/canvas#/export/workpad/pdf/base-workpad/page/2),title:'base workpad')", } `); }); diff --git a/x-pack/test/reporting/api/generation_urls.js b/x-pack/test/reporting/api/generation_urls.js index b98b1a26651a1..600489100486b 100644 --- a/x-pack/test/reporting/api/generation_urls.js +++ b/x-pack/test/reporting/api/generation_urls.js @@ -5,12 +5,12 @@ */ export const PDF_PRINT_DASHBOARD_6_3 = - '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,layout:(id:print),objectType:dashboard,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fdashboard%2F2ae34a60-3dd4-11e8-b2b9-5d5dc1715159%3F_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(description:!%27!%27,filters:!!(),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),panels:!!((embeddableConfig:(),gridData:(h:15,i:!%271!%27,w:24,x:0,y:0),id:!%27145ced90-3dcb-11e8-8660-4d65aa086b3c!%27,panelIndex:!%271!%27,type:visualization,version:!%276.3.0!%27),(embeddableConfig:(),gridData:(h:15,i:!%272!%27,w:24,x:24,y:0),id:e2023110-3dcb-11e8-8660-4d65aa086b3c,panelIndex:!%272!%27,type:visualization,version:!%276.3.0!%27)),query:(language:lucene,query:!%27!%27),timeRestore:!!f,title:!%27couple%2Bpanels!%27,viewMode:view)%27),title:%27couple%20panels%27)'; + '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,browserLocales:!(de),layout:(id:print),objectType:dashboard,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fdashboard%2F2ae34a60-3dd4-11e8-b2b9-5d5dc1715159%3F_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(description:!%27!%27,filters:!!(),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),panels:!!((embeddableConfig:(),gridData:(h:15,i:!%271!%27,w:24,x:0,y:0),id:!%27145ced90-3dcb-11e8-8660-4d65aa086b3c!%27,panelIndex:!%271!%27,type:visualization,version:!%276.3.0!%27),(embeddableConfig:(),gridData:(h:15,i:!%272!%27,w:24,x:24,y:0),id:e2023110-3dcb-11e8-8660-4d65aa086b3c,panelIndex:!%272!%27,type:visualization,version:!%276.3.0!%27)),query:(language:lucene,query:!%27!%27),timeRestore:!!f,title:!%27couple%2Bpanels!%27,viewMode:view)%27),title:%27couple%20panels%27)'; export const PDF_PRESERVE_DASHBOARD_FILTER_6_3 = - '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,layout:(dimensions:(height:439,width:1362),id:preserve_layout),objectType:dashboard,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fdashboard%2F61c58ad0-3dd3-11e8-b2b9-5d5dc1715159%3F_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(description:!%27!%27,filters:!!((!%27$state!%27:(store:appState),meta:(alias:!!n,disabled:!!f,index:a0f483a0-3dc9-11e8-8660-4d65aa086b3c,key:animal,negate:!!f,params:(query:dog,type:phrase),type:phrase,value:dog),query:(match:(animal:(query:dog,type:phrase))))),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),panels:!!((embeddableConfig:(),gridData:(h:15,i:!%271!%27,w:24,x:0,y:0),id:!%2750643b60-3dd3-11e8-b2b9-5d5dc1715159!%27,panelIndex:!%271!%27,type:visualization,version:!%276.3.0!%27),(embeddableConfig:(),gridData:(h:15,i:!%272!%27,w:24,x:24,y:0),id:a16d1990-3dca-11e8-8660-4d65aa086b3c,panelIndex:!%272!%27,type:search,version:!%276.3.0!%27)),query:(language:lucene,query:!%27!%27),timeRestore:!!t,title:!%27dashboard%2Bwith%2Bfilter!%27,viewMode:view)%27),title:%27dashboard%20with%20filter%27)'; + '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,browserLocales:!(de),layout:(dimensions:(height:439,width:1362),id:preserve_layout),objectType:dashboard,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fdashboard%2F61c58ad0-3dd3-11e8-b2b9-5d5dc1715159%3F_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(description:!%27!%27,filters:!!((!%27$state!%27:(store:appState),meta:(alias:!!n,disabled:!!f,index:a0f483a0-3dc9-11e8-8660-4d65aa086b3c,key:animal,negate:!!f,params:(query:dog,type:phrase),type:phrase,value:dog),query:(match:(animal:(query:dog,type:phrase))))),fullScreenMode:!!f,options:(hidePanelTitles:!!f,useMargins:!!t),panels:!!((embeddableConfig:(),gridData:(h:15,i:!%271!%27,w:24,x:0,y:0),id:!%2750643b60-3dd3-11e8-b2b9-5d5dc1715159!%27,panelIndex:!%271!%27,type:visualization,version:!%276.3.0!%27),(embeddableConfig:(),gridData:(h:15,i:!%272!%27,w:24,x:24,y:0),id:a16d1990-3dca-11e8-8660-4d65aa086b3c,panelIndex:!%272!%27,type:search,version:!%276.3.0!%27)),query:(language:lucene,query:!%27!%27),timeRestore:!!t,title:!%27dashboard%2Bwith%2Bfilter!%27,viewMode:view)%27),title:%27dashboard%20with%20filter%27)'; export const PDF_PRESERVE_PIE_VISUALIZATION_6_3 = - '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,layout:(dimensions:(height:441,width:1002),id:preserve_layout),objectType:visualization,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fvisualize%2Fedit%2F3fe22200-3dcb-11e8-8660-4d65aa086b3c%3F_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(filters:!!(),linked:!!f,query:(language:lucene,query:!%27!%27),uiState:(),vis:(aggs:!!((enabled:!!t,id:!%271!%27,params:(),schema:metric,type:count),(enabled:!!t,id:!%272!%27,params:(field:bytes,missingBucket:!!f,missingBucketLabel:Missing,order:desc,orderBy:!%271!%27,otherBucket:!!f,otherBucketLabel:Other,size:5),schema:segment,type:terms)),params:(addLegend:!!t,addTooltip:!!t,isDonut:!!t,labels:(last_level:!!t,show:!!f,truncate:100,values:!!t),legendPosition:right,type:pie),title:!%27Rendering%2BTest:%2Bpie!%27,type:pie))%27),title:%27Rendering%20Test:%20pie%27)'; + '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,browserLocales:!(de),layout:(dimensions:(height:441,width:1002),id:preserve_layout),objectType:visualization,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fvisualize%2Fedit%2F3fe22200-3dcb-11e8-8660-4d65aa086b3c%3F_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(filters:!!(),linked:!!f,query:(language:lucene,query:!%27!%27),uiState:(),vis:(aggs:!!((enabled:!!t,id:!%271!%27,params:(),schema:metric,type:count),(enabled:!!t,id:!%272!%27,params:(field:bytes,missingBucket:!!f,missingBucketLabel:Missing,order:desc,orderBy:!%271!%27,otherBucket:!!f,otherBucketLabel:Other,size:5),schema:segment,type:terms)),params:(addLegend:!!t,addTooltip:!!t,isDonut:!!t,labels:(last_level:!!t,show:!!f,truncate:100,values:!!t),legendPosition:right,type:pie),title:!%27Rendering%2BTest:%2Bpie!%27,type:pie))%27),title:%27Rendering%20Test:%20pie%27)'; export const PDF_PRINT_PIE_VISUALIZATION_FILTER_AND_SAVED_SEARCH_6_3 = - '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,layout:(id:print),objectType:visualization,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fvisualize%2Fedit%2Fbefdb6b0-3e59-11e8-9fc3-39e49624228e%3F_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(filters:!!((!%27$state!%27:(store:appState),meta:(alias:!!n,disabled:!!f,index:a0f483a0-3dc9-11e8-8660-4d65aa086b3c,key:animal.keyword,negate:!!f,params:(query:dog,type:phrase),type:phrase,value:dog),query:(match:(animal.keyword:(query:dog,type:phrase))))),linked:!!t,query:(language:lucene,query:!%27!%27),uiState:(),vis:(aggs:!!((enabled:!!t,id:!%271!%27,params:(),schema:metric,type:count),(enabled:!!t,id:!%272!%27,params:(field:name.keyword,missingBucket:!!f,missingBucketLabel:Missing,order:desc,orderBy:!%271!%27,otherBucket:!!f,otherBucketLabel:Other,size:5),schema:segment,type:terms)),params:(addLegend:!!t,addTooltip:!!t,isDonut:!!t,labels:(last_level:!!t,show:!!f,truncate:100,values:!!t),legendPosition:right,type:pie),title:!%27Filter%2BTest:%2Banimals:%2Blinked%2Bto%2Bsearch%2Bwith%2Bfilter!%27,type:pie))%27),title:%27Filter%20Test:%20animals:%20linked%20to%20search%20with%20filter%27)'; + '/api/reporting/generate/printablePdf?jobParams=(browserTimezone:America%2FNew_York,browserLocales:!(de),layout:(id:print),objectType:visualization,relativeUrls:!(%27%2Fapp%2Fkibana%23%2Fvisualize%2Fedit%2Fbefdb6b0-3e59-11e8-9fc3-39e49624228e%3F_g%3D(refreshInterval:(display:Off,pause:!!f,value:0),time:(from:!%27Mon%2BApr%2B09%2B2018%2B17:56:08%2BGMT-0400!%27,mode:absolute,to:!%27Wed%2BApr%2B11%2B2018%2B17:56:08%2BGMT-0400!%27))%26_a%3D(filters:!!((!%27$state!%27:(store:appState),meta:(alias:!!n,disabled:!!f,index:a0f483a0-3dc9-11e8-8660-4d65aa086b3c,key:animal.keyword,negate:!!f,params:(query:dog,type:phrase),type:phrase,value:dog),query:(match:(animal.keyword:(query:dog,type:phrase))))),linked:!!t,query:(language:lucene,query:!%27!%27),uiState:(),vis:(aggs:!!((enabled:!!t,id:!%271!%27,params:(),schema:metric,type:count),(enabled:!!t,id:!%272!%27,params:(field:name.keyword,missingBucket:!!f,missingBucketLabel:Missing,order:desc,orderBy:!%271!%27,otherBucket:!!f,otherBucketLabel:Other,size:5),schema:segment,type:terms)),params:(addLegend:!!t,addTooltip:!!t,isDonut:!!t,labels:(last_level:!!t,show:!!f,truncate:100,values:!!t),legendPosition:right,type:pie),title:!%27Filter%2BTest:%2Banimals:%2Blinked%2Bto%2Bsearch%2Bwith%2Bfilter!%27,type:pie))%27),title:%27Filter%20Test:%20animals:%20linked%20to%20search%20with%20filter%27)'; export const CSV_DISCOVER_KUERY_AND_FILTER_6_3 = '/api/reporting/generate/csv?jobParams=(conflictedTypesFields:!(),fields:!(%27@timestamp%27,agent,bytes,clientip),indexPatternId:%270bf35f60-3dc9-11e8-8660-4d65aa086b3c%27,metaFields:!(_source,_id,_type,_index,_score),searchRequest:(body:(_source:(excludes:!(),includes:!(%27@timestamp%27,agent,bytes,clientip)),docvalue_fields:!(%27@timestamp%27),query:(bool:(filter:!((bool:(minimum_should_match:1,should:!((match:(clientip:%2773.14.212.83%27)))))),must:!((range:(bytes:(gte:100,lt:1000))),(range:(%27@timestamp%27:(format:epoch_millis,gte:1369165215770,lte:1526931615770)))),must_not:!(),should:!())),script_fields:(),sort:!((%27@timestamp%27:(order:desc,unmapped_type:boolean))),stored_fields:!(%27@timestamp%27,agent,bytes,clientip),version:!t),index:%27logstash-*%27),title:%27Bytes%20and%20kuery%20in%20saved%20search%20with%20filter%27,type:search)'; From f7bb12e2adca89ed63f43bf59d6e035a03f1d334 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Tue, 21 Jan 2020 23:47:25 -0500 Subject: [PATCH 21/23] More fixes --- .../data/common/field_formats/converters/date_server.ts | 2 +- test/functional/apps/visualize/_data_table.js | 4 ++-- .../export_types/csv/server/lib/field_format_map.test.ts | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/plugins/data/common/field_formats/converters/date_server.ts b/src/plugins/data/common/field_formats/converters/date_server.ts index 0bbbef35a1ad7..ca2a35de8aaf5 100644 --- a/src/plugins/data/common/field_formats/converters/date_server.ts +++ b/src/plugins/data/common/field_formats/converters/date_server.ts @@ -35,7 +35,7 @@ export class DateFormat extends FieldFormat { private memoizedPattern: string = ''; private timeZone: string = ''; - constructor(params: IFieldFormatMetaParams, getConfig: Function) { + constructor(params: IFieldFormatMetaParams, getConfig?: Function) { super(params, getConfig); this.memoizedConverter = memoize((val: any) => { diff --git a/test/functional/apps/visualize/_data_table.js b/test/functional/apps/visualize/_data_table.js index 36efd2f0ecd11..129957f4131b4 100644 --- a/test/functional/apps/visualize/_data_table.js +++ b/test/functional/apps/visualize/_data_table.js @@ -145,9 +145,9 @@ export default function({ getService, getPageObjects }) { const data = await PageObjects.visChart.getTableVisData(); expect(data.trim().split('\n')).to.be.eql([ '≥ 0 and < 1000', - '344.094B', + '344.1B', '≥ 1000 and < 2000', - '1.697KB', + '1.7KB', ]); }); diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.test.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.test.ts index 558269cc58e7b..de57effd0bb96 100644 --- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.test.ts +++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/field_format_map.test.ts @@ -13,10 +13,10 @@ import { DefaultNumberFormat, StaticLookupFormat, // eslint-disable-next-line @kbn/eslint/no-restricted-paths -} from '../../../../../../../../../src/plugins/data/server'; +} from '../../../../../../../../src/plugins/data/server'; import { fieldFormatMapFactory } from './field_format_map'; -type ConfigValue = { number: { id: string; params: {} } } | string; +type ConfigValue = { [key: string]: { id: string; params: {} } } | string; describe('field format map', function() { const indexPatternSavedObject = { From ecd219858c7e091b66b35e71a9494282cbb8ada1 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Wed, 22 Jan 2020 10:10:54 -0500 Subject: [PATCH 22/23] Fix tests --- test/functional/apps/visualize/_data_table.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/functional/apps/visualize/_data_table.js b/test/functional/apps/visualize/_data_table.js index 129957f4131b4..ab2dc0eee0e39 100644 --- a/test/functional/apps/visualize/_data_table.js +++ b/test/functional/apps/visualize/_data_table.js @@ -376,8 +376,8 @@ export default function({ getService, getPageObjects }) { ['jpg', '9,109', '5.5KB', 'CN', '1,718', '5.5KB'], ['jpg', '9,109', '5.5KB', 'IN', '1,511', '5.5KB'], ['jpg', '9,109', '5.5KB', 'US', '770', '5.3KB'], - ['jpg', '9,109', '5.5KB', 'ID', '314', '5.4KB'], - ['jpg', '9,109', '5.5KB', 'PK', '244', '5.4KB'], + ['jpg', '9,109', '5.5KB', 'ID', '314', '5.3KB'], + ['jpg', '9,109', '5.5KB', 'PK', '244', '5.3KB'], ['css', '2,159', '5.6KB', 'CN', '422', '5.7KB'], ['css', '2,159', '5.6KB', 'IN', '346', '5.8KB'], ['css', '2,159', '5.6KB', 'US', '189', '5.3KB'], From ff4690436059a8fa200483ea32f054c967a302d8 Mon Sep 17 00:00:00 2001 From: Wylie Conlon Date: Wed, 22 Jan 2020 13:15:01 -0500 Subject: [PATCH 23/23] Fix tests --- test/functional/apps/visualize/_data_table.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/functional/apps/visualize/_data_table.js b/test/functional/apps/visualize/_data_table.js index ab2dc0eee0e39..b54e2901680e2 100644 --- a/test/functional/apps/visualize/_data_table.js +++ b/test/functional/apps/visualize/_data_table.js @@ -375,9 +375,9 @@ export default function({ getService, getPageObjects }) { expect(data).to.be.eql([ ['jpg', '9,109', '5.5KB', 'CN', '1,718', '5.5KB'], ['jpg', '9,109', '5.5KB', 'IN', '1,511', '5.5KB'], - ['jpg', '9,109', '5.5KB', 'US', '770', '5.3KB'], - ['jpg', '9,109', '5.5KB', 'ID', '314', '5.3KB'], - ['jpg', '9,109', '5.5KB', 'PK', '244', '5.3KB'], + ['jpg', '9,109', '5.5KB', 'US', '770', '5.4KB'], + ['jpg', '9,109', '5.5KB', 'ID', '314', '5.4KB'], + ['jpg', '9,109', '5.5KB', 'PK', '244', '5.4KB'], ['css', '2,159', '5.6KB', 'CN', '422', '5.7KB'], ['css', '2,159', '5.6KB', 'IN', '346', '5.8KB'], ['css', '2,159', '5.6KB', 'US', '189', '5.3KB'],