From 8dff16a0a6f0cd09e2ed04794609fa5b970d7ce8 Mon Sep 17 00:00:00 2001
From: Matthew Kime
Date: Thu, 18 Feb 2021 14:24:38 -0600
Subject: [PATCH] Index pattern field editor (#88995) (#91896)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Index pattern field editor
Co-authored-by: Sébastien Loix
---
.i18nrc.json | 1 +
docs/developer/plugin-list.asciidoc | 4 +
.../src/painless/diagnostics_adapter.ts | 13 +
packages/kbn-monaco/src/painless/index.ts | 10 +-
packages/kbn-monaco/src/painless/language.ts | 11 +-
packages/kbn-optimizer/limits.yml | 5 +-
.../public/doc_links/doc_links_service.ts | 1 +
.../data/common/index_patterns/constants.ts | 9 +
.../index_pattern_field.test.ts.snap | 4 +-
.../fields/index_pattern_field.test.ts | 4 +-
.../fields/index_pattern_field.ts | 8 +-
.../data/common/index_patterns/index.ts | 1 +
.../__snapshots__/index_pattern.test.ts.snap | 6 +-
.../index_patterns/index_pattern.ts | 3 +-
.../index_patterns/index_patterns.ts | 7 +-
.../data/common/index_patterns/types.ts | 7 +-
.../forms/hook_form_lib/hooks/use_form.ts | 3 +-
.../index_pattern_field_editor/README.md | 69 +++++
.../index_pattern_field_editor/jest.config.js | 13 +
.../index_pattern_field_editor/kibana.json | 9 +
.../public/assets/icons/LICENSE.txt | 20 ++
.../public/assets/icons/cv.png | Bin 0 -> 802 bytes
.../public/assets/icons/de.png | Bin 0 -> 124 bytes
.../public/assets/icons/go.png | Bin 0 -> 1938 bytes
.../public/assets/icons/ne.png | Bin 0 -> 336 bytes
.../public/assets/icons/ni.png | Bin 0 -> 919 bytes
.../public/assets/icons/stop.png | Bin 0 -> 1912 bytes
.../public/assets/icons/us.png | Bin 0 -> 1074 bytes
.../delete_field_provider.tsx | 129 ++++++++
.../get_delete_provider.tsx | 62 ++++
.../components/delete_field_provider/index.ts | 9 +
.../advanced_parameters_section.tsx | 44 +++
.../components/field_editor/constants.ts | 37 +++
.../field_editor/field_editor.test.tsx | 212 +++++++++++++
.../components/field_editor/field_editor.tsx | 286 ++++++++++++++++++
.../form_fields/custom_label_field.tsx | 15 +
.../field_editor/form_fields/format_field.tsx | 82 +++++
.../field_editor/form_fields/index.ts | 17 ++
.../form_fields/popularity_field.tsx | 21 ++
.../field_editor/form_fields/script_field.tsx | 227 ++++++++++++++
.../field_editor/form_fields/type_field.tsx | 65 ++++
.../components/field_editor/form_row.tsx | 86 ++++++
.../components/field_editor/form_schema.ts | 119 ++++++++
.../public/components/field_editor/index.ts | 9 +
.../public/components/field_editor/lib.ts | 60 ++++
.../field_editor/shadowing_field_warning.tsx | 32 ++
.../field_editor_flyout_content.test.ts | 198 ++++++++++++
.../field_editor_flyout_content.tsx | 253 ++++++++++++++++
.../field_editor_flyout_content_container.tsx | 205 +++++++++++++
.../__snapshots__/format_editor.test.tsx.snap | 25 ++
.../bytes/__snapshots__/bytes.test.tsx.snap | 4 +-
.../editors/bytes/bytes.test.tsx | 0
.../editors/bytes/bytes.ts | 0
.../editors/bytes/index.ts | 0
.../color/__snapshots__/color.test.tsx.snap | 30 +-
.../editors/color/color.test.tsx | 2 +-
.../editors/color/color.tsx | 20 +-
.../editors/color/index.ts | 0
.../date/__snapshots__/date.test.tsx.snap | 4 +-
.../editors/date/date.test.tsx | 0
.../field_format_editor/editors/date/date.tsx | 4 +-
.../field_format_editor/editors/date/index.ts | 0
.../__snapshots__/date_nanos.test.tsx.snap | 4 +-
.../editors/date_nanos/date_nanos.test.tsx | 2 +-
.../editors/date_nanos/date_nanos.tsx | 4 +-
.../editors/date_nanos/index.ts | 0
.../__snapshots__/default.test.tsx.snap | 0
.../editors/default/default.test.tsx | 0
.../editors/default/default.tsx | 8 +-
.../editors/default/index.ts | 0
.../__snapshots__/duration.test.tsx.snap | 12 +-
.../editors/duration/duration.test.tsx | 0
.../editors/duration/duration.tsx | 10 +-
.../editors/duration/index.tsx | 0
.../field_format_editor/editors/index.ts | 0
.../number/__snapshots__/number.test.tsx.snap | 4 +-
.../editors/number/index.ts | 0
.../editors/number/number.test.tsx | 0
.../editors/number/number.tsx | 4 +-
.../__snapshots__/percent.test.tsx.snap | 4 +-
.../editors/percent/index.ts | 0
.../editors/percent/percent.test.tsx | 2 +-
.../editors/percent/percent.tsx | 0
.../__snapshots__/static_lookup.test.tsx.snap | 16 +-
.../editors/static_lookup/index.ts | 0
.../static_lookup/static_lookup.test.tsx | 2 +-
.../editors/static_lookup/static_lookup.tsx | 16 +-
.../string/__snapshots__/string.test.tsx.snap | 2 +-
.../editors/string/index.ts | 0
.../editors/string/string.test.tsx | 0
.../editors/string/string.tsx | 2 +-
.../__snapshots__/truncate.test.tsx.snap | 2 +-
.../editors/truncate/index.ts | 0
.../editors/truncate/sample.ts | 0
.../editors/truncate/truncate.test.tsx | 0
.../editors/truncate/truncate.tsx | 2 +-
.../url/__snapshots__/url.test.tsx.snap | 36 ++-
.../field_format_editor/editors/url/index.ts | 0
.../editors/url/url.test.tsx | 36 +--
.../field_format_editor/editors/url/url.tsx | 79 ++---
.../field_format_editor.tsx | 186 ++++++++++++
.../format_editor.test.tsx | 63 ++++
.../field_format_editor/format_editor.tsx | 67 ++++
.../components/field_format_editor/index.ts | 10 +
.../__snapshots__/samples.test.tsx.snap | 2 +-
.../field_format_editor/samples/index.ts | 0
.../field_format_editor/samples/samples.scss | 0
.../samples/samples.test.tsx | 0
.../field_format_editor/samples/samples.tsx | 8 +-
.../components/field_format_editor/types.ts | 14 +
.../public/components/index.ts | 20 ++
.../public/constants.ts | 9 +
.../public/index.ts | 32 ++
.../public/lib/documentation.ts | 21 ++
.../public/lib/index.ts | 13 +
.../lib/runtime_field_validation.test.ts | 165 ++++++++++
.../public/lib/runtime_field_validation.ts | 116 +++++++
.../public/lib/serialization.ts | 28 ++
.../public/mocks.ts | 46 +++
.../public/open_editor.tsx | 119 ++++++++
.../public/plugin.test.tsx | 114 +++++++
.../public/plugin.ts | 60 ++++
.../field_format_editors.ts | 2 +-
.../service/field_format_editors/index.ts | 0
.../public/service/format_editor_service.ts | 72 +++++
.../public/service/index.ts | 9 +
.../public/shared_imports.ts | 36 +++
.../public/test_utils/helpers.ts | 27 ++
.../public/test_utils/index.ts | 13 +
.../public/test_utils/mocks.ts | 24 ++
.../public/test_utils/setup_environment.tsx | 80 +++++
.../public/test_utils/test_utils.ts | 11 +
.../public/types.ts | 63 ++++
.../index_pattern_field_editor/tsconfig.json | 20 ++
.../index_pattern_management/kibana.json | 2 +-
.../create_index_pattern_wizard.test.tsx.snap | 10 +
.../edit_index_pattern/edit_index_pattern.tsx | 19 +-
.../indexed_fields_table.test.tsx.snap | 35 ++-
.../table/__snapshots__/table.test.tsx.snap | 32 +-
.../components/table/table.test.tsx | 65 ++--
.../components/table/table.tsx | 41 ++-
.../indexed_fields_table.test.tsx | 14 +-
.../indexed_fields_table.tsx | 9 +-
.../indexed_fields_table/types.ts | 2 +
.../edit_index_pattern/tabs/tabs.tsx | 111 +++++--
.../label_template_flyout.test.tsx.snap | 109 -------
.../url_template_flyout.test.tsx.snap | 114 -------
.../url/label_template_flyout.test.tsx | 24 --
.../editors/url/label_template_flyout.tsx | 142 ---------
.../editors/url/url_template_flyout.test.tsx | 24 --
.../editors/url/url_template_flyout.tsx | 112 -------
.../field_format_editor.test.tsx | 2 +-
.../field_format_editor.tsx | 2 +-
.../components/field_format_editor/index.ts | 1 -
.../components/field_editor/field_editor.tsx | 4 +-
.../index_pattern_management/public/index.ts | 2 -
.../mount_management_section.tsx | 4 +-
.../index_pattern_management/public/mocks.ts | 23 +-
.../index_pattern_management/public/plugin.ts | 2 +
.../index_pattern_management_service.ts | 36 ---
.../index_pattern_management/public/types.ts | 3 +
.../index_pattern_management/tsconfig.json | 2 +
.../management/_handle_version_conflict.js | 6 +
.../apps/management/_index_pattern_filter.js | 8 +-
.../management/_index_pattern_popularity.js | 8 +-
.../management/_index_pattern_results_sort.js | 32 +-
test/functional/apps/visualize/_tag_cloud.ts | 6 +
test/functional/page_objects/settings_page.ts | 2 +-
tsconfig.json | 1 +
.../runtime_field_form/runtime_field_form.tsx | 2 +-
.../translations/translations/ja-JP.json | 110 +++----
.../translations/translations/zh-CN.json | 110 +++----
.../apps/rollup_job/hybrid_index_pattern.js | 2 +-
173 files changed, 4359 insertions(+), 1036 deletions(-)
create mode 100644 src/plugins/data/common/index_patterns/constants.ts
create mode 100644 src/plugins/index_pattern_field_editor/README.md
create mode 100644 src/plugins/index_pattern_field_editor/jest.config.js
create mode 100644 src/plugins/index_pattern_field_editor/kibana.json
create mode 100644 src/plugins/index_pattern_field_editor/public/assets/icons/LICENSE.txt
create mode 100644 src/plugins/index_pattern_field_editor/public/assets/icons/cv.png
create mode 100644 src/plugins/index_pattern_field_editor/public/assets/icons/de.png
create mode 100644 src/plugins/index_pattern_field_editor/public/assets/icons/go.png
create mode 100644 src/plugins/index_pattern_field_editor/public/assets/icons/ne.png
create mode 100644 src/plugins/index_pattern_field_editor/public/assets/icons/ni.png
create mode 100644 src/plugins/index_pattern_field_editor/public/assets/icons/stop.png
create mode 100644 src/plugins/index_pattern_field_editor/public/assets/icons/us.png
create mode 100644 src/plugins/index_pattern_field_editor/public/components/delete_field_provider/delete_field_provider.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/delete_field_provider/get_delete_provider.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/delete_field_provider/index.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/advanced_parameters_section.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/constants.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.test.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/custom_label_field.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/format_field.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/index.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/popularity_field.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/script_field.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/type_field.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/form_row.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/form_schema.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/index.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/lib.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor/shadowing_field_warning.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.test.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_format_editor/__snapshots__/format_editor.test.tsx.snap
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap (92%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/bytes/bytes.test.tsx (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/bytes/bytes.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/bytes/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap (87%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/color/color.test.tsx (96%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/color/color.tsx (89%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/color/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/date/__snapshots__/date.test.tsx.snap (93%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/date/date.test.tsx (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/date/date.tsx (94%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/date/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.tsx.snap (93%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx (95%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/date_nanos/date_nanos.tsx (94%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/date_nanos/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/default/__snapshots__/default.test.tsx.snap (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/default/default.test.tsx (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/default/default.tsx (92%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/default/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap (93%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/duration/duration.test.tsx (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/duration/duration.tsx (93%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/duration/index.tsx (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap (92%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/number/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/number/number.test.tsx (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/number/number.tsx (94%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap (92%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/percent/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/percent/percent.test.tsx (95%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/percent/percent.tsx (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap (88%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/static_lookup/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx (96%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/static_lookup/static_lookup.tsx (88%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/string/__snapshots__/string.test.tsx.snap (96%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/string/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/string/string.test.tsx (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/string/string.tsx (96%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap (96%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/truncate/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/truncate/sample.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/truncate/truncate.test.tsx (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/truncate/truncate.tsx (96%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap (92%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/url/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/url/url.test.tsx (75%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/editors/url/url.tsx (74%)
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.test.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_format_editor/format_editor.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_format_editor/index.ts
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/samples/__snapshots__/samples.test.tsx.snap (96%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/samples/index.ts (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/samples/samples.scss (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/samples/samples.test.tsx (100%)
rename src/plugins/{index_pattern_management/public/components/field_editor => index_pattern_field_editor/public}/components/field_format_editor/samples/samples.tsx (87%)
create mode 100644 src/plugins/index_pattern_field_editor/public/components/field_format_editor/types.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/components/index.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/constants.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/index.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/lib/documentation.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/lib/index.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.test.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/lib/runtime_field_validation.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/lib/serialization.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/mocks.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/open_editor.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/plugin.test.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/plugin.ts
rename src/plugins/{index_pattern_management => index_pattern_field_editor}/public/service/field_format_editors/field_format_editors.ts (89%)
rename src/plugins/{index_pattern_management => index_pattern_field_editor}/public/service/field_format_editors/index.ts (100%)
create mode 100644 src/plugins/index_pattern_field_editor/public/service/format_editor_service.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/service/index.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/shared_imports.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/test_utils/helpers.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/test_utils/index.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/test_utils/mocks.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/test_utils/setup_environment.tsx
create mode 100644 src/plugins/index_pattern_field_editor/public/test_utils/test_utils.ts
create mode 100644 src/plugins/index_pattern_field_editor/public/types.ts
create mode 100644 src/plugins/index_pattern_field_editor/tsconfig.json
delete mode 100644 src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap
delete mode 100644 src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap
delete mode 100644 src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.test.tsx
delete mode 100644 src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.tsx
delete mode 100644 src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.test.tsx
delete mode 100644 src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.tsx
diff --git a/.i18nrc.json b/.i18nrc.json
index 0cdcae08e54e0a..efbb5ecc0194e9 100644
--- a/.i18nrc.json
+++ b/.i18nrc.json
@@ -29,6 +29,7 @@
"maps_legacy": "src/plugins/maps_legacy",
"monaco": "packages/kbn-monaco/src",
"presentationUtil": "src/plugins/presentation_util",
+ "indexPatternFieldEditor": "src/plugins/index_pattern_field_editor",
"indexPatternManagement": "src/plugins/index_pattern_management",
"advancedSettings": "src/plugins/advanced_settings",
"kibana_legacy": "src/plugins/kibana_legacy",
diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc
index fc565491b4f63f..eecc530332b6aa 100644
--- a/docs/developer/plugin-list.asciidoc
+++ b/docs/developer/plugin-list.asciidoc
@@ -91,6 +91,10 @@ for use in their own application.
|Moves the legacy ui/registry/feature_catalogue module for registering "features" that should be shown in the home page's feature catalogue to a service within a "home" plugin. The feature catalogue refered to here should not be confused with the "feature" plugin for registering features used to derive UI capabilities for feature controls.
+|{kib-repo}blob/{branch}/src/plugins/index_pattern_field_editor/README.md[indexPatternFieldEditor]
+|The reusable field editor across Kibana!
+
+
|{kib-repo}blob/{branch}/src/plugins/index_pattern_management[indexPatternManagement]
|WARNING: Missing README.
diff --git a/packages/kbn-monaco/src/painless/diagnostics_adapter.ts b/packages/kbn-monaco/src/painless/diagnostics_adapter.ts
index fd08cc9c6b57a2..dc5f1ed95205cf 100644
--- a/packages/kbn-monaco/src/painless/diagnostics_adapter.ts
+++ b/packages/kbn-monaco/src/painless/diagnostics_adapter.ts
@@ -18,7 +18,12 @@ const toDiagnostics = (error: PainlessError): monaco.editor.IMarkerData => {
};
};
+export interface SyntaxErrors {
+ [modelId: string]: PainlessError[];
+}
export class DiagnosticsAdapter {
+ private errors: SyntaxErrors = {};
+
constructor(private worker: WorkerAccessor) {
const onModelAdd = (model: monaco.editor.IModel): void => {
let handle: any;
@@ -55,8 +60,16 @@ export class DiagnosticsAdapter {
if (errorMarkers) {
const model = monaco.editor.getModel(resource);
+ this.errors = {
+ ...this.errors,
+ [model!.id]: errorMarkers,
+ };
// Set the error markers and underline them with "Error" severity
monaco.editor.setModelMarkers(model!, ID, errorMarkers.map(toDiagnostics));
}
}
+
+ public getSyntaxErrors() {
+ return this.errors;
+ }
}
diff --git a/packages/kbn-monaco/src/painless/index.ts b/packages/kbn-monaco/src/painless/index.ts
index 5845186776b486..68582097564308 100644
--- a/packages/kbn-monaco/src/painless/index.ts
+++ b/packages/kbn-monaco/src/painless/index.ts
@@ -8,8 +8,14 @@
import { ID } from './constants';
import { lexerRules, languageConfiguration } from './lexer_rules';
-import { getSuggestionProvider } from './language';
+import { getSuggestionProvider, getSyntaxErrors } from './language';
-export const PainlessLang = { ID, getSuggestionProvider, lexerRules, languageConfiguration };
+export const PainlessLang = {
+ ID,
+ getSuggestionProvider,
+ lexerRules,
+ languageConfiguration,
+ getSyntaxErrors,
+};
export { PainlessContext, PainlessAutocompleteField } from './types';
diff --git a/packages/kbn-monaco/src/painless/language.ts b/packages/kbn-monaco/src/painless/language.ts
index 74199561bc3948..3cb26d970fc7d0 100644
--- a/packages/kbn-monaco/src/painless/language.ts
+++ b/packages/kbn-monaco/src/painless/language.ts
@@ -13,7 +13,7 @@ import { ID } from './constants';
import { PainlessContext, PainlessAutocompleteField } from './types';
import { PainlessWorker } from './worker';
import { PainlessCompletionAdapter } from './completion_adapter';
-import { DiagnosticsAdapter } from './diagnostics_adapter';
+import { DiagnosticsAdapter, SyntaxErrors } from './diagnostics_adapter';
const workerProxyService = new WorkerProxyService();
const editorStateService = new EditorStateService();
@@ -33,8 +33,15 @@ export const getSuggestionProvider = (
return new PainlessCompletionAdapter(worker, editorStateService);
};
+let diagnosticsAdapter: DiagnosticsAdapter;
+
+// Returns syntax errors for all models by model id
+export const getSyntaxErrors = (): SyntaxErrors => {
+ return diagnosticsAdapter.getSyntaxErrors();
+};
+
monaco.languages.onLanguage(ID, async () => {
workerProxyService.setup();
- new DiagnosticsAdapter(worker);
+ diagnosticsAdapter = new DiagnosticsAdapter(worker);
});
diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml
index 657aabca1e86d0..f1c95931a5f851 100644
--- a/packages/kbn-optimizer/limits.yml
+++ b/packages/kbn-optimizer/limits.yml
@@ -32,8 +32,8 @@ pageLoadAssetSize:
grokdebugger: 26779
home: 41661
indexLifecycleManagement: 107090
- indexManagement: 662506
- indexPatternManagement: 154366
+ indexManagement: 140608
+ indexPatternManagement: 28222
infra: 204800
fleet: 415829
ingestPipelines: 58003
@@ -103,6 +103,7 @@ pageLoadAssetSize:
stackAlerts: 29684
presentationUtil: 28545
spacesOss: 18817
+ indexPatternFieldEditor: 90489
osquery: 107090
fileUpload: 25664
banners: 17946
diff --git a/src/core/public/doc_links/doc_links_service.ts b/src/core/public/doc_links/doc_links_service.ts
index 55f203b12bc290..69892156bbdf21 100644
--- a/src/core/public/doc_links/doc_links_service.ts
+++ b/src/core/public/doc_links/doc_links_service.ts
@@ -121,6 +121,7 @@ export class DocLinksService {
indexPatterns: {
loadingData: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/tutorial-load-dataset.html`,
introduction: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/index-patterns.html`,
+ fieldFormattersString: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/field-formatters-string.html`,
},
addData: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/connect-to-elasticsearch.html`,
kibana: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/index.html`,
diff --git a/src/plugins/data/common/index_patterns/constants.ts b/src/plugins/data/common/index_patterns/constants.ts
new file mode 100644
index 00000000000000..88309447a8a29c
--- /dev/null
+++ b/src/plugins/data/common/index_patterns/constants.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+export const RUNTIME_FIELD_TYPES = ['keyword', 'long', 'double', 'date', 'ip', 'boolean'] as const;
diff --git a/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap b/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap
index 4ef61ec0f25571..6b1d01e5ba1429 100644
--- a/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap
+++ b/src/plugins/data/common/index_patterns/fields/__snapshots__/index_pattern_field.test.ts.snap
@@ -14,7 +14,7 @@ Object {
},
"count": 1,
"esTypes": Array [
- "text",
+ "keyword",
],
"lang": "lang",
"name": "name",
@@ -49,7 +49,7 @@ Object {
"count": 1,
"customLabel": undefined,
"esTypes": Array [
- "text",
+ "keyword",
],
"format": Object {
"id": "number",
diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts
index 85e20c5a32662e..48342a9e02a2bd 100644
--- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts
+++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.test.ts
@@ -26,7 +26,7 @@ describe('Field', function () {
script: 'script',
lang: 'lang',
count: 1,
- esTypes: ['text'],
+ esTypes: ['text'], // note, this will get replaced by the runtime field type
aggregatable: true,
filterable: true,
searchable: true,
@@ -71,7 +71,7 @@ describe('Field', function () {
});
it('sets type field when _source field', () => {
- const field = getField({ name: '_source' });
+ const field = getField({ name: '_source', runtimeField: undefined });
expect(field.type).toEqual('_source');
});
diff --git a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts
index 4a6ee1149d4c6d..e5f4945c9ad6d4 100644
--- a/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts
+++ b/src/plugins/data/common/index_patterns/fields/index_pattern_field.ts
@@ -7,7 +7,7 @@
*/
import type { RuntimeField } from '../types';
-import { KbnFieldType, getKbnFieldType } from '../../kbn_field_types';
+import { KbnFieldType, getKbnFieldType, castEsToKbnFieldTypeName } from '../../kbn_field_types';
import { KBN_FIELD_TYPES } from '../../kbn_field_types/types';
import type { IFieldType } from './types';
import { FieldSpec, IndexPattern } from '../..';
@@ -99,11 +99,13 @@ export class IndexPatternField implements IFieldType {
}
public get type() {
- return this.spec.type;
+ return this.runtimeField?.type
+ ? castEsToKbnFieldTypeName(this.runtimeField?.type)
+ : this.spec.type;
}
public get esTypes() {
- return this.spec.esTypes;
+ return this.runtimeField?.type ? [this.runtimeField?.type] : this.spec.esTypes;
}
public get scripted() {
diff --git a/src/plugins/data/common/index_patterns/index.ts b/src/plugins/data/common/index_patterns/index.ts
index 1cea49bcbecd30..7f6249caceb52e 100644
--- a/src/plugins/data/common/index_patterns/index.ts
+++ b/src/plugins/data/common/index_patterns/index.ts
@@ -12,3 +12,4 @@ export { IndexPatternsService, IndexPatternsContract } from './index_patterns';
export type { IndexPattern } from './index_patterns';
export * from './errors';
export * from './expressions';
+export * from './constants';
diff --git a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap
index 4aadddfad3b970..7757e2fdd4584d 100644
--- a/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap
+++ b/src/plugins/data/common/index_patterns/index_patterns/__snapshots__/index_pattern.test.ts.snap
@@ -565,7 +565,9 @@ Object {
"conflictDescriptions": undefined,
"count": 0,
"customLabel": undefined,
- "esTypes": undefined,
+ "esTypes": Array [
+ "keyword",
+ ],
"format": Object {
"id": "number",
"params": Object {
@@ -587,7 +589,7 @@ Object {
"searchable": false,
"shortDotsEnable": false,
"subType": undefined,
- "type": undefined,
+ "type": "string",
},
"script date": Object {
"aggregatable": true,
diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts
index 378a371dbeb3af..16d6338cc5a13e 100644
--- a/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts
+++ b/src/plugins/data/common/index_patterns/index_patterns/index_pattern.ts
@@ -412,6 +412,8 @@ export class IndexPattern implements IIndexPattern {
existingField.runtimeField = undefined;
} else {
// runtimeField only
+ this.setFieldCustomLabel(name, null);
+ this.deleteFieldFormat(name);
this.fields.remove(existingField);
}
}
@@ -446,7 +448,6 @@ export class IndexPattern implements IIndexPattern {
if (fieldObject) {
fieldObject.customLabel = newCustomLabel;
- return;
}
this.setFieldAttrs(fieldName, 'customLabel', newCustomLabel);
diff --git a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts
index 2f3884a6481b48..733f24b88d8aa7 100644
--- a/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts
+++ b/src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts
@@ -419,11 +419,10 @@ export class IndexPatternsService {
},
spec.fieldAttrs
);
- // APPLY RUNTIME FIELDS
+ // CREATE RUNTIME FIELDS
for (const [key, value] of Object.entries(runtimeFieldMap || {})) {
- if (spec.fields[key]) {
- spec.fields[key].runtimeField = value;
- } else {
+ // do not create runtime field if mapped field exists
+ if (!spec.fields[key]) {
spec.fields[key] = {
name: key,
type: castEsToKbnFieldTypeName(value.type),
diff --git a/src/plugins/data/common/index_patterns/types.ts b/src/plugins/data/common/index_patterns/types.ts
index 444d91a537d3fa..b9fc2cb2a3862b 100644
--- a/src/plugins/data/common/index_patterns/types.ts
+++ b/src/plugins/data/common/index_patterns/types.ts
@@ -10,15 +10,16 @@ import { ToastInputFields, ErrorToastOptions } from 'src/core/public/notificatio
// eslint-disable-next-line
import type { SavedObject } from 'src/core/server';
import { IFieldType } from './fields';
+import { RUNTIME_FIELD_TYPES } from './constants';
import { SerializedFieldFormat } from '../../../expressions/common';
import { KBN_FIELD_TYPES, IndexPatternField, FieldFormat } from '..';
export type FieldFormatMap = Record;
-const RUNTIME_FIELD_TYPES = ['keyword', 'long', 'double', 'date', 'ip', 'boolean'] as const;
-type RuntimeType = typeof RUNTIME_FIELD_TYPES[number];
+
+export type RuntimeType = typeof RUNTIME_FIELD_TYPES[number];
export interface RuntimeField {
type: RuntimeType;
- script: {
+ script?: {
source: string;
};
}
diff --git a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts
index 40f44f3671f312..181bd9959c1bbd 100644
--- a/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts
+++ b/src/plugins/es_ui_shared/static/forms/hook_form_lib/hooks/use_form.ts
@@ -211,11 +211,12 @@ export function useForm(
// ----------------------------------
const addField: FormHook['__addField'] = useCallback(
(field) => {
+ const fieldExists = fieldsRefs.current[field.path] !== undefined;
fieldsRefs.current[field.path] = field;
updateFormDataAt(field.path, field.value);
- if (!field.isValidated) {
+ if (!fieldExists && !field.isValidated) {
setIsValid(undefined);
// When we submit the form (and set "isSubmitted" to "true"), we validate **all fields**.
diff --git a/src/plugins/index_pattern_field_editor/README.md b/src/plugins/index_pattern_field_editor/README.md
new file mode 100644
index 00000000000000..10949954cef38f
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/README.md
@@ -0,0 +1,69 @@
+# Index pattern field editor
+
+The reusable field editor across Kibana!
+
+This editor can be used to
+
+* create or edit a runtime field inside an index pattern.
+* edit concrete (mapped) fields. In this case certain functionalities will be disabled like the possibility to change the field _type_ or to set the field _value_.
+
+## How to use
+
+You first need to add in your kibana.json the "`indexPatternFieldEditor`" plugin as a required dependency of your plugin.
+
+You will then receive in the start contract of the indexPatternFieldEditor plugin the following API:
+
+### `openEditor(options: OpenFieldEditorOptions): CloseEditor`
+
+Use this method to open the index pattern field editor to either create (runtime) or edit (concrete | runtime) a field.
+
+#### `options`
+
+`ctx: FieldEditorContext` (**required**)
+
+This is the only required option. You need to provide the context in which the editor is being consumed. This object has the following properties:
+
+- `indexPattern: IndexPattern`: the index pattern you want to create/edit the field into.
+
+`onSave(field: IndexPatternField): void` (optional)
+
+You can provide an optional `onSave` handler to be notified when the field has being created/updated. This handler is called after the field has been persisted to the saved object.
+
+`fieldName: string` (optional)
+
+You can optionally pass the name of a field to edit. Leave empty to create a new runtime field based field.
+
+### `userPermissions.editIndexPattern(): boolean`
+
+Convenience method that uses the `core.application.capabilities` api to determine whether the user can edit the index pattern.
+
+### ``
+
+This children func React component provides a handler to delete one or multiple runtime fields.
+
+#### Props
+
+* `indexPattern: IndexPattern`: the current index pattern. (**required**)
+
+```js
+
+const { DeleteRuntimeFieldProvider } = indexPatternFieldEditor;
+
+// Single field
+
+ {(deleteField) => (
+ deleteField('myField')}>
+ Delete
+
+ )}
+
+
+// Multiple fields
+
+ {(deleteFields) => (
+ deleteFields(['field1', 'field2', 'field3'])}>
+ Delete
+
+ )}
+
+```
diff --git a/src/plugins/index_pattern_field_editor/jest.config.js b/src/plugins/index_pattern_field_editor/jest.config.js
new file mode 100644
index 00000000000000..fc358c37116c98
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/jest.config.js
@@ -0,0 +1,13 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+module.exports = {
+ preset: '@kbn/test',
+ rootDir: '../../..',
+ roots: ['/src/plugins/index_pattern_field_editor'],
+};
diff --git a/src/plugins/index_pattern_field_editor/kibana.json b/src/plugins/index_pattern_field_editor/kibana.json
new file mode 100644
index 00000000000000..1e44b43ab36390
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/kibana.json
@@ -0,0 +1,9 @@
+{
+ "id": "indexPatternFieldEditor",
+ "version": "kibana",
+ "server": false,
+ "ui": true,
+ "requiredPlugins": ["data"],
+ "optionalPlugins": ["usageCollection"],
+ "requiredBundles": ["kibanaReact", "esUiShared", "usageCollection"]
+}
diff --git a/src/plugins/index_pattern_field_editor/public/assets/icons/LICENSE.txt b/src/plugins/index_pattern_field_editor/public/assets/icons/LICENSE.txt
new file mode 100644
index 00000000000000..1a86627c4a6b8c
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/assets/icons/LICENSE.txt
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Steven Skelton
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/src/plugins/index_pattern_field_editor/public/assets/icons/cv.png b/src/plugins/index_pattern_field_editor/public/assets/icons/cv.png
new file mode 100644
index 0000000000000000000000000000000000000000..8f2ff8432e6bd2a6fb6e52b5d11f47b679e69d1f
GIT binary patch
literal 802
zcmV+-1Ks?IP)fIA97IawaZjI6Pk1u^EYXI}IFh
z1Qc-v6>axykuMMPB-A8P{?aRL)^0~2u;A!$)fPd7SS1Qc?Ahqru$wS0xOe1x=s
zf|-GWlY4`-d4slkgSLBwwSI%4f`N>DgtGtt|Ns5&|K(@@;#mLu<^TTo|LTJO-bMfU
z)%xS%`r_dF;oJJoc>2t4`ry|3(v14aVfx<6`r_f@Q9|KMJmE_{;Y&N=N;%<6I^j$_
z;!Qc>OFrUHIOJ6_`rz04(~bMhZTj8F`_6Ft$UXYnw*UV6|K)c7+eH8R;{WWM|KVo;
z`{rnIsbg)XWod?FW`AUCpJQ#OVr-{kZKq;wrebQGV{4paY@rJqbpsS|8YO2+NKz6X
zY6KK;2p4ZSJ6be2TnHC%4azP!z`T@1G_`)JcjjAP$bcfUC3m7AhzR
z1)+!#Cl@>DQ;35PBe*)cm?;|Dn&Z$WIcXF70G{c)@Z+Atxfg|6TKq4BY$*zm<$t3P
zfJyXat!Rz1l1Urd1VG6K@K!@?RdI>{NPRI;%+*owx_YAt^h{W(&_Uw
zi|gqT@T~k0cpUjP!o*L2{lhjv*T7lM^K7Gz1DbceH9Ks!4CiC>R0&VhbP8O#e}FF%Mxo5AVP*p5HLn1PDccY
zShg**6JY?rJU>NJDwfKRr%O>4QEZJN8dMq_4FHUAgGMY>A{Zz^(iLhZVYI%H04iim
z!UhT-;%nGQhJt6*B0^(AlGLb_(qx42Fpy!O;{+-M6N3iThiV<&z$DD_(($|1O(KA^
zA()a$_`Rr9ej><5wFpQdQUj$BIT#F~5y=#42rVQ4gmIe$g_2+>5Tem3L39WPUp@pp
zn^q>L3pi0PbKw?~kbz+uI*F8(l|{@7BBI)K5}8J$ksz1^!-052pl+KQ6B`26I`26K
z4x*E46&g%|szEEGSc2*?CIPSX_ZC!|SF~!~%Q4{tBN@aR5}62DTbc{x^Zzu
z!vx45^}d!^m$XfTkOYVh)oZ2r;^f}eR2n*4i-<8)n}nht&P_2f1I18X2C4zs?AfV-
zetfZ1p|(2L&Z78ydW>3!iPcghhQlP_JVb>;MyEvv(;`C2FqgxPB$K(pp=@qMB+TZ(
z)F5^cCyF|U<)Bi%3Q=QoSlJ)gpf_Tzf>3Gj%p63k*ow%aw5ST4ZJe%nO)ViX6=GAP
zqTa~&5-WR6Ewk7V2zn!ygcn1yuJ=#to^#KqD%Qto=G
zXH$U3z24s5+E%ctwsva#=~DwLd{B4Y?JY&s!j|Hm^I>_xM=2-82L%q7>PNoraX)bW
z>L=%aaKAZN#_Y%(>hJF_HNs{8%hBs6SKG3>GK)i;8cc4&9>{n8@;*+H+R+60ZrG*T
z=qGdDZ|~##?ShM8ZxfE$Ev;mA>|=)bxKS_Pu=O8R7^Z)u$vqe!OedO9Abkg?a*E+n
z6eSh7?7FRN|A>4f)@xwvdTrVVQ`tkds}xVf;jE*gl?4v*RFxRId*T5Gk(WyRNS|HE
zy|WxLJ&R#~c`w$d!lym#ORsNlLgf3BK_M8@9)#Mu9dl(lhd!s+_Ni+Zer4X`sd)dW
z@`6a1xyNJ8aeZK^>pEz(Cg6gJaQZI}j{Vdz%
ziIVS*0wsrKYvgMm!wde?;Jn}bc`1vv_kp(ffTQPDn^dO#CZy<4Ndr)Ry@yinLQ%6aGK4#vhE9<9jPRc+M7(}_0!mP3bvua*x5Z5o%{
zd;guJla`OAOkULzPf@_#ywAEX=6kev#y`?`bw7PE?)^nB%Ff!^A9k}bja4pmLSoKHMd2DPGjFhSLw|IvZ~)^;^zBs2khc3GZu?wnf(u+
zG$74i6(=m&2_L_lRJ3zZs1b%|#*Qs?ukjH6_|cBx6W1IgK0jFfcVCCc|78@MZ+x=K
zG%a?$m*uZ{w8o(6Z5_{O9lX;myK62fOdXy&ckbNyd(^|0WO@nrt(2r2yIr%-mjC0F
zhtLl>42uiCUz~b^_EUV+q63cbM&66BZO_7uH8nLo*~*lZlo2NRsn=q@cSMJ*#E-(Rj)4$`PUUzjN?E1RB}n25WvWpdf%=w=%`K)T3&UVGpT
Qv-K|-!%g5+MvC(O52$Psod5s;
literal 0
HcmV?d00001
diff --git a/src/plugins/index_pattern_field_editor/public/assets/icons/ne.png b/src/plugins/index_pattern_field_editor/public/assets/icons/ne.png
new file mode 100644
index 0000000000000000000000000000000000000000..d331209e179988c1a885b3048c7cbd39d3a297ca
GIT binary patch
literal 336
zcmV-W0k8gvP)-Ns
z|K-yEyN3U>cmLC}|N8gm^x)_905+ij0001-o+w)Y004MNL_t(2
z&+X5-5`sVwMbSGV%HU4BZ@4Sq^8bHlh6Ynz9b2CG2JgY2GoWEWRBOhqc9KK^_*&ZO
z_WA=K@D&Y5naFseM$?%9^MxA4O9!x0qxB{Ox6SoUyS>QanCg)~oiEp0@LlmyR%LZB
i0(cnkG~i|M+tC{bDNJMSx|^i{0000A7{?nJZW+VqtF_Z)C?=@51lbD=CT;_Y1C-RZwA+MG+F^!)O(?{w=)^Iu
zck9p==C(%O=$5UNIh`_rj!_t_D{LT)vNBre4~||-+q+)xN};_w?@#bazR&lQCwX3c
zlP@T!_%LJlzTF%SC!_Rev5@sQSiQ0r`+`&?cGS@R)FwmT~x@3L#hs!C~Mw@gG&t2&|-My&L;c{I}p~b)GneVu2X#CMytBr~PLIjXP
zfD{AxF|}W+_KN`ah)V%~4fyN?xZc$DSM$Yw2@sG&bH@RXu#*sWM(Zw2D?qCZcq|2w
z8qiS#J{Na}>p+XL%UA>2gq?V!c1Cq+w9Po6x_SR|_Z_8sfMFOaxruLBWB4kI!TIxl
zw-?^+C`9c&3`3LH5{W*e>13RM!>}oth!N=1?=S1c9k+mAdgD=NVhcytM-p2Yi7khH
z6Eu}zsOWFufxQ82zh^yj1?C0N+t?68Z$|tUGU6oB1q>OdY1)fhi~J)80{3$K69;_e
ztk6_Ra5_Nz8woCuxH}OKZ^e8}!ryz_Aka6RHDrEc$Za!Zf7ka`l^!zuV`L}{3p|OF
z2u*DzVk-=jTv{BGw9b|^O_wPr%a!Kx=KJNXBW2AGs=t2Zc8)PjVw+fwlfG@z;dTxV
zjn52^&kc{wnZ};LE>kkTwoQ5%nic^OnR>Rh>-w3C16As!|LlUb9vZ1t&q&n)F@Q>dkQj)t70O9BK4$Ih7GZ^mjrl(Z?D?aG{enx2
zkBHx#RV>Ie_i%Tfcsc9A*%XL7=E~O>i1|T{0HgLoujlMhX1da$^q=gfH6|{vn)l_o
z%5RHwu9AF1s)^L*GONh(uY1hI#Q?sG-Mb}
zwL7xB2kUS4ZmK4Ds7!Qi>XAy6+K2pFknR1d0n%`1_2weirT4r?8lYFXpO`=Bf6>>H
zYJE4C23~mB-0z;qknBidu5x*+lx3d>*9aN(`lu5rBrR2uzIk&?EMAXBon!Q
mgP{)J@ExT0SMl1b_?$!c@-pl{qpNISPAR{l`1eBD5B~v;Q#?8V
literal 0
HcmV?d00001
diff --git a/src/plugins/index_pattern_field_editor/public/assets/icons/stop.png b/src/plugins/index_pattern_field_editor/public/assets/icons/stop.png
new file mode 100644
index 0000000000000000000000000000000000000000..4bf65fc96f59fd0ff9ac094c903510dae4c30ae9
GIT binary patch
literal 1912
zcmaJ?X;2eq7!GJT6qUmRu$DE52LrpCaD)V+h9qc|K#URbKn%%3A|xBK8;PiBK|#d>
z5fBkP0To5drPZLwKpl~KD=JVy5C^J8KphYTxWBLj({k1Ivs+k5S8jmc(`I4l(<;ys>B?J88`?g
zQOi}h9904aMsXOb!I@-2>DLq#s_(Q)>|2?Lf1P(Ote9TKtcpVHEIdbxNt{9C>4XPM#MO(7NKa=a1{kI6h|=`ssh;Tp{fD1`C^G&
zX*irSgyQoVJSB#Ul@f%&1Lyg*&M2;8{3WJ
z=Q)h!pc0J&QR2f`=?^SxM66*T6e=Pz2T{w{BT_##ssM%(XUIp^LK~6q8&*297OxSp
z5FrLKwD(`_9zG&w$gurByoB+6{1GKF^J-$a$M;CK5_jemkHZpa>&urdj-0V<;y{7&
z>J?o<(?y%xDTkz)wziJs>hu+VY}Yq-ERr($9-<|Vb@=x}$&
zGnp}%dhFHo#~pf{d7-JWz~499B?8>ES$Bt-{XPfN)0ZC*`WEKoeQ(lH=SH~a^#nP<&nh?SI?Z31o!g)yjgWLgxnKU;jC`do1
z3ceN^60&)X8^1B?VrM~XbHDqIzkW}OIbb`jp)v!us*c{jdR$v^LgFmAei22(tn4kD
z$e&=ljS(-H3nn1XI_;g(>gP+SzL<03XR>RqeY|o_U*`HiLh%lFenovl)07K3Fkd(K
zx&C9P*s>+hEOX&j+DY3>Wwt$qLcjc1beYb5QKLQYeu42MQyJzUU-)y6#v(CwhKu$I
zp4(>Bc$nh+#K~gHZ?Cs{FEW|4)8VBwvFiA#m?U;|^1+B3Jzc)3Ycote$aCgzE@ulJ
z%>7z|+qFhfZk5
zzr(7wTc=>Bf8NoXE~w_P=0n+Ea_`t2&Z8W+KiwcvBo&P6PR_kw)Ewu3`Q6w@IkDk0
zgt2jPw25|n?^#b9n6-ONPKIT5RC(1ke4OdmFZtr~{!MvMqn$>)1<)^{Pvdxbbv3VK
zTJwOL#Wh)7<0H0B+SxRa+d7+thO#xeCR^H14#xblhZ|DahdDR5Td88xY|egaY;N@9}v;NtAmV88RhB_$=g=7w(8$@^YM&t<2j
z3wc+z&8$vuUGY#OUy+DMB|KbKp)OvRwrb|7vZ|VO_Ja>!;|w*1y)Sul1B9S0XGY)c
zD?F5$DQNBYR$JCTd#x)fqEwWYe(0*Jt9uJu+&=ZGFQc|i8y+6MF)nUkRqCgeI|psn
YlFp|X8_`>;bcTN+j~l=_@4GhsA7XF;BLDyZ
literal 0
HcmV?d00001
diff --git a/src/plugins/index_pattern_field_editor/public/assets/icons/us.png b/src/plugins/index_pattern_field_editor/public/assets/icons/us.png
new file mode 100644
index 0000000000000000000000000000000000000000..f30f21f85d06a0f4fee61bad2f6ad4bdd26ffb4c
GIT binary patch
literal 1074
zcmV-21kL-2P)Fc7vXIhM|a)tN;K1JWh2Y*VMGGoxgeO4kpUSn
z8iHkCO28F9lE+l!29^8F2d3VNFn;64P$Z+oC3K%(7;L~T^?MA;jEB@E=Av6?%y`}i
zWZz*v1CUXd9{Dm(LN`gt|GL)EUrvncodiH?R)sTe^JeToQ>1OR-;P@l?4G|Yj4!@O
z@Sd=2KsTvSobkRA<3#D-zrJNLPV!US%Wb*OxrzbJItH~@@nD1QY%XGC{IejnslEW+
zq&bWW)kRbvC^EKa2|K))T{p*C3p1Pw5LY}1PtHx&QyGU
zej-Tsi>WQ2d&=is^YvmxbCa^I%C0I##-%a6;ZOd&+dhM_+vxKW43p-h%|33$m{IuM
z5ai8unirxl-6X>JpMmNBe+D4M$N-_37#V+|D%#FK7%&PDqi8uXig<}p#6^rEPGS_X
s5u=EO7)6Z4D3T&Zku))i42Vz!08vwAal)*8c>n+a07*qoM6N<$f>$NdB>(^b
literal 0
HcmV?d00001
diff --git a/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/delete_field_provider.tsx b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/delete_field_provider.tsx
new file mode 100644
index 00000000000000..a42e1c18c1a614
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/delete_field_provider.tsx
@@ -0,0 +1,129 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React, { useState, useCallback } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiConfirmModal, EuiOverlayMask } from '@elastic/eui';
+
+type DeleteFieldFunc = (fieldName: string | string[]) => void;
+
+export interface Props {
+ children: (deleteFieldHandler: DeleteFieldFunc) => React.ReactNode;
+ onConfirmDelete: (fieldsToDelete: string[]) => Promise;
+}
+
+interface State {
+ isModalOpen: boolean;
+ fieldsToDelete: string[];
+}
+
+const geti18nTexts = (fieldsToDelete?: string[]) => {
+ let modalTitle = '';
+ if (fieldsToDelete) {
+ const isSingle = fieldsToDelete.length === 1;
+
+ modalTitle = isSingle
+ ? i18n.translate(
+ 'indexPatternFieldEditor.deleteRuntimeField.confirmModal.deleteSingleTitle',
+ {
+ defaultMessage: `Remove field '{name}'?`,
+ values: { name: fieldsToDelete[0] },
+ }
+ )
+ : i18n.translate(
+ 'indexPatternFieldEditor.deleteRuntimeField.confirmModal.deleteMultipleTitle',
+ {
+ defaultMessage: `Remove {count} fields?`,
+ values: { count: fieldsToDelete.length },
+ }
+ );
+ }
+
+ return {
+ modalTitle,
+ confirmButtonText: i18n.translate(
+ 'indexPatternFieldEditor.deleteRuntimeField.confirmationModal.removeButtonLabel',
+ {
+ defaultMessage: 'Remove',
+ }
+ ),
+ cancelButtonText: i18n.translate(
+ 'indexPatternFieldEditor.deleteRuntimeField.confirmationModal.cancelButtonLabel',
+ {
+ defaultMessage: 'Cancel',
+ }
+ ),
+ warningMultipleFields: i18n.translate(
+ 'indexPatternFieldEditor.deleteRuntimeField.confirmModal.multipleDeletionDescription',
+ {
+ defaultMessage: 'You are about to remove these runtime fields:',
+ }
+ ),
+ };
+};
+
+export const DeleteRuntimeFieldProvider = ({ children, onConfirmDelete }: Props) => {
+ const [state, setState] = useState({ isModalOpen: false, fieldsToDelete: [] });
+
+ const { isModalOpen, fieldsToDelete } = state;
+ const i18nTexts = geti18nTexts(fieldsToDelete);
+ const { modalTitle, confirmButtonText, cancelButtonText, warningMultipleFields } = i18nTexts;
+ const isMultiple = Boolean(fieldsToDelete.length > 1);
+
+ const deleteField: DeleteFieldFunc = useCallback((fieldNames) => {
+ setState({
+ isModalOpen: true,
+ fieldsToDelete: Array.isArray(fieldNames) ? fieldNames : [fieldNames],
+ });
+ }, []);
+
+ const closeModal = useCallback(() => {
+ setState({ isModalOpen: false, fieldsToDelete: [] });
+ }, []);
+
+ const confirmDelete = useCallback(async () => {
+ try {
+ await onConfirmDelete(fieldsToDelete);
+ closeModal();
+ } catch (e) {
+ // silently fail as "onConfirmDelete" is responsible
+ // to show a toast message if there is an error
+ }
+ }, [closeModal, onConfirmDelete, fieldsToDelete]);
+
+ return (
+ <>
+ {children(deleteField)}
+
+ {isModalOpen && (
+
+
+ {isMultiple && (
+ <>
+ {warningMultipleFields}
+
+ {fieldsToDelete.map((fieldName) => (
+ - {fieldName}
+ ))}
+
+ >
+ )}
+
+
+ )}
+ >
+ );
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/get_delete_provider.tsx b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/get_delete_provider.tsx
new file mode 100644
index 00000000000000..c8f1ad90357617
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/get_delete_provider.tsx
@@ -0,0 +1,62 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React, { useCallback } from 'react';
+
+import { i18n } from '@kbn/i18n';
+import { NotificationsStart } from 'src/core/public';
+import { IndexPattern, UsageCollectionStart } from '../../shared_imports';
+import { pluginName } from '../../constants';
+import { DeleteRuntimeFieldProvider, Props as DeleteProviderProps } from './delete_field_provider';
+import { DataPublicPluginStart } from '../../../../data/public';
+
+export interface Props extends Omit {
+ indexPattern: IndexPattern;
+ onDelete?: (fieldNames: string[]) => void;
+}
+
+export const getDeleteProvider = (
+ indexPatternService: DataPublicPluginStart['indexPatterns'],
+ usageCollection: UsageCollectionStart,
+ notifications: NotificationsStart
+): React.FunctionComponent => {
+ return React.memo(({ indexPattern, children, onDelete }: Props) => {
+ const deleteFields = useCallback(
+ async (fieldNames: string[]) => {
+ fieldNames.forEach((fieldName) => {
+ indexPattern.removeRuntimeField(fieldName);
+ });
+
+ try {
+ usageCollection.reportUiCounter(
+ pluginName,
+ usageCollection.METRIC_TYPE.COUNT,
+ 'delete_runtime'
+ );
+ // eslint-disable-next-line no-empty
+ } catch {}
+
+ try {
+ await indexPatternService.updateSavedObject(indexPattern);
+ } catch (e) {
+ const title = i18n.translate('indexPatternFieldEditor.save.deleteErrorTitle', {
+ defaultMessage: 'Failed to save field removal',
+ });
+ notifications.toasts.addError(e, { title });
+ }
+
+ if (onDelete) {
+ onDelete(fieldNames);
+ }
+ },
+ [onDelete, indexPattern]
+ );
+
+ return ;
+ });
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/index.ts b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/index.ts
new file mode 100644
index 00000000000000..b93b7b92560ecf
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/delete_field_provider/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+export { getDeleteProvider, Props as DeleteProviderProps } from './get_delete_provider';
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/advanced_parameters_section.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/advanced_parameters_section.tsx
new file mode 100644
index 00000000000000..26504eee28ddbd
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/advanced_parameters_section.tsx
@@ -0,0 +1,44 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React, { useState } from 'react';
+import { i18n } from '@kbn/i18n';
+
+import { EuiButtonEmpty, EuiSpacer } from '@elastic/eui';
+
+interface Props {
+ children: React.ReactNode;
+}
+
+export const AdvancedParametersSection = ({ children }: Props) => {
+ const [isVisible, setIsVisible] = useState(false);
+
+ const toggleIsVisible = () => {
+ setIsVisible(!isVisible);
+ };
+
+ return (
+ <>
+
+ {isVisible
+ ? i18n.translate('indexPatternFieldEditor.editor.form.advancedSettings.hideButtonLabel', {
+ defaultMessage: 'Hide advanced settings',
+ })
+ : i18n.translate('indexPatternFieldEditor.editor.form.advancedSettings.showButtonLabel', {
+ defaultMessage: 'Show advanced settings',
+ })}
+
+
+
+
+ {/* We ned to wrap the children inside a "div" to have our css :first-child rule */}
+
{children}
+
+ >
+ );
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/constants.ts b/src/plugins/index_pattern_field_editor/public/components/field_editor/constants.ts
new file mode 100644
index 00000000000000..82711f707fa199
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/constants.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import type { EuiComboBoxOptionOption } from '@elastic/eui';
+import { RuntimeType } from '../../shared_imports';
+
+export const RUNTIME_FIELD_OPTIONS: Array> = [
+ {
+ label: 'Keyword',
+ value: 'keyword',
+ },
+ {
+ label: 'Long',
+ value: 'long',
+ },
+ {
+ label: 'Double',
+ value: 'double',
+ },
+ {
+ label: 'Date',
+ value: 'date',
+ },
+ {
+ label: 'IP',
+ value: 'ip',
+ },
+ {
+ label: 'Boolean',
+ value: 'boolean',
+ },
+];
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.test.tsx
new file mode 100644
index 00000000000000..562f15301590bb
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.test.tsx
@@ -0,0 +1,212 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { act } from 'react-dom/test-utils';
+
+import '../../test_utils/setup_environment';
+import { registerTestBed, TestBed, getCommonActions } from '../../test_utils';
+import { Field } from '../../types';
+import { FieldEditor, Props, FieldEditorFormState } from './field_editor';
+
+const defaultProps: Props = {
+ onChange: jest.fn(),
+ links: {
+ runtimePainless: 'https://elastic.co',
+ },
+ ctx: {
+ existingConcreteFields: [],
+ namesNotAllowed: [],
+ fieldTypeToProcess: 'runtime',
+ },
+ indexPattern: { fields: [] } as any,
+ fieldFormatEditors: {
+ getAll: () => [],
+ getById: () => undefined,
+ },
+ fieldFormats: {} as any,
+ uiSettings: {} as any,
+ syntaxError: {
+ error: null,
+ clear: () => {},
+ },
+};
+
+const setup = (props?: Partial) => {
+ const testBed = registerTestBed(FieldEditor, {
+ memoryRouter: {
+ wrapComponent: false,
+ },
+ })({ ...defaultProps, ...props }) as TestBed;
+
+ const actions = {
+ ...getCommonActions(testBed),
+ };
+
+ return {
+ ...testBed,
+ actions,
+ };
+};
+
+describe('', () => {
+ beforeAll(() => {
+ jest.useFakeTimers();
+ });
+
+ afterAll(() => {
+ jest.useRealTimers();
+ });
+
+ let testBed: TestBed & { actions: ReturnType };
+ let onChange: jest.Mock = jest.fn();
+
+ const lastOnChangeCall = (): FieldEditorFormState[] =>
+ onChange.mock.calls[onChange.mock.calls.length - 1];
+
+ const getLastStateUpdate = () => lastOnChangeCall()[0];
+
+ const submitFormAndGetData = async (state: FieldEditorFormState) => {
+ let formState:
+ | {
+ data: Field;
+ isValid: boolean;
+ }
+ | undefined;
+
+ let promise: ReturnType;
+
+ await act(async () => {
+ // We can't await for the promise here as the validation for the
+ // "script" field has a setTimeout which is mocked by jest. If we await
+ // we don't have the chance to call jest.advanceTimersByTime and thus the
+ // test times out.
+ promise = state.submit();
+ });
+
+ await act(async () => {
+ // The painless syntax validation has a timeout set to 600ms
+ // we give it a bit more time just to be on the safe side
+ jest.advanceTimersByTime(1000);
+ });
+
+ await act(async () => {
+ promise.then((response) => {
+ formState = response;
+ });
+ });
+
+ return formState!;
+ };
+
+ beforeEach(() => {
+ onChange = jest.fn();
+ });
+
+ test('initial state should have "set custom label", "set value" and "set format" turned off', () => {
+ testBed = setup();
+
+ ['customLabel', 'value', 'format'].forEach((row) => {
+ const testSubj = `${row}Row.toggle`;
+ const toggle = testBed.find(testSubj);
+ const isOn = toggle.props()['aria-checked'];
+
+ try {
+ expect(isOn).toBe(false);
+ } catch (e) {
+ e.message = `"${row}" row toggle expected to be 'off' but was 'on'. \n${e.message}`;
+ throw e;
+ }
+ });
+ });
+
+ test('should accept a defaultValue and onChange prop to forward the form state', async () => {
+ const field = {
+ name: 'foo',
+ type: 'date',
+ script: { source: 'emit("hello")' },
+ };
+
+ testBed = setup({ onChange, field });
+
+ expect(onChange).toHaveBeenCalled();
+
+ let lastState = getLastStateUpdate();
+ expect(lastState.isValid).toBe(undefined);
+ expect(lastState.isSubmitted).toBe(false);
+ expect(lastState.submit).toBeDefined();
+
+ const { data: formData } = await submitFormAndGetData(lastState);
+ expect(formData).toEqual(field);
+
+ // Make sure that both isValid and isSubmitted state are now "true"
+ lastState = getLastStateUpdate();
+ expect(lastState.isValid).toBe(true);
+ expect(lastState.isSubmitted).toBe(true);
+ });
+
+ describe('validation', () => {
+ test('should accept an optional list of existing fields and prevent creating duplicates', async () => {
+ const existingFields = ['myRuntimeField'];
+ testBed = setup({
+ onChange,
+ ctx: {
+ namesNotAllowed: existingFields,
+ existingConcreteFields: [],
+ fieldTypeToProcess: 'runtime',
+ },
+ });
+
+ const { form, component, actions } = testBed;
+
+ await act(async () => {
+ actions.toggleFormRow('value');
+ });
+
+ await act(async () => {
+ form.setInputValue('nameField.input', existingFields[0]);
+ form.setInputValue('scriptField', 'echo("hello")');
+ });
+
+ await act(async () => {
+ jest.advanceTimersByTime(1000); // Make sure our debounced error message is in the DOM
+ });
+
+ const lastState = getLastStateUpdate();
+ await submitFormAndGetData(lastState);
+ component.update();
+ expect(getLastStateUpdate().isValid).toBe(false);
+ expect(form.getErrorsMessages()).toEqual(['A field with this name already exists.']);
+ });
+
+ test('should not count the default value as a duplicate', async () => {
+ const existingRuntimeFieldNames = ['myRuntimeField'];
+ const field: Field = {
+ name: 'myRuntimeField',
+ type: 'boolean',
+ script: { source: 'emit("hello"' },
+ };
+
+ testBed = setup({
+ field,
+ onChange,
+ ctx: {
+ namesNotAllowed: existingRuntimeFieldNames,
+ existingConcreteFields: [],
+ fieldTypeToProcess: 'runtime',
+ },
+ });
+
+ const { form, component } = testBed;
+ const lastState = getLastStateUpdate();
+ await submitFormAndGetData(lastState);
+ component.update();
+ expect(getLastStateUpdate().isValid).toBe(true);
+ expect(form.getErrorsMessages()).toEqual([]);
+ });
+ });
+});
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx
new file mode 100644
index 00000000000000..afb87bd1e73344
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/field_editor.tsx
@@ -0,0 +1,286 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React, { useEffect } from 'react';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiSpacer,
+ EuiComboBoxOptionOption,
+ EuiCode,
+} from '@elastic/eui';
+import type { CoreStart } from 'src/core/public';
+
+import {
+ Form,
+ useForm,
+ FormHook,
+ UseField,
+ TextField,
+ RuntimeType,
+ IndexPattern,
+ DataPublicPluginStart,
+} from '../../shared_imports';
+import { Field, InternalFieldType, PluginStart } from '../../types';
+
+import { RUNTIME_FIELD_OPTIONS } from './constants';
+import { schema } from './form_schema';
+import { getNameFieldConfig } from './lib';
+import {
+ TypeField,
+ CustomLabelField,
+ ScriptField,
+ FormatField,
+ PopularityField,
+ ScriptSyntaxError,
+} from './form_fields';
+import { FormRow } from './form_row';
+import { AdvancedParametersSection } from './advanced_parameters_section';
+
+export interface FieldEditorFormState {
+ isValid: boolean | undefined;
+ isSubmitted: boolean;
+ submit: FormHook['submit'];
+}
+
+export interface FieldFormInternal extends Omit {
+ type: Array>;
+ __meta__: {
+ isCustomLabelVisible: boolean;
+ isValueVisible: boolean;
+ isFormatVisible: boolean;
+ isPopularityVisible: boolean;
+ };
+}
+
+export interface Props {
+ /** Link URLs to our doc site */
+ links: {
+ runtimePainless: string;
+ };
+ /** Optional field to edit */
+ field?: Field;
+ /** Handler to receive state changes updates */
+ onChange?: (state: FieldEditorFormState) => void;
+ indexPattern: IndexPattern;
+ fieldFormatEditors: PluginStart['fieldFormatEditors'];
+ fieldFormats: DataPublicPluginStart['fieldFormats'];
+ uiSettings: CoreStart['uiSettings'];
+ /** Context object */
+ ctx: {
+ /** The internal field type we are dealing with (concrete|runtime)*/
+ fieldTypeToProcess: InternalFieldType;
+ /**
+ * An array of field names not allowed.
+ * e.g we probably don't want a user to give a name of an existing
+ * runtime field (for that the user should edit the existing runtime field).
+ */
+ namesNotAllowed: string[];
+ /**
+ * An array of existing concrete fields. If the user gives a name to the runtime
+ * field that matches one of the concrete fields, a callout will be displayed
+ * to indicate that this runtime field will shadow the concrete field.
+ * It is also used to provide the list of field autocomplete suggestions to the code editor.
+ */
+ existingConcreteFields: Array<{ name: string; type: string }>;
+ };
+ syntaxError: ScriptSyntaxError;
+}
+
+const geti18nTexts = (): {
+ [key: string]: { title: string; description: JSX.Element | string };
+} => ({
+ customLabel: {
+ title: i18n.translate('indexPatternFieldEditor.editor.form.customLabelTitle', {
+ defaultMessage: 'Set custom label',
+ }),
+ description: i18n.translate('indexPatternFieldEditor.editor.form.customLabelDescription', {
+ defaultMessage: `Create a label to display in place of the field name in Discover, Maps, and Visualize. Useful for shortening a long field name. Queries and filters use the original field name.`,
+ }),
+ },
+ value: {
+ title: i18n.translate('indexPatternFieldEditor.editor.form.valueTitle', {
+ defaultMessage: 'Set value',
+ }),
+ description: (
+ {'_source'},
+ }}
+ />
+ ),
+ },
+ format: {
+ title: i18n.translate('indexPatternFieldEditor.editor.form.formatTitle', {
+ defaultMessage: 'Set format',
+ }),
+ description: i18n.translate('indexPatternFieldEditor.editor.form.formatDescription', {
+ defaultMessage: `Set your preferred format for displaying the value. Changing the format can affect the value and prevent highlighting in Discover.`,
+ }),
+ },
+ popularity: {
+ title: i18n.translate('indexPatternFieldEditor.editor.form.popularityTitle', {
+ defaultMessage: 'Set popularity',
+ }),
+ description: i18n.translate('indexPatternFieldEditor.editor.form.popularityDescription', {
+ defaultMessage: `Adjust the popularity to make the field appear higher or lower in the fields list. By default, Discover orders fields from most selected to least selected.`,
+ }),
+ },
+});
+
+const formDeserializer = (field: Field): FieldFormInternal => {
+ let fieldType: Array>;
+ if (!field.type) {
+ fieldType = [RUNTIME_FIELD_OPTIONS[0]];
+ } else {
+ const label = RUNTIME_FIELD_OPTIONS.find(({ value }) => value === field.type)?.label;
+ fieldType = [{ label: label ?? field.type, value: field.type as RuntimeType }];
+ }
+
+ return {
+ ...field,
+ type: fieldType,
+ __meta__: {
+ isCustomLabelVisible: field.customLabel !== undefined,
+ isValueVisible: field.script !== undefined,
+ isFormatVisible: field.format !== undefined,
+ isPopularityVisible: field.popularity !== undefined,
+ },
+ };
+};
+
+const formSerializer = (field: FieldFormInternal): Field => {
+ const { __meta__, type, ...rest } = field;
+ return {
+ type: type[0].value!,
+ ...rest,
+ };
+};
+
+const FieldEditorComponent = ({
+ field,
+ onChange,
+ links,
+ indexPattern,
+ fieldFormatEditors,
+ fieldFormats,
+ uiSettings,
+ syntaxError,
+ ctx: { fieldTypeToProcess, namesNotAllowed, existingConcreteFields },
+}: Props) => {
+ const { form } = useForm({
+ defaultValue: field,
+ schema,
+ deserializer: formDeserializer,
+ serializer: formSerializer,
+ });
+ const { submit, isValid: isFormValid, isSubmitted } = form;
+
+ const nameFieldConfig = getNameFieldConfig(namesNotAllowed, field);
+ const i18nTexts = geti18nTexts();
+
+ useEffect(() => {
+ if (onChange) {
+ onChange({ isValid: isFormValid, isSubmitted, submit });
+ }
+ }, [onChange, isFormValid, isSubmitted, submit]);
+
+ return (
+
+ );
+};
+
+export const FieldEditor = React.memo(FieldEditorComponent);
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/custom_label_field.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/custom_label_field.tsx
new file mode 100644
index 00000000000000..313137de463032
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/custom_label_field.tsx
@@ -0,0 +1,15 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+
+import { UseField, TextField } from '../../../shared_imports';
+
+export const CustomLabelField = () => {
+ return ;
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/format_field.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/format_field.tsx
new file mode 100644
index 00000000000000..db98e4a1591625
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/format_field.tsx
@@ -0,0 +1,82 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+import React, { useState, useEffect, useRef } from 'react';
+import { EuiCallOut, EuiSpacer } from '@elastic/eui';
+
+import { UseField, useFormData, ES_FIELD_TYPES, useFormContext } from '../../../shared_imports';
+import { FormatSelectEditor, FormatSelectEditorProps } from '../../field_format_editor';
+import { FieldFormInternal } from '../field_editor';
+import { FieldFormatConfig } from '../../../types';
+
+export const FormatField = ({
+ indexPattern,
+ fieldFormatEditors,
+ fieldFormats,
+ uiSettings,
+}: Omit) => {
+ const isMounted = useRef(false);
+ const [{ type }] = useFormData({ watch: ['name', 'type'] });
+ const { getFields, isSubmitted } = useFormContext();
+ const [formatError, setFormatError] = useState();
+ // convert from combobox type to values
+ const typeValue = type.reduce((collector, item) => {
+ if (item.value !== undefined) {
+ collector.push(item.value as ES_FIELD_TYPES);
+ }
+ return collector;
+ }, [] as ES_FIELD_TYPES[]);
+
+ useEffect(() => {
+ if (formatError === undefined) {
+ getFields().format.setErrors([]);
+ } else {
+ getFields().format.setErrors([{ message: formatError }]);
+ }
+ }, [formatError, getFields]);
+
+ useEffect(() => {
+ if (isMounted.current) {
+ getFields().format.reset();
+ }
+ isMounted.current = true;
+ }, [type, getFields]);
+
+ return (
+ path="format">
+ {({ setValue, errors, value }) => {
+ return (
+ <>
+ {isSubmitted && errors.length > 0 && (
+ <>
+ err.message)}
+ color="danger"
+ iconType="cross"
+ data-test-subj="formFormatError"
+ />
+
+ >
+ )}
+
+
+ >
+ );
+ }}
+
+ );
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/index.ts
new file mode 100644
index 00000000000000..e958e1362bb054
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+export { TypeField } from './type_field';
+
+export { CustomLabelField } from './custom_label_field';
+
+export { PopularityField } from './popularity_field';
+
+export { ScriptField, ScriptSyntaxError } from './script_field';
+
+export { FormatField } from './format_field';
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/popularity_field.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/popularity_field.tsx
new file mode 100644
index 00000000000000..44f83138fe1d31
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/popularity_field.tsx
@@ -0,0 +1,21 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+
+import { UseField, NumericField } from '../../../shared_imports';
+
+export const PopularityField = () => {
+ return (
+
+ );
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/script_field.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/script_field.tsx
new file mode 100644
index 00000000000000..d15445f3e10ae6
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/script_field.tsx
@@ -0,0 +1,227 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React, { useState, useEffect, useMemo, useRef } from 'react';
+import { i18n } from '@kbn/i18n';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EuiFormRow, EuiLink, EuiCode, EuiCodeBlock, EuiSpacer, EuiTitle } from '@elastic/eui';
+import { PainlessLang, PainlessContext } from '@kbn/monaco';
+
+import {
+ UseField,
+ useFormData,
+ RuntimeType,
+ FieldConfig,
+ CodeEditor,
+} from '../../../shared_imports';
+import { RuntimeFieldPainlessError } from '../../../lib';
+import { schema } from '../form_schema';
+import type { FieldFormInternal } from '../field_editor';
+
+interface Props {
+ links: { runtimePainless: string };
+ existingConcreteFields?: Array<{ name: string; type: string }>;
+ syntaxError: ScriptSyntaxError;
+}
+
+export interface ScriptSyntaxError {
+ error: RuntimeFieldPainlessError | null;
+ clear: () => void;
+}
+
+const mapReturnTypeToPainlessContext = (runtimeType: RuntimeType): PainlessContext => {
+ switch (runtimeType) {
+ case 'keyword':
+ return 'string_script_field_script_field';
+ case 'long':
+ return 'long_script_field_script_field';
+ case 'double':
+ return 'double_script_field_script_field';
+ case 'date':
+ return 'date_script_field';
+ case 'ip':
+ return 'ip_script_field_script_field';
+ case 'boolean':
+ return 'boolean_script_field_script_field';
+ default:
+ return 'string_script_field_script_field';
+ }
+};
+
+export const ScriptField = React.memo(({ existingConcreteFields, links, syntaxError }: Props) => {
+ const editorValidationTimeout = useRef>();
+
+ const [painlessContext, setPainlessContext] = useState(
+ mapReturnTypeToPainlessContext(schema.type.defaultValue[0].value!)
+ );
+
+ const [editorId, setEditorId] = useState();
+
+ const suggestionProvider = PainlessLang.getSuggestionProvider(
+ painlessContext,
+ existingConcreteFields
+ );
+
+ const [{ type, script: { source } = { source: '' } }] = useFormData({
+ watch: ['type', 'script.source'],
+ });
+
+ const { clear: clearSyntaxError } = syntaxError;
+
+ const sourceFieldConfig: FieldConfig = useMemo(() => {
+ return {
+ ...schema.script.source,
+ validations: [
+ ...schema.script.source.validations,
+ {
+ validator: () => {
+ if (editorValidationTimeout.current) {
+ clearTimeout(editorValidationTimeout.current);
+ }
+
+ return new Promise((resolve) => {
+ // monaco waits 500ms before validating, so we also add a delay
+ // before checking if there are any syntax errors
+ editorValidationTimeout.current = setTimeout(() => {
+ const painlessSyntaxErrors = PainlessLang.getSyntaxErrors();
+ // It is possible for there to be more than one editor in a view,
+ // so we need to get the syntax errors based on the editor (aka model) ID
+ const editorHasSyntaxErrors = editorId && painlessSyntaxErrors[editorId].length > 0;
+
+ if (editorHasSyntaxErrors) {
+ return resolve({
+ message: i18n.translate(
+ 'indexPatternFieldEditor.editor.form.scriptEditorValidationMessage',
+ {
+ defaultMessage: 'Invalid Painless syntax.',
+ }
+ ),
+ });
+ }
+
+ resolve(undefined);
+ }, 600);
+ });
+ },
+ },
+ ],
+ };
+ }, [editorId]);
+
+ useEffect(() => {
+ setPainlessContext(mapReturnTypeToPainlessContext(type[0]!.value!));
+ }, [type]);
+
+ useEffect(() => {
+ // Whenever the source changes we clear potential syntax errors
+ clearSyntaxError();
+ }, [source, clearSyntaxError]);
+
+ return (
+ path="script.source" config={sourceFieldConfig}>
+ {({ value, setValue, label, isValid, getErrorsMessages }) => {
+ let errorMessage: string | null = '';
+ if (syntaxError.error !== null) {
+ errorMessage = syntaxError.error.reason ?? syntaxError.error.message;
+ } else {
+ errorMessage = getErrorsMessages();
+ }
+
+ return (
+ <>
+
+ {i18n.translate(
+ 'indexPatternFieldEditor.editor.form.script.learnMoreLinkText',
+ {
+ defaultMessage: 'Learn about script syntax.',
+ }
+ )}
+
+ ),
+ source: {'_source'},
+ }}
+ />
+ }
+ fullWidth
+ >
+ setEditorId(editor.getModel()?.id)}
+ options={{
+ fontSize: 12,
+ minimap: {
+ enabled: false,
+ },
+ scrollBeyondLastLine: false,
+ wordWrap: 'on',
+ wrappingIndent: 'indent',
+ automaticLayout: true,
+ suggest: {
+ snippetsPreventQuickSuggestions: false,
+ },
+ }}
+ data-test-subj="scriptField"
+ aria-label={i18n.translate(
+ 'indexPatternFieldEditor.editor.form.scriptEditorAriaLabel',
+ {
+ defaultMessage: 'Script editor',
+ }
+ )}
+ />
+
+
+ {/* Help the user debug the error by showing where it failed in the script */}
+ {syntaxError.error !== null && (
+ <>
+
+
+
+ {i18n.translate(
+ 'indexPatternFieldEditor.editor.form.scriptEditor.debugErrorMessage',
+ {
+ defaultMessage: 'Syntax error detail',
+ }
+ )}
+
+
+
+
+ {syntaxError.error.scriptStack.join('\n')}
+
+ >
+ )}
+ >
+ );
+ }}
+
+ );
+});
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/type_field.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/type_field.tsx
new file mode 100644
index 00000000000000..36428579a30e86
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_fields/type_field.tsx
@@ -0,0 +1,65 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import { i18n } from '@kbn/i18n';
+
+import { EuiFormRow, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
+
+import { UseField, RuntimeType } from '../../../shared_imports';
+import { RUNTIME_FIELD_OPTIONS } from '../constants';
+
+interface Props {
+ isDisabled?: boolean;
+}
+
+export const TypeField = ({ isDisabled = false }: Props) => {
+ return (
+ >> path="type">
+ {({ label, value, setValue }) => {
+ if (value === undefined) {
+ return null;
+ }
+ return (
+ <>
+
+ {
+ if (newValue.length === 0) {
+ // Don't allow clearing the type. One must always be selected
+ return;
+ }
+ setValue(newValue);
+ }}
+ isClearable={false}
+ isDisabled={isDisabled}
+ data-test-subj="typeField"
+ aria-label={i18n.translate(
+ 'indexPatternFieldEditor.editor.form.typeSelectAriaLabel',
+ {
+ defaultMessage: 'Type select',
+ }
+ )}
+ fullWidth
+ />
+
+ >
+ );
+ }}
+
+ );
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/form_row.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_row.tsx
new file mode 100644
index 00000000000000..66f5af09c8b2f4
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_row.tsx
@@ -0,0 +1,86 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import { get } from 'lodash';
+import {
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiTitle,
+ EuiText,
+ EuiHorizontalRule,
+ EuiSpacer,
+} from '@elastic/eui';
+
+import { UseField, ToggleField, useFormData } from '../../shared_imports';
+
+interface Props {
+ title: string;
+ formFieldPath: string;
+ children: React.ReactNode;
+ description?: string | JSX.Element;
+ withDividerRule?: boolean;
+ 'data-test-subj'?: string;
+}
+
+export const FormRow = ({
+ title,
+ description,
+ children,
+ formFieldPath,
+ withDividerRule = false,
+ 'data-test-subj': dataTestSubj,
+}: Props) => {
+ const [formData] = useFormData({ watch: formFieldPath });
+ const isContentVisible = Boolean(get(formData, formFieldPath));
+
+ return (
+ <>
+
+
+
+
+
+
+
+ {/* Title */}
+
+ {title}
+
+
+
+ {/* Description */}
+
+ {description}
+
+
+ {/* Content */}
+ {isContentVisible && (
+ <>
+
+ {children}
+ >
+ )}
+
+
+
+
+ {withDividerRule && }
+ >
+ );
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/form_schema.ts b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_schema.ts
new file mode 100644
index 00000000000000..a722f277b8e237
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/form_schema.ts
@@ -0,0 +1,119 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+import { fieldValidators } from '../../shared_imports';
+
+import { RUNTIME_FIELD_OPTIONS } from './constants';
+
+const { emptyField, numberGreaterThanField } = fieldValidators;
+
+export const schema = {
+ name: {
+ label: i18n.translate('indexPatternFieldEditor.editor.form.nameLabel', {
+ defaultMessage: 'Name',
+ }),
+ validations: [
+ {
+ validator: emptyField(
+ i18n.translate(
+ 'indexPatternFieldEditor.editor.form.validations.nameIsRequiredErrorMessage',
+ {
+ defaultMessage: 'A name is required.',
+ }
+ )
+ ),
+ },
+ ],
+ },
+ type: {
+ label: i18n.translate('indexPatternFieldEditor.editor.form.runtimeTypeLabel', {
+ defaultMessage: 'Type',
+ }),
+ defaultValue: [RUNTIME_FIELD_OPTIONS[0]],
+ },
+ script: {
+ source: {
+ label: i18n.translate('indexPatternFieldEditor.editor.form.defineFieldLabel', {
+ defaultMessage: 'Define script',
+ }),
+ validations: [
+ {
+ validator: emptyField(
+ i18n.translate(
+ 'indexPatternFieldEditor.editor.form.validations.scriptIsRequiredErrorMessage',
+ {
+ defaultMessage: 'A script is required to set the field value.',
+ }
+ )
+ ),
+ },
+ ],
+ },
+ },
+ customLabel: {
+ label: i18n.translate('indexPatternFieldEditor.editor.form.customLabelLabel', {
+ defaultMessage: 'Custom label',
+ }),
+ validations: [
+ {
+ validator: emptyField(
+ i18n.translate(
+ 'indexPatternFieldEditor.editor.form.validations.customLabelIsRequiredErrorMessage',
+ {
+ defaultMessage: 'Give a label to the field.',
+ }
+ )
+ ),
+ },
+ ],
+ },
+ popularity: {
+ label: i18n.translate('indexPatternFieldEditor.editor.form.popularityLabel', {
+ defaultMessage: 'Popularity',
+ }),
+ validations: [
+ {
+ validator: emptyField(
+ i18n.translate(
+ 'indexPatternFieldEditor.editor.form.validations.popularityIsRequiredErrorMessage',
+ {
+ defaultMessage: 'Give a popularity to the field.',
+ }
+ )
+ ),
+ },
+ {
+ validator: numberGreaterThanField({
+ than: 0,
+ allowEquality: true,
+ message: i18n.translate(
+ 'indexPatternFieldEditor.editor.form.validations.popularityGreaterThan0ErrorMessage',
+ {
+ defaultMessage: 'The popularity must be zero or greater.',
+ }
+ ),
+ }),
+ },
+ ],
+ },
+ __meta__: {
+ isCustomLabelVisible: {
+ defaultValue: false,
+ },
+ isValueVisible: {
+ defaultValue: false,
+ },
+ isFormatVisible: {
+ defaultValue: false,
+ },
+ isPopularityVisible: {
+ defaultValue: false,
+ },
+ },
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_editor/index.ts
new file mode 100644
index 00000000000000..db7c05fa7ff7a4
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/index.ts
@@ -0,0 +1,9 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+export { FieldEditor } from './field_editor';
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/lib.ts b/src/plugins/index_pattern_field_editor/public/components/field_editor/lib.ts
new file mode 100644
index 00000000000000..2d324804c9e43d
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/lib.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+import { ValidationFunc, FieldConfig } from '../../shared_imports';
+import { Field } from '../../types';
+import { schema } from './form_schema';
+import { Props } from './field_editor';
+
+const createNameNotAllowedValidator = (
+ namesNotAllowed: string[]
+): ValidationFunc<{}, string, string> => ({ value }) => {
+ if (namesNotAllowed.includes(value)) {
+ return {
+ message: i18n.translate(
+ 'indexPatternFieldEditor.editor.runtimeFieldsEditor.existRuntimeFieldNamesValidationErrorMessage',
+ {
+ defaultMessage: 'A field with this name already exists.',
+ }
+ ),
+ };
+ }
+};
+
+/**
+ * Dynamically retrieve the config for the "name" field, adding
+ * a validator to avoid duplicated runtime fields to be created.
+ *
+ * @param namesNotAllowed Array of names not allowed for the field "name"
+ * @param field Initial value of the form
+ */
+export const getNameFieldConfig = (
+ namesNotAllowed?: string[],
+ field?: Props['field']
+): FieldConfig => {
+ const nameFieldConfig = schema.name as FieldConfig;
+
+ if (!namesNotAllowed) {
+ return nameFieldConfig;
+ }
+
+ // Add validation to not allow duplicates
+ return {
+ ...nameFieldConfig!,
+ validations: [
+ ...(nameFieldConfig.validations ?? []),
+ {
+ validator: createNameNotAllowedValidator(
+ namesNotAllowed.filter((name) => name !== field?.name)
+ ),
+ },
+ ],
+ };
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor/shadowing_field_warning.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor/shadowing_field_warning.tsx
new file mode 100644
index 00000000000000..4343b13db9a5af
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor/shadowing_field_warning.tsx
@@ -0,0 +1,32 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiCallOut } from '@elastic/eui';
+
+export const ShadowingFieldWarning = () => {
+ return (
+
+
+ {i18n.translate('indexPatternFieldEditor.editor.form.fieldShadowingCalloutDescription', {
+ defaultMessage:
+ 'This field shares the name of a mapped field. Values for this field will be returned in search results.',
+ })}
+
+
+ );
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.test.ts b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.test.ts
new file mode 100644
index 00000000000000..e943dbdda998df
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.test.ts
@@ -0,0 +1,198 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+import { act } from 'react-dom/test-utils';
+
+import '../test_utils/setup_environment';
+import { registerTestBed, TestBed, noop, docLinks, getCommonActions } from '../test_utils';
+
+import { FieldEditor } from './field_editor';
+import { FieldEditorFlyoutContent, Props } from './field_editor_flyout_content';
+
+const defaultProps: Props = {
+ onSave: noop,
+ onCancel: noop,
+ docLinks,
+ FieldEditor,
+ indexPattern: { fields: [] } as any,
+ uiSettings: {} as any,
+ fieldFormats: {} as any,
+ fieldFormatEditors: {} as any,
+ fieldTypeToProcess: 'runtime',
+ runtimeFieldValidator: () => Promise.resolve(null),
+ isSavingField: false,
+};
+
+const setup = (props: Props = defaultProps) => {
+ const testBed = registerTestBed(FieldEditorFlyoutContent, {
+ memoryRouter: { wrapComponent: false },
+ })(props) as TestBed;
+
+ const actions = {
+ ...getCommonActions(testBed),
+ };
+
+ return {
+ ...testBed,
+ actions,
+ };
+};
+
+describe('', () => {
+ beforeAll(() => {
+ jest.useFakeTimers();
+ });
+
+ afterAll(() => {
+ jest.useRealTimers();
+ });
+
+ test('should have the correct title', () => {
+ const { exists, find } = setup();
+ expect(exists('flyoutTitle')).toBe(true);
+ expect(find('flyoutTitle').text()).toBe('Create field');
+ });
+
+ test('should allow a field to be provided', () => {
+ const field = {
+ name: 'foo',
+ type: 'ip',
+ script: {
+ source: 'emit("hello world")',
+ },
+ };
+
+ const { find } = setup({ ...defaultProps, field });
+
+ expect(find('flyoutTitle').text()).toBe(`Edit ${field.name} field`);
+ expect(find('nameField.input').props().value).toBe(field.name);
+ expect(find('typeField').props().value).toBe(field.type);
+ expect(find('scriptField').props().value).toBe(field.script.source);
+ });
+
+ test('should accept an "onSave" prop', async () => {
+ const field = {
+ name: 'foo',
+ type: 'date',
+ script: { source: 'test=123' },
+ };
+ const onSave: jest.Mock = jest.fn();
+
+ const { find } = setup({ ...defaultProps, onSave, field });
+
+ await act(async () => {
+ find('fieldSaveButton').simulate('click');
+ });
+
+ await act(async () => {
+ // The painless syntax validation has a timeout set to 600ms
+ // we give it a bit more time just to be on the safe side
+ jest.advanceTimersByTime(1000);
+ });
+
+ expect(onSave).toHaveBeenCalled();
+ const fieldReturned = onSave.mock.calls[onSave.mock.calls.length - 1][0];
+ expect(fieldReturned).toEqual(field);
+ });
+
+ test('should accept an onCancel prop', () => {
+ const onCancel = jest.fn();
+ const { find } = setup({ ...defaultProps, onCancel });
+
+ find('closeFlyoutButton').simulate('click');
+
+ expect(onCancel).toHaveBeenCalled();
+ });
+
+ describe('validation', () => {
+ test('should validate the fields and prevent saving invalid form', async () => {
+ const onSave: jest.Mock = jest.fn();
+
+ const { find, exists, form, component } = setup({ ...defaultProps, onSave });
+
+ expect(find('fieldSaveButton').props().disabled).toBe(false);
+
+ await act(async () => {
+ find('fieldSaveButton').simulate('click');
+ });
+
+ await act(async () => {
+ jest.advanceTimersByTime(1000);
+ });
+
+ component.update();
+
+ expect(onSave).toHaveBeenCalledTimes(0);
+ expect(find('fieldSaveButton').props().disabled).toBe(true);
+ expect(form.getErrorsMessages()).toEqual(['A name is required.']);
+ expect(exists('formError')).toBe(true);
+ expect(find('formError').text()).toBe('Fix errors in form before continuing.');
+ });
+
+ test('should forward values from the form', async () => {
+ const onSave: jest.Mock = jest.fn();
+
+ const {
+ find,
+ component,
+ form,
+ actions: { toggleFormRow },
+ } = setup({ ...defaultProps, onSave });
+
+ act(() => {
+ form.setInputValue('nameField.input', 'someName');
+ toggleFormRow('value');
+ });
+ component.update();
+
+ await act(async () => {
+ form.setInputValue('scriptField', 'echo("hello")');
+ });
+
+ await act(async () => {
+ // Let's make sure that validation has finished running
+ jest.advanceTimersByTime(1000);
+ });
+
+ await act(async () => {
+ find('fieldSaveButton').simulate('click');
+ });
+
+ expect(onSave).toHaveBeenCalled();
+
+ let fieldReturned = onSave.mock.calls[onSave.mock.calls.length - 1][0];
+
+ expect(fieldReturned).toEqual({
+ name: 'someName',
+ type: 'keyword', // default to keyword
+ script: { source: 'echo("hello")' },
+ });
+
+ // Change the type and make sure it is forwarded
+ act(() => {
+ find('typeField').simulate('change', [
+ {
+ label: 'Other type',
+ value: 'other_type',
+ },
+ ]);
+ });
+
+ await act(async () => {
+ find('fieldSaveButton').simulate('click');
+ });
+
+ fieldReturned = onSave.mock.calls[onSave.mock.calls.length - 1][0];
+
+ expect(fieldReturned).toEqual({
+ name: 'someName',
+ type: 'other_type',
+ script: { source: 'echo("hello")' },
+ });
+ });
+ });
+});
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx
new file mode 100644
index 00000000000000..1511836da85e73
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content.tsx
@@ -0,0 +1,253 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React, { useState, useCallback, useMemo } from 'react';
+import { i18n } from '@kbn/i18n';
+import {
+ EuiFlyoutHeader,
+ EuiFlyoutBody,
+ EuiFlyoutFooter,
+ EuiTitle,
+ EuiFlexGroup,
+ EuiFlexItem,
+ EuiButtonEmpty,
+ EuiButton,
+ EuiCallOut,
+ EuiSpacer,
+} from '@elastic/eui';
+
+import { DocLinksStart, CoreStart } from 'src/core/public';
+
+import { Field, InternalFieldType, PluginStart, EsRuntimeField } from '../types';
+import { getLinks, RuntimeFieldPainlessError } from '../lib';
+import type { IndexPattern, DataPublicPluginStart } from '../shared_imports';
+import type { Props as FieldEditorProps, FieldEditorFormState } from './field_editor/field_editor';
+
+const geti18nTexts = (field?: Field) => {
+ return {
+ flyoutTitle: field
+ ? i18n.translate('indexPatternFieldEditor.editor.flyoutEditFieldTitle', {
+ defaultMessage: 'Edit {fieldName} field',
+ values: {
+ fieldName: field.name,
+ },
+ })
+ : i18n.translate('indexPatternFieldEditor.editor.flyoutDefaultTitle', {
+ defaultMessage: 'Create field',
+ }),
+ closeButtonLabel: i18n.translate('indexPatternFieldEditor.editor.flyoutCloseButtonLabel', {
+ defaultMessage: 'Close',
+ }),
+ saveButtonLabel: i18n.translate('indexPatternFieldEditor.editor.flyoutSaveButtonLabel', {
+ defaultMessage: 'Save',
+ }),
+ formErrorsCalloutTitle: i18n.translate('indexPatternFieldEditor.editor.validationErrorTitle', {
+ defaultMessage: 'Fix errors in form before continuing.',
+ }),
+ };
+};
+
+export interface Props {
+ /**
+ * Handler for the "save" footer button
+ */
+ onSave: (field: Field) => void;
+ /**
+ * Handler for the "cancel" footer button
+ */
+ onCancel: () => void;
+ /**
+ * The docLinks start service from core
+ */
+ docLinks: DocLinksStart;
+ /**
+ * The Field editor component that contains the form to create or edit a field
+ */
+ FieldEditor: React.ComponentType | null;
+ /** The internal field type we are dealing with (concrete|runtime)*/
+ fieldTypeToProcess: InternalFieldType;
+ /** Handler to validate the script */
+ runtimeFieldValidator: (field: EsRuntimeField) => Promise;
+ /** Optional field to process */
+ field?: Field;
+
+ indexPattern: IndexPattern;
+ fieldFormatEditors: PluginStart['fieldFormatEditors'];
+ fieldFormats: DataPublicPluginStart['fieldFormats'];
+ uiSettings: CoreStart['uiSettings'];
+ isSavingField: boolean;
+}
+
+const FieldEditorFlyoutContentComponent = ({
+ field,
+ onSave,
+ onCancel,
+ FieldEditor,
+ docLinks,
+ indexPattern,
+ fieldFormatEditors,
+ fieldFormats,
+ uiSettings,
+ fieldTypeToProcess,
+ runtimeFieldValidator,
+ isSavingField,
+}: Props) => {
+ const i18nTexts = geti18nTexts(field);
+
+ const [formState, setFormState] = useState({
+ isSubmitted: false,
+ isValid: field ? true : undefined,
+ submit: field
+ ? async () => ({ isValid: true, data: field })
+ : async () => ({ isValid: false, data: {} as Field }),
+ });
+
+ const [painlessSyntaxError, setPainlessSyntaxError] = useState(
+ null
+ );
+
+ const [isValidating, setIsValidating] = useState(false);
+
+ const { submit, isValid: isFormValid, isSubmitted } = formState;
+ const { fields } = indexPattern;
+ const isSaveButtonDisabled = isFormValid === false || painlessSyntaxError !== null;
+
+ const clearSyntaxError = useCallback(() => setPainlessSyntaxError(null), []);
+
+ const syntaxError = useMemo(
+ () => ({
+ error: painlessSyntaxError,
+ clear: clearSyntaxError,
+ }),
+ [painlessSyntaxError, clearSyntaxError]
+ );
+
+ const onClickSave = useCallback(async () => {
+ const { isValid, data } = await submit();
+
+ if (isValid) {
+ if (data.script) {
+ setIsValidating(true);
+
+ const error = await runtimeFieldValidator({
+ type: data.type,
+ script: data.script,
+ });
+
+ setIsValidating(false);
+ setPainlessSyntaxError(error);
+
+ if (error) {
+ return;
+ }
+ }
+
+ onSave(data);
+ }
+ }, [onSave, submit, runtimeFieldValidator]);
+
+ const namesNotAllowed = useMemo(() => fields.map((fld) => fld.name), [fields]);
+
+ const existingConcreteFields = useMemo(() => {
+ const existing: Array<{ name: string; type: string }> = [];
+
+ fields
+ .filter((fld) => {
+ const isFieldBeingEdited = field?.name === fld.name;
+ return !isFieldBeingEdited && fld.isMapped;
+ })
+ .forEach((fld) => {
+ existing.push({
+ name: fld.name,
+ type: (fld.esTypes && fld.esTypes[0]) || '',
+ });
+ });
+
+ return existing;
+ }, [fields, field]);
+
+ const ctx = useMemo(
+ () => ({
+ fieldTypeToProcess,
+ namesNotAllowed,
+ existingConcreteFields,
+ }),
+ [fieldTypeToProcess, namesNotAllowed, existingConcreteFields]
+ );
+
+ return (
+ <>
+
+
+ {i18nTexts.flyoutTitle}
+
+
+
+
+ {FieldEditor && (
+
+ )}
+
+
+
+ {FieldEditor && (
+ <>
+ {isSubmitted && isSaveButtonDisabled && (
+ <>
+
+
+ >
+ )}
+
+
+
+ {i18nTexts.closeButtonLabel}
+
+
+
+
+
+ {i18nTexts.saveButtonLabel}
+
+
+
+ >
+ )}
+
+ >
+ );
+};
+
+export const FieldEditorFlyoutContent = React.memo(FieldEditorFlyoutContentComponent);
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx
new file mode 100644
index 00000000000000..ade25424c22509
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_editor_flyout_content_container.tsx
@@ -0,0 +1,205 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React, { useCallback, useEffect, useState, useMemo } from 'react';
+import { DocLinksStart, NotificationsStart, CoreStart } from 'src/core/public';
+import { i18n } from '@kbn/i18n';
+
+import {
+ IndexPatternField,
+ IndexPattern,
+ DataPublicPluginStart,
+ RuntimeType,
+ UsageCollectionStart,
+} from '../shared_imports';
+import { Field, PluginStart, InternalFieldType } from '../types';
+import { pluginName } from '../constants';
+import { deserializeField, getRuntimeFieldValidator } from '../lib';
+import { Props as FieldEditorProps } from './field_editor/field_editor';
+import { FieldEditorFlyoutContent } from './field_editor_flyout_content';
+
+export interface FieldEditorContext {
+ indexPattern: IndexPattern;
+ /**
+ * The Kibana field type of the field to create or edit
+ * Default: "runtime"
+ */
+ fieldTypeToProcess: InternalFieldType;
+ /** The search service from the data plugin */
+ search: DataPublicPluginStart['search'];
+}
+
+export interface Props {
+ /**
+ * Handler for the "save" footer button
+ */
+ onSave: (field: IndexPatternField) => void;
+ /**
+ * Handler for the "cancel" footer button
+ */
+ onCancel: () => void;
+ /**
+ * The docLinks start service from core
+ */
+ docLinks: DocLinksStart;
+ /**
+ * The context object specific to where the editor is currently being consumed
+ */
+ ctx: FieldEditorContext;
+ /**
+ * Optional field to edit
+ */
+ field?: IndexPatternField;
+ /**
+ * Services
+ */
+ indexPatternService: DataPublicPluginStart['indexPatterns'];
+ notifications: NotificationsStart;
+ fieldFormatEditors: PluginStart['fieldFormatEditors'];
+ fieldFormats: DataPublicPluginStart['fieldFormats'];
+ uiSettings: CoreStart['uiSettings'];
+ usageCollection: UsageCollectionStart;
+}
+
+/**
+ * The container component will be in charge of the communication with the index pattern service
+ * to retrieve/save the field in the saved object.
+ * The component is the presentational component that won't know
+ * anything about where a field comes from and where it should be persisted.
+ */
+
+export const FieldEditorFlyoutContentContainer = ({
+ field,
+ onSave,
+ onCancel,
+ docLinks,
+ indexPatternService,
+ ctx: { indexPattern, fieldTypeToProcess, search },
+ notifications,
+ fieldFormatEditors,
+ fieldFormats,
+ uiSettings,
+ usageCollection,
+}: Props) => {
+ const fieldToEdit = deserializeField(indexPattern, field);
+ const [Editor, setEditor] = useState | null>(null);
+ const [isSaving, setIsSaving] = useState(false);
+
+ const saveField = useCallback(
+ async (updatedField: Field) => {
+ setIsSaving(true);
+
+ const { script } = updatedField;
+
+ if (fieldTypeToProcess === 'runtime') {
+ try {
+ usageCollection.reportUiCounter(
+ pluginName,
+ usageCollection.METRIC_TYPE.COUNT,
+ 'save_runtime'
+ );
+ // eslint-disable-next-line no-empty
+ } catch {}
+ // rename an existing runtime field
+ if (field?.name && field.name !== updatedField.name) {
+ indexPattern.removeRuntimeField(field.name);
+ }
+
+ indexPattern.addRuntimeField(updatedField.name, {
+ type: updatedField.type as RuntimeType,
+ script,
+ });
+ } else {
+ try {
+ usageCollection.reportUiCounter(
+ pluginName,
+ usageCollection.METRIC_TYPE.COUNT,
+ 'save_concrete'
+ );
+ // eslint-disable-next-line no-empty
+ } catch {}
+ }
+
+ const editedField = indexPattern.getFieldByName(updatedField.name);
+
+ try {
+ if (!editedField) {
+ throw new Error(
+ `Unable to find field named '${updatedField.name}' on index pattern '${indexPattern.title}'`
+ );
+ }
+
+ indexPattern.setFieldCustomLabel(updatedField.name, updatedField.customLabel);
+ editedField.count = updatedField.popularity || 0;
+ if (updatedField.format) {
+ indexPattern.setFieldFormat(updatedField.name, updatedField.format);
+ } else {
+ indexPattern.deleteFieldFormat(updatedField.name);
+ }
+
+ await indexPatternService.updateSavedObject(indexPattern).then(() => {
+ const message = i18n.translate('indexPatternFieldEditor.deleteField.savedHeader', {
+ defaultMessage: "Saved '{fieldName}'",
+ values: { fieldName: updatedField.name },
+ });
+ notifications.toasts.addSuccess(message);
+ setIsSaving(false);
+ onSave(editedField);
+ });
+ } catch (e) {
+ const title = i18n.translate('indexPatternFieldEditor.save.errorTitle', {
+ defaultMessage: 'Failed to save field changes',
+ });
+ notifications.toasts.addError(e, { title });
+ setIsSaving(false);
+ }
+ },
+ [
+ onSave,
+ indexPattern,
+ indexPatternService,
+ notifications,
+ fieldTypeToProcess,
+ field?.name,
+ usageCollection,
+ ]
+ );
+
+ const validateRuntimeField = useMemo(() => getRuntimeFieldValidator(indexPattern.title, search), [
+ search,
+ indexPattern,
+ ]);
+
+ const loadEditor = useCallback(async () => {
+ const { FieldEditor } = await import('./field_editor');
+
+ setEditor(() => FieldEditor);
+ }, []);
+
+ useEffect(() => {
+ // On mount: load the editor asynchronously
+ loadEditor();
+ }, [loadEditor]);
+
+ return (
+
+ );
+};
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/__snapshots__/format_editor.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/__snapshots__/format_editor.test.tsx.snap
new file mode 100644
index 00000000000000..82d21eb5d30ada
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/__snapshots__/format_editor.test.tsx.snap
@@ -0,0 +1,25 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`FieldFormatEditor should render normally 1`] = `
+
+
+
+`;
+
+exports[`FieldFormatEditor should render nothing if there is no editor for the format 1`] = `
+
+
+
+`;
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap
similarity index 92%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap
index 69ea6c481d49b1..0f35267e1fb387 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/__snapshots__/bytes.test.tsx.snap
@@ -16,7 +16,7 @@ exports[`BytesFormatEditor should render normally 1`] = `
>
@@ -30,7 +30,7 @@ exports[`BytesFormatEditor should render normally 1`] = `
label={
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/bytes/bytes.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/bytes.test.tsx
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/bytes/bytes.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/bytes.test.tsx
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/bytes/bytes.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/bytes.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/bytes/bytes.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/bytes.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/bytes/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/bytes/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/bytes/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap
similarity index 87%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap
index c66e7789aa511e..c33bb57bfeac89 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/__snapshots__/color.test.tsx.snap
@@ -9,7 +9,7 @@ exports[`ColorFormatEditor should render multiple colors 1`] = `
"field": "regex",
"name": ,
"render": [Function],
@@ -18,7 +18,7 @@ exports[`ColorFormatEditor should render multiple colors 1`] = `
"field": "text",
"name": ,
"render": [Function],
@@ -27,7 +27,7 @@ exports[`ColorFormatEditor should render multiple colors 1`] = `
"field": "background",
"name": ,
"render": [Function],
@@ -35,7 +35,7 @@ exports[`ColorFormatEditor should render multiple colors 1`] = `
Object {
"name": ,
"render": [Function],
@@ -89,7 +89,7 @@ exports[`ColorFormatEditor should render multiple colors 1`] = `
>
@@ -108,7 +108,7 @@ exports[`ColorFormatEditor should render other type normally (range field) 1`] =
"field": "range",
"name": ,
"render": [Function],
@@ -117,7 +117,7 @@ exports[`ColorFormatEditor should render other type normally (range field) 1`] =
"field": "text",
"name": ,
"render": [Function],
@@ -126,7 +126,7 @@ exports[`ColorFormatEditor should render other type normally (range field) 1`] =
"field": "background",
"name": ,
"render": [Function],
@@ -134,7 +134,7 @@ exports[`ColorFormatEditor should render other type normally (range field) 1`] =
Object {
"name": ,
"render": [Function],
@@ -181,7 +181,7 @@ exports[`ColorFormatEditor should render other type normally (range field) 1`] =
>
@@ -200,7 +200,7 @@ exports[`ColorFormatEditor should render string type normally (regex field) 1`]
"field": "regex",
"name": ,
"render": [Function],
@@ -209,7 +209,7 @@ exports[`ColorFormatEditor should render string type normally (regex field) 1`]
"field": "text",
"name": ,
"render": [Function],
@@ -218,7 +218,7 @@ exports[`ColorFormatEditor should render string type normally (regex field) 1`]
"field": "background",
"name": ,
"render": [Function],
@@ -226,7 +226,7 @@ exports[`ColorFormatEditor should render string type normally (regex field) 1`]
Object {
"name": ,
"render": [Function],
@@ -273,7 +273,7 @@ exports[`ColorFormatEditor should render string type normally (regex field) 1`]
>
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/color.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.test.tsx
similarity index 96%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/color.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.test.tsx
index f0e7d4aea42c84..1026012f3b8878 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/color.test.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.test.tsx
@@ -11,7 +11,7 @@ import { shallowWithI18nProvider } from '@kbn/test/jest';
import { FieldFormat } from 'src/plugins/data/public';
import { ColorFormatEditor } from './color';
-import { fieldFormats } from '../../../../../../../../data/public';
+import { fieldFormats } from '../../../../../../data/public';
const fieldType = 'string';
const format = {
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/color.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx
similarity index 89%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/color.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx
index b169624fce9080..1e899a7179554f 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/color.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/color.tsx
@@ -14,7 +14,7 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { DefaultFormatEditor, FormatEditorProps } from '../default';
-import { fieldFormats } from '../../../../../../../../../plugins/data/public';
+import { fieldFormats } from '../../../../../../data/public';
interface Color {
range?: string;
@@ -86,7 +86,7 @@ export class ColorFormatEditor extends DefaultFormatEditor
),
@@ -110,7 +110,7 @@ export class ColorFormatEditor extends DefaultFormatEditor
),
@@ -134,7 +134,7 @@ export class ColorFormatEditor extends DefaultFormatEditor
),
@@ -158,7 +158,7 @@ export class ColorFormatEditor extends DefaultFormatEditor
),
@@ -181,7 +181,7 @@ export class ColorFormatEditor extends DefaultFormatEditor
),
@@ -200,15 +200,15 @@ export class ColorFormatEditor extends DefaultFormatEditor {
@@ -229,7 +229,7 @@ export class ColorFormatEditor extends DefaultFormatEditor
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/color/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/color/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date/__snapshots__/date.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/__snapshots__/date.test.tsx.snap
similarity index 93%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date/__snapshots__/date.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/__snapshots__/date.test.tsx.snap
index 48a7c3e013b1af..4560904c9b4c41 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date/__snapshots__/date.test.tsx.snap
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/__snapshots__/date.test.tsx.snap
@@ -16,7 +16,7 @@ exports[`DateFormatEditor should render normally 1`] = `
>
@@ -30,7 +30,7 @@ exports[`DateFormatEditor should render normally 1`] = `
label={
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date/date.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/date.test.tsx
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date/date.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/date.test.tsx
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date/date.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/date.tsx
similarity index 94%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date/date.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/date.tsx
index ae29c7a1236f57..62fb08855ce93e 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date/date.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/date.tsx
@@ -41,7 +41,7 @@ export class DateFormatEditor extends DefaultFormatEditor{defaultPattern},
@@ -54,7 +54,7 @@ export class DateFormatEditor extends DefaultFormatEditor
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.tsx.snap
similarity index 93%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.tsx.snap
index 540c8ece9e35b9..0d0962a281950c 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.tsx.snap
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/__snapshots__/date_nanos.test.tsx.snap
@@ -16,7 +16,7 @@ exports[`DateFormatEditor should render normally 1`] = `
>
@@ -30,7 +30,7 @@ exports[`DateFormatEditor should render normally 1`] = `
label={
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx
similarity index 95%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx
index 3f66dc59ab5697..4e8d56f91c6eb1 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/date_nanos.test.tsx
@@ -8,7 +8,7 @@
import React from 'react';
import { shallow } from 'enzyme';
-import { FieldFormat } from '../../../../../../../../data/public';
+import type { FieldFormat } from 'src/plugins/data/public';
import { DateNanosFormatEditor } from './date_nanos';
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/date_nanos.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/date_nanos.tsx
similarity index 94%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/date_nanos.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/date_nanos.tsx
index fab96322f0a16d..d9ee099aaef36a 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/date_nanos.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/date_nanos.tsx
@@ -40,7 +40,7 @@ export class DateNanosFormatEditor extends DefaultFormatEditor{defaultPattern},
@@ -53,7 +53,7 @@ export class DateNanosFormatEditor extends DefaultFormatEditor
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/date_nanos/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/date_nanos/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/default/__snapshots__/default.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/__snapshots__/default.test.tsx.snap
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/default/__snapshots__/default.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/__snapshots__/default.test.tsx.snap
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/default/default.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.test.tsx
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/default/default.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.test.tsx
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/default/default.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx
similarity index 92%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/default/default.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx
index 1bfc2c2fa340e1..06f3b318b6e935 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/default/default.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/default.tsx
@@ -10,8 +10,8 @@ import React, { PureComponent, ReactText } from 'react';
import { i18n } from '@kbn/i18n';
import { FieldFormat, FieldFormatsContentType } from 'src/plugins/data/public';
-import { Sample } from '../../../../types';
-import { FieldFormatEditorProps } from '../../field_format_editor';
+import { Sample } from '../../types';
+import { FormatSelectEditorProps } from '../../field_format_editor';
export type ConverterParams = string | number | Array;
@@ -30,7 +30,7 @@ export const convertSampleInput = (
};
});
} catch (e) {
- error = i18n.translate('indexPatternManagement.defaultErrorMessage', {
+ error = i18n.translate('indexPatternFieldEditor.defaultErrorMessage', {
defaultMessage: 'An error occurred while trying to use this format configuration: {message}',
values: { message: e.message },
});
@@ -51,7 +51,7 @@ export interface FormatEditorProps {
format: FieldFormat;
formatParams: { type?: string } & P;
onChange: (newParams: Record) => void;
- onError: FieldFormatEditorProps['onError'];
+ onError: FormatSelectEditorProps['onError'];
}
export interface FormatEditorState {
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/default/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/default/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/default/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap
similarity index 93%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap
index c617c3b43039bf..cb7949deda64f6 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/__snapshots__/duration.test.tsx.snap
@@ -12,7 +12,7 @@ exports[`DurationFormatEditor should render human readable output normally 1`] =
label={
}
@@ -42,7 +42,7 @@ exports[`DurationFormatEditor should render human readable output normally 1`] =
label={
}
@@ -124,7 +124,7 @@ exports[`DurationFormatEditor should render non-human readable output normally 1
label={
}
@@ -154,7 +154,7 @@ exports[`DurationFormatEditor should render non-human readable output normally 1
label={
}
@@ -189,7 +189,7 @@ exports[`DurationFormatEditor should render non-human readable output normally 1
label={
}
@@ -216,7 +216,7 @@ exports[`DurationFormatEditor should render non-human readable output normally 1
label={
}
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/duration/duration.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.test.tsx
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/duration/duration.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.test.tsx
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/duration/duration.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx
similarity index 93%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/duration/duration.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx
index 4842c7066a2ef8..de413d02c5011c 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/duration/duration.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/duration.tsx
@@ -65,7 +65,7 @@ export class DurationFormatEditor extends DefaultFormatEditor<
!(nextProps.format as DurationFormat).isHuman() &&
nextProps.formatParams.outputPrecision > 20
) {
- error = i18n.translate('indexPatternManagement.durationErrorMessage', {
+ error = i18n.translate('indexPatternFieldEditor.durationErrorMessage', {
defaultMessage: 'Decimal places must be between 0 and 20',
});
nextProps.onError(error);
@@ -91,7 +91,7 @@ export class DurationFormatEditor extends DefaultFormatEditor<
}
@@ -115,7 +115,7 @@ export class DurationFormatEditor extends DefaultFormatEditor<
}
@@ -140,7 +140,7 @@ export class DurationFormatEditor extends DefaultFormatEditor<
}
@@ -163,7 +163,7 @@ export class DurationFormatEditor extends DefaultFormatEditor<
}
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/duration/index.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/index.tsx
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/duration/index.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/duration/index.tsx
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap
similarity index 92%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap
index c73b5e7186547f..3cac3850548351 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/__snapshots__/number.test.tsx.snap
@@ -16,7 +16,7 @@ exports[`NumberFormatEditor should render normally 1`] = `
>
@@ -30,7 +30,7 @@ exports[`NumberFormatEditor should render normally 1`] = `
label={
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/number/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/number/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/number/number.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.test.tsx
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/number/number.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.test.tsx
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/number/number.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.tsx
similarity index 94%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/number/number.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.tsx
index 250bbe570a9c4f..2aeb90373bfaba 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/number/number.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/number/number.tsx
@@ -36,7 +36,7 @@ export class NumberFormatEditor extends DefaultFormatEditor{defaultPattern} }}
/>
@@ -45,7 +45,7 @@ export class NumberFormatEditor extends DefaultFormatEditor
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap
similarity index 92%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap
index 16ce8ca9643ef2..f6af1f0dff7fe3 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/__snapshots__/percent.test.tsx.snap
@@ -16,7 +16,7 @@ exports[`PercentFormatEditor should render normally 1`] = `
>
@@ -30,7 +30,7 @@ exports[`PercentFormatEditor should render normally 1`] = `
label={
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/percent/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/percent/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/percent/percent.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/percent.test.tsx
similarity index 95%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/percent/percent.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/percent.test.tsx
index 6eff2fd279cbb5..072dc0caeb3c8b 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/percent/percent.test.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/percent.test.tsx
@@ -8,7 +8,7 @@
import React from 'react';
import { shallow } from 'enzyme';
-import { FieldFormat } from '../../../../../../../../data/public';
+import { FieldFormat } from 'src/plugins/data/public';
import { PercentFormatEditor } from './percent';
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/percent/percent.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/percent.tsx
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/percent/percent.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/percent/percent.tsx
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap
similarity index 88%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap
index 46267b0c3c0e98..c5697cb699eb7a 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/__snapshots__/static_lookup.test.tsx.snap
@@ -9,7 +9,7 @@ exports[`StaticLookupFormatEditor should render multiple lookup entries and unkn
"field": "key",
"name": ,
"render": [Function],
@@ -18,7 +18,7 @@ exports[`StaticLookupFormatEditor should render multiple lookup entries and unkn
"field": "value",
"name": ,
"render": [Function],
@@ -73,7 +73,7 @@ exports[`StaticLookupFormatEditor should render multiple lookup entries and unkn
>
@@ -89,7 +89,7 @@ exports[`StaticLookupFormatEditor should render multiple lookup entries and unkn
label={
}
@@ -116,7 +116,7 @@ exports[`StaticLookupFormatEditor should render normally 1`] = `
"field": "key",
"name": ,
"render": [Function],
@@ -125,7 +125,7 @@ exports[`StaticLookupFormatEditor should render normally 1`] = `
"field": "value",
"name": ,
"render": [Function],
@@ -174,7 +174,7 @@ exports[`StaticLookupFormatEditor should render normally 1`] = `
>
@@ -190,7 +190,7 @@ exports[`StaticLookupFormatEditor should render normally 1`] = `
label={
}
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx
similarity index 96%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx
index e24b656267d1aa..8d9cb17b33a403 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.test.tsx
@@ -9,7 +9,7 @@
import React from 'react';
import { shallowWithI18nProvider } from '@kbn/test/jest';
import { StaticLookupFormatEditorFormatParams } from './static_lookup';
-import { FieldFormat } from '../../../../../../../../data/public';
+import { FieldFormat } from 'src/plugins/data/public';
import { StaticLookupFormatEditor } from './static_lookup';
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/static_lookup.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.tsx
similarity index 88%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/static_lookup.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.tsx
index 8c49615c99f6c2..8ac03bb23bd25a 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/static_lookup/static_lookup.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/static_lookup/static_lookup.tsx
@@ -72,7 +72,7 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor
),
@@ -96,7 +96,7 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor
),
@@ -118,15 +118,15 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor {
@@ -148,7 +148,7 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor
@@ -156,7 +156,7 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor
}
@@ -164,7 +164,7 @@ export class StaticLookupFormatEditor extends DefaultFormatEditor
}
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/string.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.test.tsx
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/string.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.test.tsx
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/string.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.tsx
similarity index 96%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/string.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.tsx
index 6f9e0e10e188a4..e86a62775cebcb 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/string/string.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/string/string.tsx
@@ -47,7 +47,7 @@ export class StringFormatEditor extends DefaultFormatEditor
}
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap
similarity index 96%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap
index 2d1ee496d2786b..f982632bba5235 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/__snapshots__/truncate.test.tsx.snap
@@ -12,7 +12,7 @@ exports[`TruncateFormatEditor should render normally 1`] = `
label={
}
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/sample.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/sample.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/sample.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/sample.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/truncate.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.test.tsx
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/truncate.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.test.tsx
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/truncate.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.tsx
similarity index 96%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/truncate.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.tsx
index 4b24d33e58f3ce..03b7d6e0573cc5 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/truncate/truncate.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/truncate/truncate.tsx
@@ -37,7 +37,7 @@ export class TruncateFormatEditor extends DefaultFormatEditor
}
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap
similarity index 92%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap
index b627dbe0576ee2..bc5efb8f5eda42 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/__snapshots__/url.test.tsx.snap
@@ -166,14 +166,26 @@ exports[`UrlFormatEditor should render normally 1`] = `
class="euiFormHelpText euiFormRow__text"
id="generated-id-help"
>
-
+
+
+ (opens in a new tab or window)
+
+
@@ -217,14 +229,26 @@ exports[`UrlFormatEditor should render normally 1`] = `
class="euiFormHelpText euiFormRow__text"
id="generated-id-help"
>
-
+
+
+ (opens in a new tab or window)
+
+
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/index.ts b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/index.ts
similarity index 100%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/index.ts
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/index.ts
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.test.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.test.tsx
similarity index 75%
rename from src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.test.tsx
rename to src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.test.tsx
index 5c86abc3b4a9c1..9f299a433aab1a 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url.test.tsx
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/editors/url/url.test.tsx
@@ -10,8 +10,8 @@ import React from 'react';
import { FieldFormat } from 'src/plugins/data/public';
import { IntlProvider } from 'react-intl';
import { UrlFormatEditor } from './url';
-import { coreMock } from '../../../../../../../../../core/public/mocks';
-import { createKibanaReactContext } from '../../../../../../../../kibana_react/public';
+import { coreMock } from 'src/core/public/mocks';
+import { createKibanaReactContext } from '../../../../../../kibana_react/public';
import { render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
@@ -76,38 +76,6 @@ describe('UrlFormatEditor', () => {
expect(container).toMatchSnapshot();
});
- it('should render url template help', async () => {
- const { getByText, getByTestId } = renderWithContext(
-
- );
-
- getByText('URL template help');
- userEvent.click(getByText('URL template help'));
- expect(getByTestId('urlTemplateFlyoutTestSubj')).toBeVisible();
- });
-
- it('should render label template help', async () => {
- const { getByText, getByTestId } = renderWithContext(
-
- );
-
- getByText('Label template help');
- userEvent.click(getByText('Label template help'));
- expect(getByTestId('labelTemplateFlyoutTestSubj')).toBeVisible();
- });
-
it('should render width and height fields if image', async () => {
const { getByLabelText } = renderWithContext(
{
static contextType = contextType;
static formatId = 'url';
- // TODO: @kbn/optimizer can't compile this
- // declare context: IndexPatternManagmentContextValue;
- context: IndexPatternManagmentContextValue | undefined;
private get sampleIconPath() {
const sampleIconPath = `/plugins/indexPatternManagement/assets/icons/{{value}}.png`;
return this.context?.services.http
@@ -110,32 +103,6 @@ export class UrlFormatEditor extends DefaultFormatEditor<
this.onChange(params);
};
- showUrlTemplateHelp = () => {
- this.setState({
- showLabelTemplateHelp: false,
- showUrlTemplateHelp: true,
- });
- };
-
- hideUrlTemplateHelp = () => {
- this.setState({
- showUrlTemplateHelp: false,
- });
- };
-
- showLabelTemplateHelp = () => {
- this.setState({
- showLabelTemplateHelp: true,
- showUrlTemplateHelp: false,
- });
- };
-
- hideLabelTemplateHelp = () => {
- this.setState({
- showLabelTemplateHelp: false,
- });
- };
-
renderWidthHeightParameters = () => {
const width = this.sanitizeNumericValue(this.props.formatParams.width);
const height = this.sanitizeNumericValue(this.props.formatParams.height);
@@ -143,7 +110,7 @@ export class UrlFormatEditor extends DefaultFormatEditor<
+
}
>
+
}
>
-
-
+
}
>
}
@@ -217,9 +179,12 @@ export class UrlFormatEditor extends DefaultFormatEditor<
+
) : (
-
+
)
}
checked={!formatParams.openLinkInCurrentTab}
@@ -233,14 +198,17 @@ export class UrlFormatEditor extends DefaultFormatEditor<
}
helpText={
-
+
@@ -260,14 +228,17 @@ export class UrlFormatEditor extends DefaultFormatEditor<
}
helpText={
-
+
diff --git a/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx
new file mode 100644
index 00000000000000..1f3e87e69fd4c2
--- /dev/null
+++ b/src/plugins/index_pattern_field_editor/public/components/field_format_editor/field_format_editor.tsx
@@ -0,0 +1,186 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+import React, { PureComponent } from 'react';
+import { EuiCode, EuiFormRow, EuiSelect } from '@elastic/eui';
+
+import { FormattedMessage } from '@kbn/i18n/react';
+import { i18n } from '@kbn/i18n';
+import {
+ FieldFormatInstanceType,
+ IndexPattern,
+ KBN_FIELD_TYPES,
+ ES_FIELD_TYPES,
+ DataPublicPluginStart,
+ FieldFormat,
+} from 'src/plugins/data/public';
+import { CoreStart } from 'src/core/public';
+import { castEsToKbnFieldTypeName } from '../../../../data/public';
+import { FormatEditor } from './format_editor';
+import { FormatEditorServiceStart } from '../../service';
+import { FieldFormatConfig } from '../../types';
+
+export interface FormatSelectEditorProps {
+ esTypes: ES_FIELD_TYPES[];
+ indexPattern: IndexPattern;
+ fieldFormatEditors: FormatEditorServiceStart['fieldFormatEditors'];
+ fieldFormats: DataPublicPluginStart['fieldFormats'];
+ uiSettings: CoreStart['uiSettings'];
+ onChange: (change?: FieldFormatConfig) => void;
+ onError: (error?: string) => void;
+ value?: FieldFormatConfig;
+}
+
+interface FieldTypeFormat {
+ id: string;
+ title: string;
+}
+
+export interface FormatSelectEditorState {
+ fieldTypeFormats: FieldTypeFormat[];
+ fieldFormatId?: string;
+ fieldFormatParams?: { [key: string]: unknown };
+ format: FieldFormat;
+ kbnType: KBN_FIELD_TYPES;
+}
+
+interface InitialFieldTypeFormat extends FieldTypeFormat {
+ defaultFieldFormat: FieldFormatInstanceType;
+}
+
+const getFieldTypeFormatsList = (
+ fieldType: KBN_FIELD_TYPES,
+ defaultFieldFormat: FieldFormatInstanceType,
+ fieldFormats: DataPublicPluginStart['fieldFormats']
+) => {
+ const formatsByType = fieldFormats.getByFieldType(fieldType).map(({ id, title }) => ({
+ id,
+ title,
+ }));
+
+ return [
+ {
+ id: '',
+ defaultFieldFormat,
+ title: i18n.translate('indexPatternFieldEditor.defaultFormatDropDown', {
+ defaultMessage: '- Default -',
+ }),
+ },
+ ...formatsByType,
+ ];
+};
+
+export class FormatSelectEditor extends PureComponent<
+ FormatSelectEditorProps,
+ FormatSelectEditorState
+> {
+ constructor(props: FormatSelectEditorProps) {
+ super(props);
+ const { fieldFormats, esTypes, value } = props;
+ const kbnType = castEsToKbnFieldTypeName(esTypes[0] || 'keyword');
+
+ // get current formatter for field, provides default if none exists
+ const format = value?.id
+ ? fieldFormats.getInstance(value?.id, value?.params)
+ : fieldFormats.getDefaultInstance(kbnType, esTypes);
+
+ this.state = {
+ fieldTypeFormats: getFieldTypeFormatsList(
+ kbnType,
+ fieldFormats.getDefaultType(kbnType, esTypes) as FieldFormatInstanceType,
+ fieldFormats
+ ),
+ format,
+ kbnType,
+ };
+ }
+ onFormatChange = (formatId: string, params?: any) => {
+ const { fieldTypeFormats } = this.state;
+ const { fieldFormats, uiSettings } = this.props;
+
+ const FieldFormatClass = fieldFormats.getType(
+ formatId || (fieldTypeFormats[0] as InitialFieldTypeFormat).defaultFieldFormat.id
+ ) as FieldFormatInstanceType;
+
+ const newFormat = new FieldFormatClass(params, (key: string) => uiSettings.get(key));
+
+ this.setState(
+ {
+ fieldFormatId: formatId,
+ fieldFormatParams: params,
+ format: newFormat,
+ },
+ () => {
+ this.props.onChange(
+ formatId
+ ? {
+ id: formatId,
+ params: params || {},
+ }
+ : undefined
+ );
+ }
+ );
+ };
+ onFormatParamsChange = (newParams: { fieldType: string; [key: string]: any }) => {
+ const { fieldFormatId } = this.state;
+ this.onFormatChange(fieldFormatId as string, newParams);
+ };
+
+ render() {
+ const { fieldFormatEditors, onError, value } = this.props;
+ const fieldFormatId = value?.id;
+ const fieldFormatParams = value?.params;
+ const { kbnType } = this.state;
+
+ const { fieldTypeFormats, format } = this.state;
+
+ const defaultFormat = (fieldTypeFormats[0] as InitialFieldTypeFormat).defaultFieldFormat.title;
+
+ const label = defaultFormat ? (
+
{conflictedFields.length > 0 && (
@@ -203,6 +207,9 @@ export const EditIndexPattern = withRouter(
fields={fields}
history={history}
location={location}
+ refreshFields={() => {
+ setFields(indexPattern.getNonScriptedFields());
+ }}
/>
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap
index 8e7fac9c6c1483..6e5e652b8d0eb3 100644
--- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap
+++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/__snapshots__/indexed_fields_table.test.tsx.snap
@@ -3,6 +3,7 @@
exports[`IndexedFieldsTable should filter based on the query bar 1`] = `
`;
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx
index c5ef40be9c0657..9c154ce1b0e7ba 100644
--- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx
+++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.test.tsx
@@ -23,99 +23,84 @@ const items: IndexedFieldItem[] = [
searchable: true,
info: [],
type: 'name',
+ kbnType: 'string',
excluded: false,
format: '',
+ isMapped: true,
},
{
name: 'timestamp',
displayName: 'timestamp',
type: 'date',
+ kbnType: 'date',
info: [],
excluded: false,
format: 'YYYY-MM-DD',
+ isMapped: true,
},
{
name: 'conflictingField',
displayName: 'conflictingField',
- type: 'conflict',
+ type: 'text, long',
+ kbnType: 'conflict',
info: [],
excluded: false,
format: '',
+ isMapped: true,
},
];
+const renderTable = (
+ { editField } = {
+ editField: () => {},
+ }
+) =>
+ shallow(
+ {}} />
+ );
+
describe('Table', () => {
test('should render normally', () => {
- const component = shallow(
- {}} />
- );
-
- expect(component).toMatchSnapshot();
+ expect(renderTable()).toMatchSnapshot();
});
test('should render normal field name', () => {
- const component = shallow(
- {}} />
- );
-
- const tableCell = shallow(component.prop('columns')[0].render('Elastic', items[0]));
+ const tableCell = shallow(renderTable().prop('columns')[0].render('Elastic', items[0]));
expect(tableCell).toMatchSnapshot();
});
test('should render timestamp field name', () => {
- const component = shallow(
- {}} />
- );
-
- const tableCell = shallow(component.prop('columns')[0].render('timestamp', items[1]));
+ const tableCell = shallow(renderTable().prop('columns')[0].render('timestamp', items[1]));
expect(tableCell).toMatchSnapshot();
});
test('should render the boolean template (true)', () => {
- const component = shallow(
- {}} />
- );
-
- const tableCell = shallow(component.prop('columns')[3].render(true));
+ const tableCell = shallow(renderTable().prop('columns')[3].render(true));
expect(tableCell).toMatchSnapshot();
});
test('should render the boolean template (false)', () => {
- const component = shallow(
- {}} />
- );
-
- const tableCell = shallow(component.prop('columns')[3].render(false, items[2]));
+ const tableCell = shallow(renderTable().prop('columns')[3].render(false, items[2]));
expect(tableCell).toMatchSnapshot();
});
test('should render normal type', () => {
- const component = shallow(
- {}} />
- );
-
- const tableCell = shallow(component.prop('columns')[1].render('string'));
+ const tableCell = shallow(renderTable().prop('columns')[1].render('string', {}));
expect(tableCell).toMatchSnapshot();
});
test('should render conflicting type', () => {
- const component = shallow(
- {}} />
+ const tableCell = shallow(
+ renderTable().prop('columns')[1].render('conflict', { kbnType: 'conflict' })
);
-
- const tableCell = shallow(component.prop('columns')[1].render('conflict', true));
expect(tableCell).toMatchSnapshot();
});
test('should allow edits', () => {
const editField = jest.fn();
- const component = shallow(
-
- );
-
// Click the edit button
- component.prop('columns')[6].actions[0].onClick();
+ renderTable({ editField }).prop('columns')[6].actions[0].onClick();
expect(editField).toBeCalled();
});
});
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx
index 58080722d1bced..4e9a2bb6451125 100644
--- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx
+++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/components/table/table.tsx
@@ -140,6 +140,18 @@ const editDescription = i18n.translate(
{ defaultMessage: 'Edit' }
);
+const deleteLabel = i18n.translate(
+ 'indexPatternManagement.editIndexPattern.fields.table.deleteLabel',
+ {
+ defaultMessage: 'Delete',
+ }
+);
+
+const deleteDescription = i18n.translate(
+ 'indexPatternManagement.editIndexPattern.fields.table.deleteDescription',
+ { defaultMessage: 'Delete' }
+);
+
const labelDescription = i18n.translate(
'indexPatternManagement.editIndexPattern.fields.table.customLabelTooltip',
{ defaultMessage: 'A custom label for the field.' }
@@ -149,6 +161,7 @@ interface IndexedFieldProps {
indexPattern: IIndexPattern;
items: IndexedFieldItem[];
editField: (field: IndexedFieldItem) => void;
+ deleteField: (fieldName: string) => void;
}
export class Table extends PureComponent {
@@ -221,7 +234,7 @@ export class Table extends PureComponent {
}
render() {
- const { items, editField } = this.props;
+ const { items, editField, deleteField } = this.props;
const pagination = {
initialPageSize: 10,
@@ -245,8 +258,8 @@ export class Table extends PureComponent {
name: typeHeader,
dataType: 'string',
sortable: true,
- render: (value: string) => {
- return this.renderFieldType(value, value === 'conflict');
+ render: (value: string, field: IndexedFieldItem) => {
+ return this.renderFieldType(value, field.kbnType === 'conflict');
},
'data-test-subj': 'indexedFieldType',
},
@@ -294,10 +307,30 @@ export class Table extends PureComponent {
],
width: '40px',
},
+ {
+ name: '',
+ actions: [
+ {
+ name: deleteLabel,
+ description: deleteDescription,
+ icon: 'trash',
+ onClick: (field) => deleteField(field.name),
+ type: 'icon',
+ 'data-test-subj': 'deleteField',
+ available: (field) => !field.isMapped,
+ },
+ ],
+ width: '40px',
+ },
];
return (
-
+
);
}
}
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx
index fb9b33114ad0b7..e587ada6695cb4 100644
--- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx
+++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.test.tsx
@@ -10,7 +10,6 @@ import React from 'react';
import { shallow } from 'enzyme';
import { IndexPatternField, IndexPattern } from 'src/plugins/data/public';
import { IndexedFieldsTable } from './indexed_fields_table';
-import { IndexedFieldItem } from './types';
jest.mock('@elastic/eui', () => ({
EuiFlexGroup: 'eui-flex-group',
@@ -27,7 +26,8 @@ jest.mock('./components/table', () => ({
}));
const helpers = {
- redirectToRoute: (field: IndexedFieldItem) => {},
+ editField: (fieldName: string) => {},
+ deleteField: (fieldName: string) => {},
getFieldInfo: () => [],
};
@@ -36,7 +36,9 @@ const indexPattern = ({
getFormatterForFieldNoDefault: () => ({ params: () => ({}) }),
} as unknown) as IndexPattern;
-const mockFieldToIndexPatternField = (spec: Record) => {
+const mockFieldToIndexPatternField = (
+ spec: Record
+) => {
return new IndexPatternField((spec as unknown) as IndexPatternField['spec']);
};
@@ -45,10 +47,10 @@ const fields = [
name: 'Elastic',
displayName: 'Elastic',
searchable: true,
- type: 'string',
+ esTypes: ['keyword'],
},
- { name: 'timestamp', displayName: 'timestamp', type: 'date' },
- { name: 'conflictingField', displayName: 'conflictingField', type: 'conflict' },
+ { name: 'timestamp', displayName: 'timestamp', esTypes: ['date'] },
+ { name: 'conflictingField', displayName: 'conflictingField', esTypes: ['keyword', 'long'] },
].map(mockFieldToIndexPatternField);
describe('IndexedFieldsTable', () => {
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx
index 95458b55dbf2a7..c703a882d38d6a 100644
--- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx
+++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/indexed_fields_table.tsx
@@ -18,7 +18,8 @@ interface IndexedFieldsTableProps {
fieldFilter?: string;
indexedFieldTypeFilter?: string;
helpers: {
- redirectToRoute: (obj: any) => void;
+ editField: (fieldName: string) => void;
+ deleteField: (fieldName: string) => void;
getFieldInfo: (indexPattern: IndexPattern, field: IFieldType) => string[];
};
fieldWildcardMatcher: (filters: any[]) => (val: any) => boolean;
@@ -60,10 +61,13 @@ export class IndexedFieldsTable extends Component<
fields.map((field) => {
return {
...field.spec,
+ type: field.esTypes?.join(', ') || '',
+ kbnType: field.type,
displayName: field.displayName,
format: indexPattern.getFormatterForFieldNoDefault(field.name)?.type?.title || '',
excluded: fieldWildcardMatch ? fieldWildcardMatch(field.name) : false,
info: helpers.getFieldInfo && helpers.getFieldInfo(indexPattern, field),
+ isMapped: !!field.isMapped,
};
})) ||
[]
@@ -102,7 +106,8 @@ export class IndexedFieldsTable extends Component<
this.props.helpers.redirectToRoute(field)}
+ editField={(field) => this.props.helpers.editField(field.name)}
+ deleteField={(fieldName) => this.props.helpers.deleteField(fieldName)}
/>
);
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/types.ts b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/types.ts
index dee8f5b0d775f9..47ae84b4d2fd8f 100644
--- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/types.ts
+++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/indexed_fields_table/types.ts
@@ -11,4 +11,6 @@ import { IFieldType } from '../../../../../../plugins/data/public';
export interface IndexedFieldItem extends IFieldType {
info: string[];
excluded: boolean;
+ kbnType: string;
+ isMapped: boolean;
}
diff --git a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx
index ac57c6ffd78ed9..7771c5d54f4157 100644
--- a/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx
+++ b/src/plugins/index_pattern_management/public/components/edit_index_pattern/tabs/tabs.tsx
@@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
-import React, { useState, useCallback, useEffect, Fragment, useMemo } from 'react';
+import React, { useState, useCallback, useEffect, Fragment, useMemo, useRef } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import {
EuiFlexGroup,
@@ -17,6 +17,7 @@ import {
EuiFieldSearch,
EuiSelect,
EuiSelectOption,
+ EuiButton,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { fieldWildcardMatcher } from '../../../../../kibana_utils/public';
@@ -39,6 +40,7 @@ interface TabsProps extends Pick {
indexPattern: IndexPattern;
fields: IndexPatternField[];
saveIndexPattern: DataPublicPluginStart['indexPatterns']['updateSavedObject'];
+ refreshFields: () => void;
}
const searchAriaLabel = i18n.translate(
@@ -62,11 +64,26 @@ const filterPlaceholder = i18n.translate(
}
);
-export function Tabs({ indexPattern, saveIndexPattern, fields, history, location }: TabsProps) {
+const addFieldButtonLabel = i18n.translate(
+ 'indexPatternManagement.editIndexPattern.fields.addFieldButtonLabel',
+ {
+ defaultMessage: 'Add field',
+ }
+);
+
+export function Tabs({
+ indexPattern,
+ saveIndexPattern,
+ fields,
+ history,
+ location,
+ refreshFields,
+}: TabsProps) {
const {
uiSettings,
indexPatternManagementStart,
docLinks,
+ indexPatternFieldEditor,
} = useKibana().services;
const [fieldFilter, setFieldFilter] = useState('');
const [indexedFieldTypeFilter, setIndexedFieldTypeFilter] = useState('');
@@ -76,6 +93,8 @@ export function Tabs({ indexPattern, saveIndexPattern, fields, history, location
const [syncingStateFunc, setSyncingStateFunc] = useState({
getCurrentTab: () => TAB_INDEXED_FIELDS,
});
+ const closeEditorHandler = useRef<() => void | undefined>();
+ const { DeleteRuntimeFieldProvider } = indexPatternFieldEditor;
const refreshFilters = useCallback(() => {
const tempIndexedFieldTypes: string[] = [];
@@ -86,7 +105,9 @@ export function Tabs({ indexPattern, saveIndexPattern, fields, history, location
tempScriptedFieldLanguages.push(field.lang);
}
} else {
- tempIndexedFieldTypes.push(field.type);
+ if (field.esTypes) {
+ tempIndexedFieldTypes.push(field.esTypes?.join(', '));
+ }
}
});
@@ -96,10 +117,36 @@ export function Tabs({ indexPattern, saveIndexPattern, fields, history, location
);
}, [indexPattern]);
+ const closeFieldEditor = useCallback(() => {
+ if (closeEditorHandler.current) {
+ closeEditorHandler.current();
+ }
+ }, []);
+
+ const openFieldEditor = useCallback(
+ (fieldName?: string) => {
+ closeEditorHandler.current = indexPatternFieldEditor.openEditor({
+ ctx: {
+ indexPattern,
+ },
+ onSave: refreshFields,
+ fieldName,
+ });
+ },
+ [indexPatternFieldEditor, indexPattern, refreshFields]
+ );
+
useEffect(() => {
refreshFilters();
}, [indexPattern, indexPattern.fields, refreshFilters]);
+ useEffect(() => {
+ return () => {
+ // When the component unmounts, make sure to close the field editor
+ closeFieldEditor();
+ };
+ }, [closeFieldEditor]);
+
const fieldWildcardMatcherDecorated = useCallback(
(filters: string[]) => fieldWildcardMatcher(filters, uiSettings.get(UI_SETTINGS.META_FIELDS)),
[uiSettings]
@@ -120,15 +167,22 @@ export function Tabs({ indexPattern, saveIndexPattern, fields, history, location
/>
{type === TAB_INDEXED_FIELDS && indexedFieldTypes.length > 0 && (
-
- setIndexedFieldTypeFilter(e.target.value)}
- data-test-subj="indexedFieldTypeFilterDropdown"
- aria-label={filterAriaLabel}
- />
-
+ <>
+
+ setIndexedFieldTypeFilter(e.target.value)}
+ data-test-subj="indexedFieldTypeFilterDropdown"
+ aria-label={filterAriaLabel}
+ />
+
+
+ openFieldEditor()}>
+ {addFieldButtonLabel}
+
+
+ >
)}
{type === TAB_SCRIPTED_FIELDS && scriptedFieldLanguages.length > 0 && (
@@ -149,6 +203,7 @@ export function Tabs({ indexPattern, saveIndexPattern, fields, history, location
indexedFieldTypes,
scriptedFieldLanguageFilter,
scriptedFieldLanguages,
+ openFieldEditor,
]
);
@@ -161,19 +216,22 @@ export function Tabs({ indexPattern, saveIndexPattern, fields, history, location
{getFilterSection(type)}
- {
- history.push(getPath(field, indexPattern));
- },
- getFieldInfo: indexPatternManagementStart.list.getFieldInfo,
- }}
- />
+
+ {(deleteField) => (
+
+ )}
+
);
case TAB_SCRIPTED_FIELDS:
@@ -227,6 +285,9 @@ export function Tabs({ indexPattern, saveIndexPattern, fields, history, location
refreshFilters,
scriptedFieldLanguageFilter,
saveIndexPattern,
+ openFieldEditor,
+ DeleteRuntimeFieldProvider,
+ refreshFields,
]
);
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap
deleted file mode 100644
index 38f630358d064c..00000000000000
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/label_template_flyout.test.tsx.snap
+++ /dev/null
@@ -1,109 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`LabelTemplateFlyout should not render if not visible 1`] = `""`;
-
-exports[`LabelTemplateFlyout should render normally 1`] = `
-
-
-
-
-
-
-
-
- {{ }}
- ,
- }
- }
- />
-
-
- -
-
- value
-
- —
-
-
- -
-
- url
-
- —
-
-
-
-
-
-
- User #1234",
- "urlTemplate": "http://company.net/profiles?user_id={{value}}",
- },
- Object {
- "input": "/assets/main.css",
- "labelTemplate": "View Asset",
- "output": "View Asset",
- "urlTemplate": "http://site.com{{rawValue}}",
- },
- ]
- }
- noItemsMessage="No items found"
- responsive={true}
- tableLayout="fixed"
- />
-
-
-
-`;
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap
deleted file mode 100644
index 83e815dd72661c..00000000000000
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/__snapshots__/url_template_flyout.test.tsx.snap
+++ /dev/null
@@ -1,114 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`UrlTemplateFlyout should not render if not visible 1`] = `""`;
-
-exports[`UrlTemplateFlyout should render normally 1`] = `
-
-
-
-
-
-
-
-
- {{ }}
- ,
- "strongUrlTemplate":
-
- ,
- }
- }
- />
-
-
- -
-
- value
-
- —
-
-
- -
-
- rawValue
-
- —
-
-
-
-
-
-
-
-
-
-
-`;
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.test.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.test.tsx
deleted file mode 100644
index 0af6ba062e86b8..00000000000000
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.test.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-import React from 'react';
-import { shallowWithI18nProvider } from '@kbn/test/jest';
-
-import { LabelTemplateFlyout } from './label_template_flyout';
-
-describe('LabelTemplateFlyout', () => {
- it('should render normally', async () => {
- const component = shallowWithI18nProvider();
- expect(component).toMatchSnapshot();
- });
-
- it('should not render if not visible', async () => {
- const component = shallowWithI18nProvider();
- expect(component).toMatchSnapshot();
- });
-});
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.tsx
deleted file mode 100644
index 0ce1858a5cc44c..00000000000000
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/label_template_flyout.tsx
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-import React from 'react';
-
-import { EuiBasicTable, EuiCode, EuiFlyout, EuiFlyoutBody, EuiText } from '@elastic/eui';
-
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-
-interface LabelTemplateExampleItem {
- input: string | number;
- urlTemplate: string;
- labelTemplate: string;
- output: string;
-}
-
-const items: LabelTemplateExampleItem[] = [
- {
- input: 1234,
- urlTemplate: 'http://company.net/profiles?user_id={{value}}',
- labelTemplate: i18n.translate('indexPatternManagement.labelTemplate.example.idLabel', {
- defaultMessage: 'User #{value}',
- values: { value: '{{value}}' },
- }),
- output:
- '' +
- i18n.translate('indexPatternManagement.labelTemplate.example.output.idLabel', {
- defaultMessage: 'User',
- }) +
- ' #1234',
- },
- {
- input: '/assets/main.css',
- urlTemplate: 'http://site.com{{rawValue}}',
- labelTemplate: i18n.translate('indexPatternManagement.labelTemplate.example.pathLabel', {
- defaultMessage: 'View Asset',
- }),
- output:
- '' +
- i18n.translate('indexPatternManagement.labelTemplate.example.output.pathLabel', {
- defaultMessage: 'View Asset',
- }) +
- '',
- },
-];
-
-export const LabelTemplateFlyout = ({ isVisible = false, onClose = () => {} }) => {
- return isVisible ? (
-
-
-
-
-
-
-
- {'{{ }}'} }}
- />
-
-
- -
- value —
-
-
- -
- url —
-
-
-
-
-
-
-
- items={items}
- columns={[
- {
- field: 'input',
- name: i18n.translate('indexPatternManagement.labelTemplate.inputHeader', {
- defaultMessage: 'Input',
- }),
- width: '160px',
- },
- {
- field: 'urlTemplate',
- name: i18n.translate('indexPatternManagement.labelTemplate.urlHeader', {
- defaultMessage: 'URL Template',
- }),
- },
- {
- field: 'labelTemplate',
- name: i18n.translate('indexPatternManagement.labelTemplate.labelHeader', {
- defaultMessage: 'Label Template',
- }),
- },
- {
- field: 'output',
- name: i18n.translate('indexPatternManagement.labelTemplate.outputHeader', {
- defaultMessage: 'Output',
- }),
- render: (value: LabelTemplateExampleItem['output']) => {
- return (
-
- );
- },
- },
- ]}
- />
-
-
-
- ) : null;
-};
-
-LabelTemplateFlyout.displayName = 'LabelTemplateFlyout';
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.test.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.test.tsx
deleted file mode 100644
index bbdb18da901d1f..00000000000000
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.test.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-import React from 'react';
-import { shallowWithI18nProvider } from '@kbn/test/jest';
-
-import { UrlTemplateFlyout } from './url_template_flyout';
-
-describe('UrlTemplateFlyout', () => {
- it('should render normally', async () => {
- const component = shallowWithI18nProvider();
- expect(component).toMatchSnapshot();
- });
-
- it('should not render if not visible', async () => {
- const component = shallowWithI18nProvider();
- expect(component).toMatchSnapshot();
- });
-});
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.tsx
deleted file mode 100644
index fc2b8d72536ec6..00000000000000
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/editors/url/url_template_flyout.tsx
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-import React from 'react';
-
-import { EuiBasicTable, EuiCode, EuiFlyout, EuiFlyoutBody, EuiText } from '@elastic/eui';
-
-import { i18n } from '@kbn/i18n';
-import { FormattedMessage } from '@kbn/i18n/react';
-
-export const UrlTemplateFlyout = ({ isVisible = false, onClose = () => {} }) => {
- return isVisible ? (
-
-
-
-
-
-
-
- {'{{ }}'},
- strongUrlTemplate: (
-
-
-
- ),
- }}
- />
-
-
- -
- value —
-
-
- -
- rawValue —
-
-
-
-
-
-
-
-
-
-
- ) : null;
-};
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx
index d9352f18e96731..78dc87f7a8027a 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx
+++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.test.tsx
@@ -10,7 +10,7 @@ import React, { PureComponent } from 'react';
import { shallow } from 'enzyme';
import { FieldFormatEditor } from './field_format_editor';
-import { DefaultFormatEditor } from './editors/default';
+import type { DefaultFormatEditor } from 'src/plugins/index_pattern_field_editor/public';
class TestEditor extends PureComponent {
render() {
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.tsx b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.tsx
index 81abf2b5b1d203..60107e19170c77 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.tsx
+++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/field_format_editor.tsx
@@ -7,7 +7,7 @@
*/
import React, { PureComponent, Fragment } from 'react';
-import { DefaultFormatEditor } from '../../components/field_format_editor/editors/default';
+import type { DefaultFormatEditor } from 'src/plugins/index_pattern_field_editor/public';
export interface FieldFormatEditorProps {
fieldType: string;
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/index.ts b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/index.ts
index 7eea994a3e2d2f..66d9760b24c657 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/index.ts
+++ b/src/plugins/index_pattern_management/public/components/field_editor/components/field_format_editor/index.ts
@@ -7,4 +7,3 @@
*/
export { FieldFormatEditor } from './field_format_editor';
-export * from './editors';
diff --git a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx
index 829536063a26c9..f0da57a5f9b6f3 100644
--- a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx
+++ b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx
@@ -487,7 +487,7 @@ export class FieldEditor extends PureComponent
diff --git a/src/plugins/index_pattern_management/public/index.ts b/src/plugins/index_pattern_management/public/index.ts
index 27e405a4113de9..94611705a93908 100644
--- a/src/plugins/index_pattern_management/public/index.ts
+++ b/src/plugins/index_pattern_management/public/index.ts
@@ -31,6 +31,4 @@ export {
IndexPatternListConfig,
} from './service';
-export { DefaultFormatEditor } from './components/field_editor/components/field_format_editor';
-
export { MlCardState } from './types';
diff --git a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx
index 45941969dbed12..e47f60ad6fcdd6 100644
--- a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx
+++ b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx
@@ -42,7 +42,7 @@ export async function mountManagementSection(
) {
const [
{ chrome, application, savedObjects, uiSettings, notifications, overlays, http, docLinks },
- { data },
+ { data, indexPatternFieldEditor },
indexPatternManagementStart,
] = await getStartServices();
const canSave = Boolean(application.capabilities.indexPatterns.save);
@@ -61,9 +61,11 @@ export async function mountManagementSection(
http,
docLinks,
data,
+ indexPatternFieldEditor,
indexPatternManagementStart: indexPatternManagementStart as IndexPatternManagementStart,
setBreadcrumbs: params.setBreadcrumbs,
getMlCardState,
+ fieldFormatEditors: indexPatternFieldEditor.fieldFormatEditors,
};
ReactDOM.render(
diff --git a/src/plugins/index_pattern_management/public/mocks.ts b/src/plugins/index_pattern_management/public/mocks.ts
index 974b1ae1bd8630..309d5a5611cd62 100644
--- a/src/plugins/index_pattern_management/public/mocks.ts
+++ b/src/plugins/index_pattern_management/public/mocks.ts
@@ -11,11 +11,13 @@ import { coreMock } from '../../../core/public/mocks';
import { managementPluginMock } from '../../management/public/mocks';
import { urlForwardingPluginMock } from '../../url_forwarding/public/mocks';
import { dataPluginMock } from '../../data/public/mocks';
+import { indexPatternFieldEditorPluginMock } from '../../index_pattern_field_editor/public/mocks';
import {
IndexPatternManagementSetup,
IndexPatternManagementStart,
IndexPatternManagementPlugin,
} from './plugin';
+import { IndexPatternManagmentContext } from './types';
const createSetupContract = (): IndexPatternManagementSetup => ({
creation: {
@@ -24,10 +26,6 @@ const createSetupContract = (): IndexPatternManagementSetup => ({
list: {
addListConfig: jest.fn(),
} as any,
- fieldFormatEditors: {
- getAll: jest.fn(),
- getById: jest.fn(),
- } as any,
environment: {
update: jest.fn(),
},
@@ -43,10 +41,6 @@ const createStartContract = (): IndexPatternManagementStart => ({
getFieldInfo: jest.fn(),
areScriptedFieldsEnabled: jest.fn(),
} as any,
- fieldFormatEditors: {
- getAll: jest.fn(),
- getById: jest.fn(),
- } as any,
});
const createInstance = async () => {
@@ -59,6 +53,7 @@ const createInstance = async () => {
const doStart = () =>
plugin.start(coreMock.createStart(), {
data: dataPluginMock.createStartContract(),
+ indexPatternFieldEditor: indexPatternFieldEditorPluginMock.createStartContract(),
});
return {
@@ -69,13 +64,17 @@ const createInstance = async () => {
};
const docLinks = {
+ ELASTIC_WEBSITE_URL: 'htts://jestTest.elastic.co',
+ DOC_LINK_VERSION: 'jest',
links: {
indexPatterns: {},
scriptedFields: {},
- },
+ } as any,
};
-const createIndexPatternManagmentContext = () => {
+const createIndexPatternManagmentContext = (): {
+ [key in keyof IndexPatternManagmentContext]: any;
+} => {
const {
chrome,
application,
@@ -86,6 +85,7 @@ const createIndexPatternManagmentContext = () => {
} = coreMock.createStart();
const { http } = coreMock.createSetup();
const data = dataPluginMock.createStartContract();
+ const indexPatternFieldEditor = indexPatternFieldEditorPluginMock.createStartContract();
return {
chrome,
@@ -97,8 +97,11 @@ const createIndexPatternManagmentContext = () => {
http,
docLinks,
data,
+ indexPatternFieldEditor,
indexPatternManagementStart: createStartContract(),
setBreadcrumbs: () => {},
+ getMlCardState: () => 2,
+ fieldFormatEditors: indexPatternFieldEditor.fieldFormatEditors,
};
};
diff --git a/src/plugins/index_pattern_management/public/plugin.ts b/src/plugins/index_pattern_management/public/plugin.ts
index bc4ef83de5012f..ed92172c8b91ca 100644
--- a/src/plugins/index_pattern_management/public/plugin.ts
+++ b/src/plugins/index_pattern_management/public/plugin.ts
@@ -17,6 +17,7 @@ import {
} from './service';
import { ManagementSetup } from '../../management/public';
+import { IndexPatternFieldEditorStart } from '../../index_pattern_field_editor/public';
export interface IndexPatternManagementSetupDependencies {
management: ManagementSetup;
@@ -25,6 +26,7 @@ export interface IndexPatternManagementSetupDependencies {
export interface IndexPatternManagementStartDependencies {
data: DataPublicPluginStart;
+ indexPatternFieldEditor: IndexPatternFieldEditorStart;
}
export type IndexPatternManagementSetup = IndexPatternManagementServiceSetup;
diff --git a/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts b/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts
index a686891c980149..15be7f11892e49 100644
--- a/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts
+++ b/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts
@@ -9,23 +9,7 @@
import { HttpSetup } from '../../../../core/public';
import { IndexPatternCreationManager, IndexPatternCreationConfig } from './creation';
import { IndexPatternListManager, IndexPatternListConfig } from './list';
-import { FieldFormatEditors } from './field_format_editors';
import { EnvironmentService } from './environment';
-
-import {
- BytesFormatEditor,
- ColorFormatEditor,
- DateFormatEditor,
- DateNanosFormatEditor,
- DurationFormatEditor,
- NumberFormatEditor,
- PercentFormatEditor,
- StaticLookupFormatEditor,
- StringFormatEditor,
- TruncateFormatEditor,
- UrlFormatEditor,
-} from '../components/field_editor/components/field_format_editor';
-
interface SetupDependencies {
httpClient: HttpSetup;
}
@@ -38,13 +22,11 @@ interface SetupDependencies {
export class IndexPatternManagementService {
indexPatternCreationManager: IndexPatternCreationManager;
indexPatternListConfig: IndexPatternListManager;
- fieldFormatEditors: FieldFormatEditors;
environmentService: EnvironmentService;
constructor() {
this.indexPatternCreationManager = new IndexPatternCreationManager();
this.indexPatternListConfig = new IndexPatternListManager();
- this.fieldFormatEditors = new FieldFormatEditors();
this.environmentService = new EnvironmentService();
}
@@ -55,26 +37,9 @@ export class IndexPatternManagementService {
const indexPatternListConfigSetup = this.indexPatternListConfig.setup();
indexPatternListConfigSetup.addListConfig(IndexPatternListConfig);
- const defaultFieldFormatEditors = [
- BytesFormatEditor,
- ColorFormatEditor,
- DateFormatEditor,
- DateNanosFormatEditor,
- DurationFormatEditor,
- NumberFormatEditor,
- PercentFormatEditor,
- StaticLookupFormatEditor,
- StringFormatEditor,
- TruncateFormatEditor,
- UrlFormatEditor,
- ];
-
- const fieldFormatEditorsSetup = this.fieldFormatEditors.setup(defaultFieldFormatEditors);
-
return {
creation: creationManagerSetup,
list: indexPatternListConfigSetup,
- fieldFormatEditors: fieldFormatEditorsSetup,
environment: this.environmentService.setup(),
};
}
@@ -83,7 +48,6 @@ export class IndexPatternManagementService {
return {
creation: this.indexPatternCreationManager.start(),
list: this.indexPatternListConfig.start(),
- fieldFormatEditors: this.fieldFormatEditors.start(),
};
}
diff --git a/src/plugins/index_pattern_management/public/types.ts b/src/plugins/index_pattern_management/public/types.ts
index 84e8ae007b99c8..62ee18ababc0bd 100644
--- a/src/plugins/index_pattern_management/public/types.ts
+++ b/src/plugins/index_pattern_management/public/types.ts
@@ -20,6 +20,7 @@ import { DataPublicPluginStart } from 'src/plugins/data/public';
import { ManagementAppMountParams } from '../../management/public';
import { IndexPatternManagementStart } from './index';
import { KibanaReactContextValue } from '../../kibana_react/public';
+import { IndexPatternFieldEditorStart } from '../../index_pattern_field_editor/public';
export interface IndexPatternManagmentContext {
chrome: ChromeStart;
@@ -31,9 +32,11 @@ export interface IndexPatternManagmentContext {
http: HttpSetup;
docLinks: DocLinksStart;
data: DataPublicPluginStart;
+ indexPatternFieldEditor: IndexPatternFieldEditorStart;
indexPatternManagementStart: IndexPatternManagementStart;
setBreadcrumbs: ManagementAppMountParams['setBreadcrumbs'];
getMlCardState: () => MlCardState;
+ fieldFormatEditors: IndexPatternFieldEditorStart['fieldFormatEditors'];
}
export type IndexPatternManagmentContextValue = KibanaReactContextValue;
diff --git a/src/plugins/index_pattern_management/tsconfig.json b/src/plugins/index_pattern_management/tsconfig.json
index 4dca1634fddb69..37bd3e4aa5bbb9 100644
--- a/src/plugins/index_pattern_management/tsconfig.json
+++ b/src/plugins/index_pattern_management/tsconfig.json
@@ -18,5 +18,7 @@
{ "path": "../url_forwarding/tsconfig.json" },
{ "path": "../kibana_react/tsconfig.json" },
{ "path": "../kibana_utils/tsconfig.json" },
+ { "path": "../es_ui_shared/tsconfig.json" },
+ { "path": "../index_pattern_field_editor/tsconfig.json" },
]
}
diff --git a/test/functional/apps/management/_handle_version_conflict.js b/test/functional/apps/management/_handle_version_conflict.js
index 15483edc613a8d..5ad45b33feae87 100644
--- a/test/functional/apps/management/_handle_version_conflict.js
+++ b/test/functional/apps/management/_handle_version_conflict.js
@@ -18,6 +18,7 @@
import expect from '@kbn/expect';
export default function ({ getService, getPageObjects }) {
+ const testSubjects = getService('testSubjects');
const esArchiver = getService('esArchiver');
const browser = getService('browser');
const es = getService('legacyEs');
@@ -66,6 +67,11 @@ export default function ({ getService, getPageObjects }) {
log.debug('Starting openControlsByName (' + fieldName + ')');
await PageObjects.settings.openControlsByName(fieldName);
log.debug('controls are open');
+ await (
+ await (await testSubjects.find('formatRow')).findAllByCssSelector(
+ '[data-test-subj="toggle"]'
+ )
+ )[0].click();
await PageObjects.settings.setFieldFormat('url');
const response = await es.update({
index: '.kibana',
diff --git a/test/functional/apps/management/_index_pattern_filter.js b/test/functional/apps/management/_index_pattern_filter.js
index eeb0b224d5f0ca..261ba29410a09d 100644
--- a/test/functional/apps/management/_index_pattern_filter.js
+++ b/test/functional/apps/management/_index_pattern_filter.js
@@ -35,23 +35,23 @@ export default function ({ getService, getPageObjects }) {
await PageObjects.settings.clickKibanaIndexPatterns();
await PageObjects.settings.clickIndexPatternLogstash();
await PageObjects.settings.getFieldTypes();
- await PageObjects.settings.setFieldTypeFilter('string');
+ await PageObjects.settings.setFieldTypeFilter('keyword');
await retry.try(async function () {
const fieldTypes = await PageObjects.settings.getFieldTypes();
expect(fieldTypes.length).to.be.above(0);
for (const fieldType of fieldTypes) {
- expect(fieldType).to.be('string');
+ expect(fieldType).to.be('keyword');
}
});
- await PageObjects.settings.setFieldTypeFilter('number');
+ await PageObjects.settings.setFieldTypeFilter('long');
await retry.try(async function () {
const fieldTypes = await PageObjects.settings.getFieldTypes();
expect(fieldTypes.length).to.be.above(0);
for (const fieldType of fieldTypes) {
- expect(fieldType).to.be('number');
+ expect(fieldType).to.be('long');
}
});
});
diff --git a/test/functional/apps/management/_index_pattern_popularity.js b/test/functional/apps/management/_index_pattern_popularity.js
index 231617d7084e96..0618dd79e272ec 100644
--- a/test/functional/apps/management/_index_pattern_popularity.js
+++ b/test/functional/apps/management/_index_pattern_popularity.js
@@ -10,6 +10,7 @@ import expect from '@kbn/expect';
export default function ({ getService, getPageObjects }) {
const kibanaServer = getService('kibanaServer');
+ const testSubjects = getService('testSubjects');
const log = getService('log');
const PageObjects = getPageObjects(['settings', 'common']);
@@ -27,11 +28,12 @@ export default function ({ getService, getPageObjects }) {
log.debug('Starting openControlsByName (' + fieldName + ')');
await PageObjects.settings.openControlsByName(fieldName);
log.debug('increasePopularity');
+ await testSubjects.click('toggleAdvancedSetting');
await PageObjects.settings.increasePopularity();
});
afterEach(async () => {
- await PageObjects.settings.controlChangeCancel();
+ await testSubjects.click('closeFlyoutButton');
await PageObjects.settings.removeIndexPattern();
// Cancel saving the popularity change (we didn't make a change in this case, just checking the value)
});
@@ -44,12 +46,12 @@ export default function ({ getService, getPageObjects }) {
it('should be reset on cancel', async function () {
// Cancel saving the popularity change
- await PageObjects.settings.controlChangeCancel();
+ await testSubjects.click('closeFlyoutButton');
await PageObjects.settings.openControlsByName(fieldName);
// check that it is 0 (previous increase was cancelled
const popularity = await PageObjects.settings.getPopularity();
log.debug('popularity = ' + popularity);
- expect(popularity).to.be('');
+ expect(popularity).to.be('0');
});
it('can be saved', async function () {
diff --git a/test/functional/apps/management/_index_pattern_results_sort.js b/test/functional/apps/management/_index_pattern_results_sort.js
index 90af0636bcd486..cedf5ee355b36a 100644
--- a/test/functional/apps/management/_index_pattern_results_sort.js
+++ b/test/functional/apps/management/_index_pattern_results_sort.js
@@ -17,6 +17,12 @@ export default function ({ getService, getPageObjects }) {
before(async function () {
// delete .kibana index and then wait for Kibana to re-create it
await kibanaServer.uiSettings.replace({});
+ await PageObjects.settings.navigateTo();
+ await PageObjects.settings.createIndexPattern();
+ });
+
+ after(async function () {
+ return await PageObjects.settings.removeIndexPattern();
});
const columns = [
@@ -31,8 +37,8 @@ export default function ({ getService, getPageObjects }) {
},
{
heading: 'Type',
- first: '_source',
- last: 'string',
+ first: '',
+ last: 'text',
selector: async function () {
const tableRow = await PageObjects.settings.getTableRow(0, 1);
return await tableRow.getVisibleText();
@@ -42,16 +48,11 @@ export default function ({ getService, getPageObjects }) {
columns.forEach(function (col) {
describe('sort by heading - ' + col.heading, function indexPatternCreation() {
- before(async function () {
- await PageObjects.settings.createIndexPattern();
- });
-
- after(async function () {
- return await PageObjects.settings.removeIndexPattern();
- });
-
it('should sort ascending', async function () {
- await PageObjects.settings.sortBy(col.heading);
+ console.log('col.heading', col.heading);
+ if (col.heading !== 'Name') {
+ await PageObjects.settings.sortBy(col.heading);
+ }
const rowText = await col.selector();
expect(rowText).to.be(col.first);
});
@@ -65,15 +66,6 @@ export default function ({ getService, getPageObjects }) {
});
describe('field list pagination', function () {
const EXPECTED_FIELD_COUNT = 86;
-
- before(async function () {
- await PageObjects.settings.createIndexPattern();
- });
-
- after(async function () {
- return await PageObjects.settings.removeIndexPattern();
- });
-
it('makelogs data should have expected number of fields', async function () {
await retry.try(async function () {
const TabCount = await PageObjects.settings.getFieldsTabCount();
diff --git a/test/functional/apps/visualize/_tag_cloud.ts b/test/functional/apps/visualize/_tag_cloud.ts
index e619a35fb3d0b7..c7d864e5cfb233 100644
--- a/test/functional/apps/visualize/_tag_cloud.ts
+++ b/test/functional/apps/visualize/_tag_cloud.ts
@@ -11,6 +11,7 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getService, getPageObjects }: FtrProviderContext) {
+ const testSubjects = getService('testSubjects');
const filterBar = getService('filterBar');
const log = getService('log');
const inspector = getService('inspector');
@@ -145,6 +146,11 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.settings.clickIndexPatternLogstash();
await PageObjects.settings.filterField(termsField);
await PageObjects.settings.openControlsByName(termsField);
+ await (
+ await (await testSubjects.find('formatRow')).findAllByCssSelector(
+ '[data-test-subj="toggle"]'
+ )
+ )[0].click();
await PageObjects.settings.setFieldFormat('bytes');
await PageObjects.settings.controlChangeSave();
await PageObjects.common.navigateToApp('visualize');
diff --git a/test/functional/page_objects/settings_page.ts b/test/functional/page_objects/settings_page.ts
index f59db345f39ff9..09a05732b791bb 100644
--- a/test/functional/page_objects/settings_page.ts
+++ b/test/functional/page_objects/settings_page.ts
@@ -249,7 +249,7 @@ export function SettingsPageProvider({ getService, getPageObjects }: FtrProvider
await find.clickByCssSelector(
`table.euiTable tbody tr.euiTableRow:nth-child(${tableFields.indexOf(name) + 1})
- td:last-child button`
+ td:nth-last-child(2) button`
);
}
diff --git a/tsconfig.json b/tsconfig.json
index 48feac3efe4752..f6ce6b92b7e02f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -63,5 +63,6 @@
{ "path": "./src/plugins/visualizations/tsconfig.json" },
{ "path": "./src/plugins/visualize/tsconfig.json" },
{ "path": "./src/plugins/index_pattern_management/tsconfig.json" },
+ { "path": "./src/plugins/index_pattern_field_editor/tsconfig.json" },
]
}
diff --git a/x-pack/plugins/runtime_fields/public/components/runtime_field_form/runtime_field_form.tsx b/x-pack/plugins/runtime_fields/public/components/runtime_field_form/runtime_field_form.tsx
index f6815a4264a5d4..023b620522282b 100644
--- a/x-pack/plugins/runtime_fields/public/components/runtime_field_form/runtime_field_form.tsx
+++ b/x-pack/plugins/runtime_fields/public/components/runtime_field_form/runtime_field_form.tsx
@@ -8,7 +8,6 @@
import React, { useEffect, useState, useCallback } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
-import { EuiCode } from '@elastic/eui';
import { PainlessLang, PainlessContext } from '@kbn/monaco';
import {
EuiFlexGroup,
@@ -19,6 +18,7 @@ import {
EuiComboBoxOptionOption,
EuiLink,
EuiCallOut,
+ EuiCode,
} from '@elastic/eui';
import {
diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json
index 301f133436e6f8..3a07e83950149f 100644
--- a/x-pack/plugins/translations/translations/ja-JP.json
+++ b/x-pack/plugins/translations/translations/ja-JP.json
@@ -2597,15 +2597,15 @@
"indexPatternManagement.actions.deleteButton": "削除",
"indexPatternManagement.actions.saveButton": "フィールドを保存",
"indexPatternManagement.aliasLabel": "エイリアス",
- "indexPatternManagement.color.actions": "アクション",
- "indexPatternManagement.color.addColorButton": "色を追加",
- "indexPatternManagement.color.backgroundLabel": "背景色",
- "indexPatternManagement.color.deleteAria": "削除",
- "indexPatternManagement.color.deleteTitle": "色のフォーマットを削除",
- "indexPatternManagement.color.exampleLabel": "例",
- "indexPatternManagement.color.patternLabel": "パターン(正規表現)",
- "indexPatternManagement.color.rangeLabel": "範囲(min:max)",
- "indexPatternManagement.color.textColorLabel": "文字の色",
+ "indexPatternFieldEditor.color.actions": "アクション",
+ "indexPatternFieldEditor.color.addColorButton": "色を追加",
+ "indexPatternFieldEditor.color.backgroundLabel": "背景色",
+ "indexPatternFieldEditor.color.deleteAria": "削除",
+ "indexPatternFieldEditor.color.deleteTitle": "色のフォーマットを削除",
+ "indexPatternFieldEditor.color.exampleLabel": "例",
+ "indexPatternFieldEditor.color.patternLabel": "パターン(正規表現)",
+ "indexPatternFieldEditor.color.rangeLabel": "範囲(min:max)",
+ "indexPatternFieldEditor.color.textColorLabel": "文字の色",
"indexPatternManagement.createHeader": "スクリプトフィールドを作成",
"indexPatternManagement.createIndexPattern.betaLabel": "ベータ",
"indexPatternManagement.createIndexPattern.description": "インデックスパターンは、{single}または{multiple}データソース、{star}と一致します。",
@@ -2669,10 +2669,10 @@
"indexPatternManagement.createIndexPatternHeader": "{indexPatternName}の作成",
"indexPatternManagement.customLabel": "カスタムラベル",
"indexPatternManagement.dataStreamLabel": "データストリーム",
- "indexPatternManagement.date.documentationLabel": "ドキュメント",
- "indexPatternManagement.date.momentLabel": "Moment.jsのフォーマットパターン(デフォルト: {defaultPattern})",
- "indexPatternManagement.defaultErrorMessage": "このフォーマット構成の使用を試みた際にエラーが発生しました: {message}",
- "indexPatternManagement.defaultFormatDropDown": "- デフォルト -",
+ "indexPatternFieldEditor.date.documentationLabel": "ドキュメント",
+ "indexPatternFieldEditor.date.momentLabel": "Moment.jsのフォーマットパターン(デフォルト: {defaultPattern})",
+ "indexPatternFieldEditor.defaultErrorMessage": "このフォーマット構成の使用を試みた際にエラーが発生しました: {message}",
+ "indexPatternFieldEditor.defaultFormatDropDown": "- デフォルト -",
"indexPatternManagement.defaultFormatHeader": "フォーマット (デフォルト: {defaultFormat})",
"indexPatternManagement.deleteField.cancelButton": "キャンセル",
"indexPatternManagement.deleteField.deleteButton": "削除",
@@ -2682,11 +2682,11 @@
"indexPatternManagement.deleteFieldLabel": "削除されたフィールドは復元できません。{separator}続行してよろしいですか?",
"indexPatternManagement.disabledCallOutHeader": "スクリプティングが無効です",
"indexPatternManagement.disabledCallOutLabel": "Elasticsearchでのすべてのインラインスクリプティングが無効になっています。Kibanaでスクリプトフィールドを使用するには、インラインスクリプティングを有効にする必要があります。",
- "indexPatternManagement.duration.decimalPlacesLabel": "小数部分の桁数",
- "indexPatternManagement.duration.inputFormatLabel": "インプット形式",
- "indexPatternManagement.duration.outputFormatLabel": "アウトプット形式",
- "indexPatternManagement.duration.showSuffixLabel": "接尾辞を表示",
- "indexPatternManagement.durationErrorMessage": "小数部分の桁数は0から20までの間で指定する必要があります",
+ "indexPatternFieldEditor.duration.decimalPlacesLabel": "小数部分の桁数",
+ "indexPatternFieldEditor.duration.inputFormatLabel": "インプット形式",
+ "indexPatternFieldEditor.duration.outputFormatLabel": "アウトプット形式",
+ "indexPatternFieldEditor.duration.showSuffixLabel": "接尾辞を表示",
+ "indexPatternFieldEditor.durationErrorMessage": "小数部分の桁数は0から20までの間で指定する必要があります",
"indexPatternManagement.editHeader": "{fieldName}を編集",
"indexPatternManagement.editIndexPattern.createIndex.defaultButtonDescription": "すべてのデータに完全アグリゲーションを実行",
"indexPatternManagement.editIndexPattern.createIndex.defaultButtonText": "標準インデックスパターン",
@@ -2771,7 +2771,6 @@
"indexPatternManagement.editIndexPattern.tabs.sourceHeader": "フィールドフィルター",
"indexPatternManagement.editIndexPattern.timeFilterHeader": "時刻フィールド:「{timeFieldName}」",
"indexPatternManagement.editIndexPattern.timeFilterLabel.mappingAPILink": "マッピングAPI",
- "indexPatternManagement.editIndexPattern.timeFilterLabel.timeFilterDetail": "このページは{indexPatternTitle}インデックス内のすべてのフィールドと、Elasticsearchに記録された各フィールドのコアタイプを一覧表示します。フィールドタイプを変更するにはElasticsearchを使用します",
"indexPatternManagement.editIndexPatternLiveRegionAriaLabel": "インデックスパターン",
"indexPatternManagement.emptyIndexPatternPrompt.documentation": "ドキュメンテーションを表示",
"indexPatternManagement.emptyIndexPatternPrompt.indexPatternExplanation": "Kibanaでは、検索するインデックスを特定するためにインデックスパターンが必要です。インデックスパターンは、昨日のログデータなど特定のインデックス、またはログデータを含むすべてのインデックスを参照できます。",
@@ -2780,7 +2779,6 @@
"indexPatternManagement.emptyIndexPatternPrompt.youHaveData": "Elasticsearchにデータがあります。",
"indexPatternManagement.fieldTypeConflict": "フィールドタイプの矛盾",
"indexPatternManagement.formatHeader": "フォーマット",
- "indexPatternManagement.formatLabel": "フォーマットは、特定の値の表示形式を管理できます。また、値を完全に変更したり、ディスカバリでのハイライト機能を無効にしたりすることも可能です。",
"indexPatternManagement.frozenLabel": "凍結",
"indexPatternManagement.indexLabel": "インデックス",
"indexPatternManagement.indexNameLabel": "インデックス名",
@@ -2797,19 +2795,6 @@
"indexPatternManagement.indexPatternTable.indexPatternExplanation": "Elasticsearchからのデータの取得に役立つインデックスパターンを作成して管理します。",
"indexPatternManagement.indexPatternTable.title": "インデックスパターン",
"indexPatternManagement.labelHelpText": "このフィールドが Discover、Maps、Visualize に表示されるときに使用するカスタムラベルを設定します。現在、クエリとフィルターはカスタムラベルをサポートせず、元のフィールド名が使用されます。",
- "indexPatternManagement.labelTemplate.example.idLabel": "ユーザー#{value}",
- "indexPatternManagement.labelTemplate.example.output.idLabel": "ユーザー",
- "indexPatternManagement.labelTemplate.example.output.pathLabel": "アセットを表示",
- "indexPatternManagement.labelTemplate.example.pathLabel": "アセットを表示",
- "indexPatternManagement.labelTemplate.examplesHeader": "例",
- "indexPatternManagement.labelTemplate.inputHeader": "インプット",
- "indexPatternManagement.labelTemplate.labelHeader": "ラベルテンプレート",
- "indexPatternManagement.labelTemplate.outputHeader": "アウトプット",
- "indexPatternManagement.labelTemplate.urlHeader": "URLテンプレート",
- "indexPatternManagement.labelTemplate.urlLabel": "フォーマット済みURL",
- "indexPatternManagement.labelTemplate.valueLabel": "フィールド値",
- "indexPatternManagement.labelTemplateHeader": "ラベルテンプレート",
- "indexPatternManagement.labelTemplateLabel": "このフィールドのURLが長い場合、URLのテキストバージョン用の代替テンプレートを使用すると良いかもしれません。URLの代わりに表示されますが、URLにリンクされます。このフォーマットは、値の投入に二重中括弧の表記{doubleCurlyBraces}を使用する文字列です。次の値にアクセスできます。",
"indexPatternManagement.languageLabel": "言語",
"indexPatternManagement.mappingConflictLabel.mappingConflictDetail": "{mappingConflict} {fieldName}というフィールドはすでに存在します。スクリプトフィールドに同じ名前を付けると、同時に両方のフィールドにクエリが実行できなくなります。",
"indexPatternManagement.mappingConflictLabel.mappingConflictLabel": "マッピングの矛盾:",
@@ -2817,27 +2802,27 @@
"indexPatternManagement.nameErrorMessage": "名前が必要です",
"indexPatternManagement.nameLabel": "名前",
"indexPatternManagement.namePlaceholder": "新規スクリプトフィールド",
- "indexPatternManagement.number.documentationLabel": "ドキュメント",
- "indexPatternManagement.number.numeralLabel": "Numeral.js のフォーマットパターン (デフォルト: {defaultPattern})",
+ "indexPatternFieldEditor.number.documentationLabel": "ドキュメント",
+ "indexPatternFieldEditor.number.numeralLabel": "Numeral.js のフォーマットパターン (デフォルト: {defaultPattern})",
"indexPatternManagement.popularityLabel": "利用頻度",
- "indexPatternManagement.samples.inputHeader": "インプット",
- "indexPatternManagement.samples.outputHeader": "アウトプット",
- "indexPatternManagement.samplesHeader": "サンプル",
+ "indexPatternFieldEditor.samples.inputHeader": "インプット",
+ "indexPatternFieldEditor.samples.outputHeader": "アウトプット",
+ "indexPatternFieldEditor.samplesHeader": "サンプル",
"indexPatternManagement.script.accessWithLabel": "{code} でフィールドにアクセスします。",
"indexPatternManagement.script.getHelpLabel": "構文のヒントを得たり、スクリプトの結果をプレビューしたりできます。",
"indexPatternManagement.scriptingLanguages.errorFetchingToastDescription": "Elasticsearchから利用可能なスクリプト言語の取得中にエラーが発生しました",
"indexPatternManagement.scriptInvalidErrorMessage": "スクリプトが無効です。詳細については、スクリプトプレビューを表示してください",
"indexPatternManagement.scriptLabel": "スクリプト",
"indexPatternManagement.scriptRequiredErrorMessage": "スクリプトが必要です",
- "indexPatternManagement.staticLookup.actions": "アクション",
- "indexPatternManagement.staticLookup.addEntryButton": "エントリーを追加",
- "indexPatternManagement.staticLookup.deleteAria": "削除",
- "indexPatternManagement.staticLookup.deleteTitle": "エントリーの削除",
- "indexPatternManagement.staticLookup.keyLabel": "キー",
- "indexPatternManagement.staticLookup.leaveBlankPlaceholder": "値をそのままにするには空欄にします",
- "indexPatternManagement.staticLookup.unknownKeyLabel": "不明なキーの値",
- "indexPatternManagement.staticLookup.valueLabel": "値",
- "indexPatternManagement.string.transformLabel": "変換",
+ "indexPatternFieldEditor.staticLookup.actions": "アクション",
+ "indexPatternFieldEditor.staticLookup.addEntryButton": "エントリーを追加",
+ "indexPatternFieldEditor.staticLookup.deleteAria": "削除",
+ "indexPatternFieldEditor.staticLookup.deleteTitle": "エントリーの削除",
+ "indexPatternFieldEditor.staticLookup.keyLabel": "キー",
+ "indexPatternFieldEditor.staticLookup.leaveBlankPlaceholder": "値をそのままにするには空欄にします",
+ "indexPatternFieldEditor.staticLookup.unknownKeyLabel": "不明なキーの値",
+ "indexPatternFieldEditor.staticLookup.valueLabel": "値",
+ "indexPatternFieldEditor.string.transformLabel": "変換",
"indexPatternManagement.syntax.default.formatLabel": "doc['some_field'].value",
"indexPatternManagement.syntax.defaultLabel.defaultDetail": "デフォルトで、KibanaのスクリプトフィールドはElasticsearchでの使用を目的に特別に開発されたシンプルでセキュアなスクリプト言語の{painless}を使用します。ドキュメントの値にアクセスするには次のフォーマットを使用します。",
"indexPatternManagement.syntax.defaultLabel.painlessLink": "Painless",
@@ -2868,27 +2853,18 @@
"indexPatternManagement.testScript.resultsLabel": "最初の10件",
"indexPatternManagement.testScript.resultsTitle": "結果を表示",
"indexPatternManagement.testScript.submitButtonLabel": "スクリプトを実行",
- "indexPatternManagement.truncate.lengthLabel": "フィールドの長さ",
+ "indexPatternFieldEditor.truncate.lengthLabel": "フィールドの長さ",
"indexPatternManagement.typeLabel": "型",
- "indexPatternManagement.url.heightLabel": "高さ",
- "indexPatternManagement.url.labelTemplateHelpText": "ラベルテンプレートのヘルプ",
- "indexPatternManagement.url.labelTemplateLabel": "ラベルテンプレート",
- "indexPatternManagement.url.offLabel": "オフ",
- "indexPatternManagement.url.onLabel": "オン",
- "indexPatternManagement.url.openTabLabel": "新規タブで開く",
- "indexPatternManagement.url.template.helpLinkText": "URLテンプレートのヘルプ",
- "indexPatternManagement.url.typeLabel": "型",
- "indexPatternManagement.url.urlTemplateLabel": "URLテンプレート",
- "indexPatternManagement.url.widthLabel": "幅",
- "indexPatternManagement.urlTemplate.examplesHeader": "例",
- "indexPatternManagement.urlTemplate.inputHeader": "インプット",
- "indexPatternManagement.urlTemplate.outputHeader": "アウトプット",
- "indexPatternManagement.urlTemplate.rawValueLabel": "非エスケープ値",
- "indexPatternManagement.urlTemplate.templateHeader": "テンプレート",
- "indexPatternManagement.urlTemplate.valueLabel": "URLエスケープ値",
- "indexPatternManagement.urlTemplateHeader": "URLテンプレート",
- "indexPatternManagement.urlTemplateLabel.fieldDetail": "フィールドにURLの一部のみが含まれている場合、{strongUrlTemplate}でその値を完全なURLとしてフォーマットできます。このフォーマットは、値の投入に二重中括弧の表記{doubleCurlyBraces}を使用する文字列です。次の値にアクセスできます。",
- "indexPatternManagement.urlTemplateLabel.strongUrlTemplateLabel": "URLテンプレート",
+ "indexPatternFieldEditor.url.heightLabel": "高さ",
+ "indexPatternFieldEditor.url.labelTemplateHelpText": "ラベルテンプレートのヘルプ",
+ "indexPatternFieldEditor.url.labelTemplateLabel": "ラベルテンプレート",
+ "indexPatternFieldEditor.url.offLabel": "オフ",
+ "indexPatternFieldEditor.url.onLabel": "オン",
+ "indexPatternFieldEditor.url.openTabLabel": "新規タブで開く",
+ "indexPatternFieldEditor.url.template.helpLinkText": "URLテンプレートのヘルプ",
+ "indexPatternFieldEditor.url.typeLabel": "型",
+ "indexPatternFieldEditor.url.urlTemplateLabel": "URLテンプレート",
+ "indexPatternFieldEditor.url.widthLabel": "幅",
"indexPatternManagement.warningCallOut.descriptionLabel": "計算値の表示と集約にスクリプトフィールドが使用できます。そのため非常に遅い場合があり、適切に行わないとKibanaが使用できなくなる可能性もあります。この場合安全策はありません。入力ミスがあると、あちこちに予期せぬ例外が起こります!",
"indexPatternManagement.warningCallOutHeader": "十分ご注意ください",
"indexPatternManagement.warningCallOutLabel.callOutDetail": "スクリプトフィールドを使う前に、{scripFields}と{scriptsInAggregation}についてよく理解するようにしてください。",
diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json
index b8c727c77e465e..0e8ede769a5d25 100644
--- a/x-pack/plugins/translations/translations/zh-CN.json
+++ b/x-pack/plugins/translations/translations/zh-CN.json
@@ -2601,15 +2601,15 @@
"indexPatternManagement.actions.deleteButton": "删除",
"indexPatternManagement.actions.saveButton": "保存字段",
"indexPatternManagement.aliasLabel": "别名",
- "indexPatternManagement.color.actions": "操作",
- "indexPatternManagement.color.addColorButton": "添加颜色",
- "indexPatternManagement.color.backgroundLabel": "背景色",
- "indexPatternManagement.color.deleteAria": "删除",
- "indexPatternManagement.color.deleteTitle": "删除颜色格式",
- "indexPatternManagement.color.exampleLabel": "示例",
- "indexPatternManagement.color.patternLabel": "模式(正则表达式)",
- "indexPatternManagement.color.rangeLabel": "范围(最小值:最大值)",
- "indexPatternManagement.color.textColorLabel": "文本颜色",
+ "indexPatternFieldEditor.color.actions": "操作",
+ "indexPatternFieldEditor.color.addColorButton": "添加颜色",
+ "indexPatternFieldEditor.color.backgroundLabel": "背景色",
+ "indexPatternFieldEditor.color.deleteAria": "删除",
+ "indexPatternFieldEditor.color.deleteTitle": "删除颜色格式",
+ "indexPatternFieldEditor.color.exampleLabel": "示例",
+ "indexPatternFieldEditor.color.patternLabel": "模式(正则表达式)",
+ "indexPatternFieldEditor.color.rangeLabel": "范围(最小值:最大值)",
+ "indexPatternFieldEditor.color.textColorLabel": "文本颜色",
"indexPatternManagement.createHeader": "创建脚本字段",
"indexPatternManagement.createIndexPattern.betaLabel": "公测版",
"indexPatternManagement.createIndexPattern.description": "索引模式可以匹配单个源,例如 {single} 或 {multiple} 个数据源、{star}。",
@@ -2673,10 +2673,10 @@
"indexPatternManagement.createIndexPatternHeader": "创建 {indexPatternName}",
"indexPatternManagement.customLabel": "定制标签",
"indexPatternManagement.dataStreamLabel": "数据流",
- "indexPatternManagement.date.documentationLabel": "文档",
- "indexPatternManagement.date.momentLabel": "Moment.js 格式模式(默认值:{defaultPattern})",
- "indexPatternManagement.defaultErrorMessage": "尝试使用此格式配置时发生错误:{message}",
- "indexPatternManagement.defaultFormatDropDown": "- 默认值 -",
+ "indexPatternFieldEditor.date.documentationLabel": "文档",
+ "indexPatternFieldEditor.date.momentLabel": "Moment.js 格式模式(默认值:{defaultPattern})",
+ "indexPatternFieldEditor.defaultErrorMessage": "尝试使用此格式配置时发生错误:{message}",
+ "indexPatternFieldEditor.defaultFormatDropDown": "- 默认值 -",
"indexPatternManagement.defaultFormatHeader": "格式(默认值:{defaultFormat})",
"indexPatternManagement.deleteField.cancelButton": "取消",
"indexPatternManagement.deleteField.deleteButton": "删除",
@@ -2686,11 +2686,11 @@
"indexPatternManagement.deleteFieldLabel": "您无法恢复已删除字段。{separator}确定要执行此操作?",
"indexPatternManagement.disabledCallOutHeader": "脚本已禁用",
"indexPatternManagement.disabledCallOutLabel": "所有内联脚本在 Elasticsearch 中已禁用。必须至少为一种语言启用内联脚本,才能在 Kibana 中使用脚本字段。",
- "indexPatternManagement.duration.decimalPlacesLabel": "小数位数",
- "indexPatternManagement.duration.inputFormatLabel": "输入格式",
- "indexPatternManagement.duration.outputFormatLabel": "输出格式",
- "indexPatternManagement.duration.showSuffixLabel": "显示后缀",
- "indexPatternManagement.durationErrorMessage": "小数位数必须介于 0 和 20 之间",
+ "indexPatternFieldEditor.duration.decimalPlacesLabel": "小数位数",
+ "indexPatternFieldEditor.duration.inputFormatLabel": "输入格式",
+ "indexPatternFieldEditor.duration.outputFormatLabel": "输出格式",
+ "indexPatternFieldEditor.duration.showSuffixLabel": "显示后缀",
+ "indexPatternFieldEditor.durationErrorMessage": "小数位数必须介于 0 和 20 之间",
"indexPatternManagement.editHeader": "编辑 {fieldName}",
"indexPatternManagement.editIndexPattern.createIndex.defaultButtonDescription": "对任何数据执行完全聚合",
"indexPatternManagement.editIndexPattern.createIndex.defaultButtonText": "标准索引模式",
@@ -2775,7 +2775,6 @@
"indexPatternManagement.editIndexPattern.tabs.sourceHeader": "字段筛选",
"indexPatternManagement.editIndexPattern.timeFilterHeader": "时间字段:“{timeFieldName}”",
"indexPatternManagement.editIndexPattern.timeFilterLabel.mappingAPILink": "映射 API",
- "indexPatternManagement.editIndexPattern.timeFilterLabel.timeFilterDetail": "此页根据 Elasticsearch 的记录列出“{indexPatternTitle}”索引中的每个字段以及字段的关联核心类型。要更改字段类型,请使用 Elasticsearch",
"indexPatternManagement.editIndexPatternLiveRegionAriaLabel": "索引模式",
"indexPatternManagement.emptyIndexPatternPrompt.documentation": "阅读文档",
"indexPatternManagement.emptyIndexPatternPrompt.indexPatternExplanation": "Kibana 需要索引模式,以识别您要浏览的索引。索引模式可以指向特定索引(例如昨天的日志数据),或包含日志数据的所有索引。",
@@ -2784,7 +2783,6 @@
"indexPatternManagement.emptyIndexPatternPrompt.youHaveData": "您在 Elasticsearch 中有数据。",
"indexPatternManagement.fieldTypeConflict": "字段类型冲突",
"indexPatternManagement.formatHeader": "格式",
- "indexPatternManagement.formatLabel": "设置格式允许您控制特定值的显示方式。其还会导致值完全更改,并阻止 Discover 中的突出显示起作用。",
"indexPatternManagement.frozenLabel": "已冻结",
"indexPatternManagement.indexLabel": "索引",
"indexPatternManagement.indexNameLabel": "索引名称",
@@ -2801,19 +2799,6 @@
"indexPatternManagement.indexPatternTable.indexPatternExplanation": "创建和管理帮助您从 Elasticsearch 中检索数据的索引模式。",
"indexPatternManagement.indexPatternTable.title": "索引模式",
"indexPatternManagement.labelHelpText": "设置此字段在 Discover、Maps 和 Visualize 中显示时要使用的定制标签。当前查询和筛选不支持定制标签,将使用原始字段名称。",
- "indexPatternManagement.labelTemplate.example.idLabel": "用户 #{value}",
- "indexPatternManagement.labelTemplate.example.output.idLabel": "用户",
- "indexPatternManagement.labelTemplate.example.output.pathLabel": "查看资产",
- "indexPatternManagement.labelTemplate.example.pathLabel": "查看资产",
- "indexPatternManagement.labelTemplate.examplesHeader": "示例",
- "indexPatternManagement.labelTemplate.inputHeader": "输入",
- "indexPatternManagement.labelTemplate.labelHeader": "标签模板",
- "indexPatternManagement.labelTemplate.outputHeader": "输出",
- "indexPatternManagement.labelTemplate.urlHeader": "URL 模板",
- "indexPatternManagement.labelTemplate.urlLabel": "格式化 URL",
- "indexPatternManagement.labelTemplate.valueLabel": "字段值",
- "indexPatternManagement.labelTemplateHeader": "标签模板",
- "indexPatternManagement.labelTemplateLabel": "如果此字段中的 URL 很长,为 URL 的文本版本提供备选模板可能会很有用。该文本将会显示,而非显示该 url,但仍会链接到该 URL。该格式是使用双大括号表示法 {doubleCurlyBraces} 来注入值的字符串。可以访问以下值:",
"indexPatternManagement.languageLabel": "语言",
"indexPatternManagement.mappingConflictLabel.mappingConflictDetail": "{mappingConflict}您已经有名称为 {fieldName} 的字段。使用相同的名称命名您的脚本字段意味着您将无法同时查找两个字段。",
"indexPatternManagement.mappingConflictLabel.mappingConflictLabel": "映射冲突:",
@@ -2821,27 +2806,27 @@
"indexPatternManagement.nameErrorMessage": "“名称”必填",
"indexPatternManagement.nameLabel": "名称",
"indexPatternManagement.namePlaceholder": "新建脚本字段",
- "indexPatternManagement.number.documentationLabel": "文档",
- "indexPatternManagement.number.numeralLabel": "Numeral.js 格式模式(默认值:{defaultPattern})",
+ "indexPatternFieldEditor.number.documentationLabel": "文档",
+ "indexPatternFieldEditor.number.numeralLabel": "Numeral.js 格式模式(默认值:{defaultPattern})",
"indexPatternManagement.popularityLabel": "常见度",
- "indexPatternManagement.samples.inputHeader": "输入",
- "indexPatternManagement.samples.outputHeader": "输出",
- "indexPatternManagement.samplesHeader": "样例",
+ "indexPatternFieldEditor.samples.inputHeader": "输入",
+ "indexPatternFieldEditor.samples.outputHeader": "输出",
+ "indexPatternFieldEditor.samplesHeader": "样例",
"indexPatternManagement.script.accessWithLabel": "使用 {code} 访问字段。",
"indexPatternManagement.script.getHelpLabel": "获取该语法的帮助,预览脚本的结果。",
"indexPatternManagement.scriptingLanguages.errorFetchingToastDescription": "从 Elasticsearch 获取可用的脚本语言时出错",
"indexPatternManagement.scriptInvalidErrorMessage": "脚本无效。查看脚本预览以了解详情",
"indexPatternManagement.scriptLabel": "脚本",
"indexPatternManagement.scriptRequiredErrorMessage": "“脚本”必填",
- "indexPatternManagement.staticLookup.actions": "操作",
- "indexPatternManagement.staticLookup.addEntryButton": "添加条目",
- "indexPatternManagement.staticLookup.deleteAria": "删除",
- "indexPatternManagement.staticLookup.deleteTitle": "删除条目",
- "indexPatternManagement.staticLookup.keyLabel": "键",
- "indexPatternManagement.staticLookup.leaveBlankPlaceholder": "留空可使值保持原样",
- "indexPatternManagement.staticLookup.unknownKeyLabel": "未知键的值",
- "indexPatternManagement.staticLookup.valueLabel": "值",
- "indexPatternManagement.string.transformLabel": "转换",
+ "indexPatternFieldEditor.staticLookup.actions": "操作",
+ "indexPatternFieldEditor.staticLookup.addEntryButton": "添加条目",
+ "indexPatternFieldEditor.staticLookup.deleteAria": "删除",
+ "indexPatternFieldEditor.staticLookup.deleteTitle": "删除条目",
+ "indexPatternFieldEditor.staticLookup.keyLabel": "键",
+ "indexPatternFieldEditor.staticLookup.leaveBlankPlaceholder": "留空可使值保持原样",
+ "indexPatternFieldEditor.staticLookup.unknownKeyLabel": "未知键的值",
+ "indexPatternFieldEditor.staticLookup.valueLabel": "值",
+ "indexPatternFieldEditor.string.transformLabel": "转换",
"indexPatternManagement.syntax.default.formatLabel": "doc['some_field'].value",
"indexPatternManagement.syntax.defaultLabel.defaultDetail": "默认情况下,Kibana 脚本字段使用 {painless}(一种简单且安全的脚本语言,专用于 Elasticsearch)通过以下格式访问文档中的值:",
"indexPatternManagement.syntax.defaultLabel.painlessLink": "Painless",
@@ -2872,27 +2857,18 @@
"indexPatternManagement.testScript.resultsLabel": "前 10 个结果",
"indexPatternManagement.testScript.resultsTitle": "预览结果",
"indexPatternManagement.testScript.submitButtonLabel": "运行脚本",
- "indexPatternManagement.truncate.lengthLabel": "字段长度",
+ "indexPatternFieldEditor.truncate.lengthLabel": "字段长度",
"indexPatternManagement.typeLabel": "类型",
- "indexPatternManagement.url.heightLabel": "高",
- "indexPatternManagement.url.labelTemplateHelpText": "标签模板帮助",
- "indexPatternManagement.url.labelTemplateLabel": "标签模板",
- "indexPatternManagement.url.offLabel": "关闭",
- "indexPatternManagement.url.onLabel": "开启",
- "indexPatternManagement.url.openTabLabel": "在新选项卡中打开",
- "indexPatternManagement.url.template.helpLinkText": "URL 模板帮助",
- "indexPatternManagement.url.typeLabel": "类型",
- "indexPatternManagement.url.urlTemplateLabel": "URL 模板",
- "indexPatternManagement.url.widthLabel": "宽",
- "indexPatternManagement.urlTemplate.examplesHeader": "示例",
- "indexPatternManagement.urlTemplate.inputHeader": "输入",
- "indexPatternManagement.urlTemplate.outputHeader": "输出",
- "indexPatternManagement.urlTemplate.rawValueLabel": "非转义值",
- "indexPatternManagement.urlTemplate.templateHeader": "模板",
- "indexPatternManagement.urlTemplate.valueLabel": "URI 转义值",
- "indexPatternManagement.urlTemplateHeader": "Url 模板",
- "indexPatternManagement.urlTemplateLabel.fieldDetail": "如果字段仅包含 URL 的一部分,则 {strongUrlTemplate} 可用于将该值格式化为完整的 URL。该格式是使用双大括号表示法 {doubleCurlyBraces} 来注入值的字符串。可以访问以下值:",
- "indexPatternManagement.urlTemplateLabel.strongUrlTemplateLabel": "Url 模板",
+ "indexPatternFieldEditor.url.heightLabel": "高",
+ "indexPatternFieldEditor.url.labelTemplateHelpText": "标签模板帮助",
+ "indexPatternFieldEditor.url.labelTemplateLabel": "标签模板",
+ "indexPatternFieldEditor.url.offLabel": "关闭",
+ "indexPatternFieldEditor.url.onLabel": "开启",
+ "indexPatternFieldEditor.url.openTabLabel": "在新选项卡中打开",
+ "indexPatternFieldEditor.url.template.helpLinkText": "URL 模板帮助",
+ "indexPatternFieldEditor.url.typeLabel": "类型",
+ "indexPatternFieldEditor.url.urlTemplateLabel": "URL 模板",
+ "indexPatternFieldEditor.url.widthLabel": "宽",
"indexPatternManagement.warningCallOut.descriptionLabel": "脚本字段可用于显示并聚合计算值。因此,它们会很慢,如果操作不当,会导致 Kibana 不可用。此处没有安全网。如果拼写错误,则在任何地方都会引发异常!",
"indexPatternManagement.warningCallOutHeader": "谨慎操作",
"indexPatternManagement.warningCallOutLabel.callOutDetail": "请先熟悉{scripFields}以及{scriptsInAggregation},然后再使用脚本字段。",
diff --git a/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js b/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js
index 0cad97e268dda0..4fd7c2cc2f0679 100644
--- a/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js
+++ b/x-pack/test/functional/apps/rollup_job/hybrid_index_pattern.js
@@ -99,7 +99,7 @@ export default function ({ getService, getPageObjects }) {
// ensure all fields are available
await PageObjects.settings.clickIndexPatternByName(rollupIndexPatternName);
const fields = await PageObjects.settings.getFieldNames();
- expect(fields).to.eql(['_source', '_id', '_type', '_index', '_score', '@timestamp']);
+ expect(fields).to.eql(['@timestamp', '_id', '_index', '_score', '_source', '_type']);
});
after(async () => {