[Visualize] New visualization wizard (#79627) (#82858)

* [Visualizations] New vis wizard

* Update functional tests

* Create oss plugins for maps and lens and unregister alias function

* Add new plugins to .i18nrc.json

* Add readme and codeowners to the new plugins

* update docs

* fix tests

* fix types

* fixes

* Update development docs

* fix oss functional tests

* Fix jest and x-pack functional tests

* Fix functional test

* changes on the layout

* Cleanup and responsiveness

* cleanup unecessary code

* add common folder to the new OSS plugins

* remove unecessary translations

* Update limits.yml file

* Fix basic label

* Add experimental badge on controls vis

* Nice improvements

* fixes

* Improving styles

* Making modal go full height on smaller screens

* Fixing sass lint warning

* fix lint error

* fix internationalization error

* PR fixes

* PR changes

* Use useCallback where possible

* Remove translations that need to be translated again

* Lazy Load wizard modal

* Remove legacyMapVisualizationWarning

* Import the OSS plugins constants from the plugins

* Export constant from lensOss

* Change the new oss plugins from OSS to Oss

* Add a new line to the kibana.json files of the new plugins

* New nit fix

* Fix spaces

* Change the texts for the first step of the modal

* Fix test

* Fixes some of the PR comments

* Add onClick funtionality to the entire aggregation based card

* Cards description changes, introduce a copyFromRoot method to solve the problem of when disabling the x-pack plugic, to also disable the oss

* Create new FTR for testing the functionality of the wizard when both maps and lens apps are disabled

* fix eslint error

* Change groupTitles and descriptions

* Change input vis description

* Remove the copyFromRoot from the signature of the ConfigDeprecationFactory and export it from the main entrypoint

* Make the disabled cards badge clickable

* Changes from code review

* Fix functional tests failures

* Rename groupTitle to titleInWizard to be more specific

* Change vega vis note

* minor design changes

* fix problem with plugins list docs

* Retrieve maps and lens landing page from docs service and add tracking url param

* Fix funtional test for the new dashboard flow

* Fix logic in alias registry for removing the discardOnRegister alias

* no need to remove the alias entry from the discardOnRegister array

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: miukimiu <elizabet.oliveira@elastic.co>
# Conflicts:
#	.github/CODEOWNERS
#	docs/developer/plugin-list.asciidoc
#	packages/kbn-optimizer/limits.yml
#	x-pack/scripts/functional_tests.js
This commit is contained in:
Stratoula Kalafateli 2020-11-06 20:49:10 +02:00 committed by GitHub
parent 0deed8eb29
commit 73d2de6503
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
109 changed files with 2455 additions and 2910 deletions

399
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1,399 @@
# GitHub CODEOWNERS definition
# Identify which groups will be pinged by changes to different parts of the codebase.
# For more info, see https://help.github.com/articles/about-codeowners/
# The #CC# prefix delineates Code Coverage,
# used for the 'team' designator within Kibana Stats
# App
/x-pack/plugins/discover_enhanced/ @elastic/kibana-app
/x-pack/plugins/lens/ @elastic/kibana-app
/x-pack/plugins/graph/ @elastic/kibana-app
/src/plugins/advanced_settings/ @elastic/kibana-app
/src/plugins/charts/ @elastic/kibana-app
/src/plugins/discover/ @elastic/kibana-app
/src/plugins/lens_oss/ @elastic/kibana-app
/src/plugins/management/ @elastic/kibana-app
/src/plugins/kibana_legacy/ @elastic/kibana-app
/src/plugins/timelion/ @elastic/kibana-app
/src/plugins/vis_default_editor/ @elastic/kibana-app
/src/plugins/vis_type_metric/ @elastic/kibana-app
/src/plugins/vis_type_table/ @elastic/kibana-app
/src/plugins/vis_type_tagcloud/ @elastic/kibana-app
/src/plugins/vis_type_timelion/ @elastic/kibana-app
/src/plugins/vis_type_timeseries/ @elastic/kibana-app
/src/plugins/vis_type_vega/ @elastic/kibana-app
/src/plugins/vis_type_vislib/ @elastic/kibana-app
/src/plugins/vis_type_xy/ @elastic/kibana-app
/src/plugins/visualize/ @elastic/kibana-app
/src/plugins/visualizations/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/common/utils @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/migrations @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/discover/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/kibana/public/local_application_service/ @elastic/kibana-app
#CC# /src/legacy/core_plugins/timelion @elastic/kibana-app
#CC# /src/legacy/core_plugins/vis_type_tagcloud @elastic/kibana-app
#CC# /src/legacy/core_plugins/vis_type_vega @elastic/kibana-app
#CC# /src/legacy/core_plugins/vis_type_vislib/ @elastic/kibana-app
#CC# /src/legacy/server/url_shortening/ @elastic/kibana-app
#CC# /src/legacy/ui/public/state_management @elastic/kibana-app
# App Architecture
/examples/bfetch_explorer/ @elastic/kibana-app-arch
/examples/dashboard_embeddable_examples/ @elastic/kibana-app-arch
/examples/demo_search/ @elastic/kibana-app-arch
/examples/developer_examples/ @elastic/kibana-app-arch
/examples/embeddable_examples/ @elastic/kibana-app-arch
/examples/embeddable_explorer/ @elastic/kibana-app-arch
/examples/state_container_examples/ @elastic/kibana-app-arch
/examples/ui_actions_examples/ @elastic/kibana-app-arch
/examples/ui_actions_explorer/ @elastic/kibana-app-arch
/examples/url_generators_examples/ @elastic/kibana-app-arch
/examples/url_generators_explorer/ @elastic/kibana-app-arch
/packages/elastic-datemath/ @elastic/kibana-app-arch
/packages/kbn-interpreter/ @elastic/kibana-app-arch
/src/plugins/bfetch/ @elastic/kibana-app-arch
/src/plugins/data/ @elastic/kibana-app-arch
/src/plugins/embeddable/ @elastic/kibana-app-arch
/src/plugins/expressions/ @elastic/kibana-app-arch
/src/plugins/inspector/ @elastic/kibana-app-arch
/src/plugins/kibana_react/ @elastic/kibana-app-arch
/src/plugins/kibana_react/public/code_editor @elastic/kibana-presentation
/src/plugins/kibana_utils/ @elastic/kibana-app-arch
/src/plugins/navigation/ @elastic/kibana-app-arch
/src/plugins/share/ @elastic/kibana-app-arch
/src/plugins/ui_actions/ @elastic/kibana-app-arch
/x-pack/examples/ui_actions_enhanced_examples/ @elastic/kibana-app-arch
/x-pack/plugins/data_enhanced/ @elastic/kibana-app-arch
/x-pack/plugins/embeddable_enhanced/ @elastic/kibana-app-arch
/x-pack/plugins/ui_actions_enhanced/ @elastic/kibana-app-arch
#CC# /src/plugins/bfetch/ @elastic/kibana-app-arch
#CC# /src/plugins/index_pattern_management/ @elastic/kibana-app-arch
#CC# /src/plugins/inspector/ @elastic/kibana-app-arch
#CC# /src/plugins/share/ @elastic/kibana-app-arch
#CC# /x-pack/plugins/advanced_ui_actions/ @elastic/kibana-app-arch
#CC# /x-pack/plugins/drilldowns/ @elastic/kibana-app-arch
#CC# /packages/kbn-interpreter/ @elastic/kibana-app-arch
# APM
/x-pack/plugins/apm/ @elastic/apm-ui
/x-pack/test/functional/apps/apm/ @elastic/apm-ui
/src/plugins/apm_oss/ @elastic/apm-ui
/src/apm.js @watson @vigneshshanmugam
#CC# /src/plugins/apm_oss/ @elastic/apm-ui
#CC# /src/legacy/core_plugins/apm_oss/ @elastic/apm-ui
#CC# /src/legacy/ui/public/apm @elastic/apm-ui
#CC# /x-pack/legacy/plugins/apm/ @elastic/apm-ui
#CC# /x-pack/plugins/observability/ @elastic/apm-ui
# Client Side Monitoring (lives in APM directories but owned by Uptime)
/x-pack/plugins/apm/e2e/cypress/support/step_definitions/csm @elastic/uptime
/x-pack/plugins/apm/e2e/cypress/integration/csm_dashboard.feature @elastic/uptime
/x-pack/plugins/apm/public/application/csmApp.tsx @elastic/uptime
/x-pack/plugins/apm/public/components/app/RumDashboard @elastic/uptime
/x-pack/plugins/apm/server/lib/rum_client @elastic/uptime
/x-pack/plugins/apm/server/routes/rum_client.ts @elastic/uptime
/x-pack/plugins/apm/server/projections/rum_page_load_transactions.ts @elastic/uptime
/x-pack/plugins/apm/server/projections/rum_overview.ts @elastic/uptime
#CC# /x-pack/legacy/plugins/uptime @elastic/uptime
# Beats
/x-pack/plugins/beats_management/ @elastic/beats
/x-pack/legacy/plugins/beats_management/ @elastic/beats
#CC# /x-pack/plugins/beats_management/ @elastic/beats
# Presentation
/src/plugins/dashboard/ @elastic/kibana-presentation
/src/plugins/input_control_vis/ @elastic/kibana-presentation
/src/plugins/vis_type_markdown/ @elastic/kibana-presentation
/x-pack/plugins/canvas/ @elastic/kibana-presentation
/x-pack/plugins/dashboard_enhanced/ @elastic/kibana-presentation
/x-pack/test/functional/apps/canvas/ @elastic/kibana-presentation
#CC# /src/legacy/core_plugins/kibana/public/dashboard/ @elastic/kibana-presentation
#CC# /src/legacy/core_plugins/input_control_vis @elastic/kibana-presentation
#CC# /src/plugins/kibana_react/public/code_editor/ @elastic/kibana-presentation
#CC# /x-pack/legacy/plugins/canvas/ @elastic/kibana-presentation
#CC# /x-pack/plugins/dashboard_mode @elastic/kibana-presentation
#CC# /x-pack/legacy/plugins/dashboard_mode/ @elastic/kibana-presentation
# Core UI
# Exclude tutorials folder for now because they are not owned by Kibana app and most will move out soon
/src/plugins/home/public @elastic/kibana-core-ui
/src/plugins/home/server/*.ts @elastic/kibana-core-ui
/src/plugins/home/server/services/ @elastic/kibana-core-ui
/src/plugins/kibana_overview/ @elastic/kibana-core-ui
/x-pack/plugins/global_search_bar/ @elastic/kibana-core-ui
#CC# /src/legacy/core_plugins/newsfeed @elastic/kibana-core-ui
#CC# /src/legacy/server/sample_data/ @elastic/kibana-core-ui
#CC# /src/plugins/newsfeed @elastic/kibana-core-ui
#CC# /src/plugins/home/public @elastic/kibana-core-ui
#CC# /src/plugins/home/server/services/ @elastic/kibana-core-ui
#CC# /src/plugins/home/ @elastic/kibana-core-ui
#CC# /x-pack/plugins/global_search_providers/ @elastic/kibana-core-ui
# Observability UIs
/x-pack/plugins/infra/ @elastic/logs-metrics-ui
/x-pack/plugins/ingest_manager/ @elastic/ingest-management
/x-pack/plugins/observability/ @elastic/observability-ui
/x-pack/plugins/monitoring/ @elastic/stack-monitoring-ui
/x-pack/plugins/uptime @elastic/uptime
# Machine Learning
/x-pack/plugins/ml/ @elastic/ml-ui
/x-pack/test/functional/apps/machine_learning/ @elastic/ml-ui
/x-pack/test/functional/services/machine_learning/ @elastic/ml-ui
/x-pack/test/functional/services/ml.ts @elastic/ml-ui
# ML team owns and maintains the transform plugin despite it living in the Elasticsearch management section.
/x-pack/plugins/transform/ @elastic/ml-ui
/x-pack/test/functional/apps/transform/ @elastic/ml-ui
/x-pack/test/functional/services/transform_ui/ @elastic/ml-ui
/x-pack/test/functional/services/transform.ts @elastic/ml-ui
# Maps
/x-pack/plugins/maps/ @elastic/kibana-gis
/x-pack/test/api_integration/apis/maps/ @elastic/kibana-gis
/x-pack/test/functional/apps/maps/ @elastic/kibana-gis
/x-pack/test/functional/es_archives/maps/ @elastic/kibana-gis
/x-pack/test/visual_regression/tests/maps/index.js @elastic/kibana-gis
#CC# /src/plugins/maps_legacy/ @elastic/kibana-gis
#CC# /x-pack/plugins/file_upload @elastic/kibana-gis
#CC# /x-pack/plugins/maps_legacy_licensing @elastic/kibana-gis
#CC# /src/plugins/home/server/tutorials @elastic/kibana-gis
#CC# /src/plugins/tile_map/ @elastic/kibana-gis
#CC# /src/plugins/region_map/ @elastic/kibana-gis
# Operations
/src/dev/ @elastic/kibana-operations
/src/setup_node_env/ @elastic/kibana-operations
/src/optimize/ @elastic/kibana-operations
/packages/*eslint*/ @elastic/kibana-operations
/packages/*babel*/ @elastic/kibana-operations
/packages/kbn-dev-utils*/ @elastic/kibana-operations
/packages/kbn-es/ @elastic/kibana-operations
/packages/kbn-optimizer/ @elastic/kibana-operations
/packages/kbn-pm/ @elastic/kibana-operations
/packages/kbn-test/ @elastic/kibana-operations
/packages/kbn-ui-shared-deps/ @elastic/kibana-operations
/packages/kbn-es-archiver/ @elastic/kibana-operations
/packages/kbn-utils/ @elastic/kibana-operations
/src/legacy/server/keystore/ @elastic/kibana-operations
/src/legacy/server/pid/ @elastic/kibana-operations
/src/legacy/server/sass/ @elastic/kibana-operations
/src/legacy/server/utils/ @elastic/kibana-operations
/src/legacy/server/warnings/ @elastic/kibana-operations
/.ci/es-snapshots/ @elastic/kibana-operations
/vars/ @elastic/kibana-operations
#CC# /packages/kbn-expect/ @elastic/kibana-operations
# Quality Assurance
/src/dev/code_coverage @elastic/kibana-qa
/vars/*Coverage.groovy @elastic/kibana-qa
/test/functional/services/common @elastic/kibana-qa
/test/functional/services/lib @elastic/kibana-qa
/test/functional/services/remote @elastic/kibana-qa
# Platform
/src/core/ @elastic/kibana-platform
/src/plugins/saved_objects_tagging_oss @elastic/kibana-platform
/config/kibana.yml @elastic/kibana-platform
/x-pack/plugins/features/ @elastic/kibana-platform
/x-pack/plugins/licensing/ @elastic/kibana-platform
/x-pack/plugins/global_search/ @elastic/kibana-platform
/x-pack/plugins/cloud/ @elastic/kibana-platform
/x-pack/plugins/saved_objects_tagging/ @elastic/kibana-platform
/x-pack/test/saved_objects_field_count/ @elastic/kibana-platform
/x-pack/test/saved_object_tagging/ @elastic/kibana-platform
/packages/kbn-config-schema/ @elastic/kibana-platform
/packages/kbn-std/ @elastic/kibana-platform
/src/legacy/server/config/ @elastic/kibana-platform
/src/legacy/server/http/ @elastic/kibana-platform
/src/legacy/server/logging/ @elastic/kibana-platform
/src/legacy/server/saved_objects/ @elastic/kibana-platform
/src/legacy/server/status/ @elastic/kibana-platform
/src/plugins/status_page/ @elastic/kibana-platform
/src/plugins/saved_objects_management/ @elastic/kibana-platform
/src/dev/run_check_published_api_changes.ts @elastic/kibana-platform
#CC# /src/core/server/csp/ @elastic/kibana-platform
#CC# /src/legacy/core_plugins/kibana/server/lib @elastic/kibana-platform
#CC# /src/legacy/core_plugins/kibana/server/lib/management/saved_objects @elastic/kibana-platform
#CC# /src/legacy/core_plugins/kibana/server/routes/api/import/ @elastic/kibana-platform
#CC# /src/legacy/core_plugins/kibana/server/routes/api/export/ @elastic/kibana-platform
#CC# /src/legacy/core_plugins/elasticsearch @elastic/kibana-platform
#CC# /src/legacy/core_plugins/testbed @elastic/kibana-platform
#CC# /src/legacy/server/config/ @elastic/kibana-platform
#CC# /src/legacy/server/http/ @elastic/kibana-platform
#CC# /src/legacy/server/status/ @elastic/kibana-platform
#CC# /src/legacy/ui/public/new_platform @elastic/kibana-platform
#CC# /src/legacy/ui/public/plugin_discovery @elastic/kibana-platform
#CC# /src/legacy/ui/public/chrome @elastic/kibana-platform
#CC# /src/legacy/ui/public/notify @elastic/kibana-platform
#CC# /src/legacy/ui/public/documentation_links @elastic/kibana-platform
#CC# /src/legacy/ui/public/autoload @elastic/kibana-platform
#CC# /src/plugins/legacy_export/ @elastic/kibana-platform
#CC# /src/plugins/saved_objects/ @elastic/kibana-platform
#CC# /src/plugins/status_page/ @elastic/kibana-platform
#CC# /src/plugins/testbed/server/ @elastic/kibana-platform
#CC# /x-pack/legacy/plugins/xpack_main/server/ @elastic/kibana-platform
#CC# /x-pack/legacy/server/lib/ @elastic/kibana-platform
#CC# /x-pack/plugins/cloud/ @elastic/kibana-platform
#CC# /x-pack/plugins/features/ @elastic/kibana-platform
#CC# /x-pack/plugins/global_search/ @elastic/kibana-platform
#CC# /src/legacy/plugin_discovery/ @elastic/kibana-platform
# Security
/src/core/server/csp/ @elastic/kibana-security @elastic/kibana-platform
/src/plugins/security_oss/ @elastic/kibana-security
/test/security_functional/ @elastic/kibana-security
/x-pack/plugins/spaces/ @elastic/kibana-security
/x-pack/plugins/encrypted_saved_objects/ @elastic/kibana-security
/x-pack/plugins/security/ @elastic/kibana-security
/x-pack/test/api_integration/apis/security/ @elastic/kibana-security
/x-pack/test/ui_capabilities/ @elastic/kibana-security
/x-pack/test/encrypted_saved_objects_api_integration/ @elastic/kibana-security
/x-pack/test/functional/apps/security/ @elastic/kibana-security
/x-pack/test/kerberos_api_integration/ @elastic/kibana-security
/x-pack/test/oidc_api_integration/ @elastic/kibana-security
/x-pack/test/pki_api_integration/ @elastic/kibana-security
/x-pack/test/security_api_integration/ @elastic/kibana-security
/x-pack/test/security_functional/ @elastic/kibana-security
/x-pack/test/spaces_api_integration/ @elastic/kibana-security
/x-pack/test/token_api_integration/ @elastic/kibana-security
#CC# /src/legacy/ui/public/capabilities @elastic/kibana-security
#CC# /x-pack/legacy/plugins/encrypted_saved_objects/ @elastic/kibana-security
#CC# /x-pack/plugins/security_solution/ @elastic/kibana-security
#CC# /x-pack/plugins/security/ @elastic/kibana-security
#CC# /x-pack/plugins/audit_trail/ @elastic/kibana-security
# Kibana Localization
/src/dev/i18n/ @elastic/kibana-localization
/src/legacy/server/i18n/ @elastic/kibana-localization
/src/core/public/i18n/ @elastic/kibana-localization
/packages/kbn-i18n/ @elastic/kibana-localization
#CC# /src/legacy/server/i18n/ @elastic/kibana-localization
#CC# /x-pack/plugins/translations/ @elastic/kibana-localization
# Kibana Telemetry
/packages/kbn-analytics/ @elastic/kibana-telemetry
/packages/kbn-telemetry-tools/ @elastic/kibana-telemetry
/src/plugins/kibana_usage_collection/ @elastic/kibana-telemetry
/src/plugins/newsfeed/ @elastic/kibana-telemetry
/src/plugins/telemetry/ @elastic/kibana-telemetry
/src/plugins/telemetry_collection_manager/ @elastic/kibana-telemetry
/src/plugins/telemetry_management_section/ @elastic/kibana-telemetry
/src/plugins/usage_collection/ @elastic/kibana-telemetry
/x-pack/plugins/telemetry_collection_xpack/ @elastic/kibana-telemetry
/.telemetryrc.json @elastic/kibana-telemetry
/x-pack/.telemetryrc.json @elastic/kibana-telemetry
src/plugins/telemetry/schema/legacy_oss_plugins.json @elastic/kibana-telemetry
src/plugins/telemetry/schema/oss_plugins.json @elastic/kibana-telemetry
x-pack/plugins/telemetry_collection_xpack/schema/xpack_plugins.json @elastic/kibana-telemetry
# Kibana Alerting Services
/x-pack/plugins/alerts/ @elastic/kibana-alerting-services
/x-pack/plugins/actions/ @elastic/kibana-alerting-services
/x-pack/plugins/event_log/ @elastic/kibana-alerting-services
/x-pack/plugins/task_manager/ @elastic/kibana-alerting-services
/x-pack/test/alerting_api_integration/ @elastic/kibana-alerting-services
/x-pack/test/plugin_api_integration/plugins/task_manager/ @elastic/kibana-alerting-services
/x-pack/test/plugin_api_integration/test_suites/task_manager/ @elastic/kibana-alerting-services
/x-pack/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services
/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/ @elastic/kibana-alerting-services
/x-pack/test/functional_with_es_ssl/fixtures/plugins/alerts/ @elastic/kibana-alerting-services
#CC# /x-pack/legacy/plugins/actions/ @elastic/kibana-alerting-services
#CC# /x-pack/legacy/plugins/alerting/ @elastic/kibana-alerting-services
#CC# /x-pack/legacy/plugins/task_manager @elastic/kibana-alerting-services
#CC# /x-pack/legacy/plugins/triggers_actions_ui/ @elastic/kibana-alerting-services
#CC# /x-pack/plugins/stack_alerts @elastic/kibana-alerting-services
# Enterprise Search
# Shared
/x-pack/plugins/enterprise_search/ @elastic/enterprise-search-frontend
/x-pack/test/functional_enterprise_search/ @elastic/enterprise-search-frontend
# Elasticsearch UI
/src/plugins/dev_tools/ @elastic/es-ui
/src/plugins/console/ @elastic/es-ui
/src/plugins/es_ui_shared/ @elastic/es-ui
/x-pack/plugins/cross_cluster_replication/ @elastic/es-ui
/x-pack/plugins/index_lifecycle_management/ @elastic/es-ui
/x-pack/plugins/console_extensions/ @elastic/es-ui
/x-pack/plugins/es_ui_shared/ @elastic/es-ui
/x-pack/plugins/grokdebugger/ @elastic/es-ui
/x-pack/plugins/index_management/ @elastic/es-ui
/x-pack/plugins/license_management/ @elastic/es-ui
/x-pack/plugins/painless_lab/ @elastic/es-ui
/x-pack/plugins/remote_clusters/ @elastic/es-ui
/x-pack/plugins/rollup/ @elastic/es-ui
/x-pack/plugins/searchprofiler/ @elastic/es-ui
/x-pack/plugins/snapshot_restore/ @elastic/es-ui
/x-pack/plugins/upgrade_assistant/ @elastic/es-ui
/x-pack/plugins/watcher/ @elastic/es-ui
/x-pack/plugins/ingest_pipelines/ @elastic/es-ui
/packages/kbn-ace/ @elastic/es-ui
/packages/kbn-monaco/ @elastic/es-ui
#CC# /src/legacy/core_plugins/kibana/public/dev_tools/ @elastic/es-ui
#CC# /src/legacy/core_plugins/console_legacy @elastic/es-ui
#CC# /x-pack/legacy/plugins/rollup/ @elastic/es-ui
#CC# /x-pack/legacy/server/lib/create_router/ @elastic/es-ui
#CC# /x-pack/legacy/server/lib/check_license/ @elastic/es-ui
#CC# /x-pack/plugins/console_extensions/ @elastic/es-ui
#CC# /x-pack/plugins/cross_cluster_replication/ @elastic/es-ui
#CC# /x-pack/plugins/es_ui_shared/ @elastic/es-u
# Endpoint
/x-pack/plugins/endpoint/ @elastic/endpoint-app-team @elastic/siem
/x-pack/test/api_integration/apis/endpoint/ @elastic/endpoint-app-team @elastic/siem
/x-pack/test/endpoint_api_integration_no_ingest/ @elastic/endpoint-app-team @elastic/siem
/x-pack/test/security_solution_endpoint/ @elastic/endpoint-app-team @elastic/siem
/x-pack/test/functional/es_archives/endpoint/ @elastic/endpoint-app-team @elastic/siem
/x-pack/test/plugin_functional/plugins/resolver_test/ @elastic/endpoint-app-team @elastic/siem
/x-pack/test/plugin_functional/test_suites/resolver/ @elastic/endpoint-app-team @elastic/siem
#CC# /x-pack/legacy/plugins/siem/ @elastic/siem
#CC# /x-pack/plugins/siem/ @elastic/siem
#CC# /x-pack/plugins/security_solution/ @elastic/siem
# Security Solution
/x-pack/plugins/security_solution/ @elastic/siem @elastic/endpoint-app-team
/x-pack/test/detection_engine_api_integration @elastic/siem @elastic/endpoint-app-team
/x-pack/test/lists_api_integration @elastic/siem @elastic/endpoint-app-team
/x-pack/test/api_integration/apis/security_solution @elastic/siem @elastic/endpoint-app-team
/x-pack/plugins/case @elastic/siem @elastic/endpoint-app-team
/x-pack/plugins/lists @elastic/siem @elastic/endpoint-app-team
# Security Intelligence And Analytics
/x-pack/plugins/security_solution/server/lib/detection_engine/rules/prepackaged_rules @elastic/security-intelligence-analytics
# Design (at the bottom for specificity of SASS files)
**/*.scss @elastic/kibana-design
#CC# /packages/kbn-ui-framework/ @elastic/kibana-design
# Core UI design
/src/plugins/dashboard/**/*.scss @elastic/kibana-core-ui-designers
/src/plugins/embeddable/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/plugins/canvas/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/plugins/spaces/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/plugins/security/**/*.scss @elastic/kibana-core-ui-designers
# Observability design
/x-pack/plugins/apm/**/*.scss @elastic/observability-design
/x-pack/plugins/infra/**/*.scss @elastic/observability-design
/x-pack/plugins/ingest_manager/**/*.scss @elastic/observability-design
/x-pack/plugins/observability/**/*.scss @elastic/observability-design
/x-pack/plugins/monitoring/**/*.scss @elastic/observability-design
# Ent. Search design
/x-pack/plugins/enterprise_search/**/*.scss @elastic/ent-search-design
# Security design
/x-pack/plugins/endpoint/**/*.scss @elastic/security-design
/x-pack/plugins/security_solution/**/*.scss @elastic/security-design
# Logstash
#CC# /x-pack/plugins/logstash/ @elastic/logstash
# Reporting
#CC# /x-pack/plugins/reporting/ @elastic/kibana-reporting-services

View file

@ -59,6 +59,8 @@
"visTypeVislib": "src/plugins/vis_type_vislib",
"visTypeXy": "src/plugins/vis_type_xy",
"visualizations": "src/plugins/visualizations",
"lensOss": "src/plugins/lens_oss",
"mapsOss": "src/plugins/maps_oss",
"visualize": "src/plugins/visualize",
"apmOss": "src/plugins/apm_oss",
"usageCollection": "src/plugins/usage_collection"

View file

@ -6,6 +6,7 @@ files:
- 'src/plugins/vis_type_vislib/**/*.s+(a|c)ss'
- 'src/plugins/vis_type_vega/**/*.s+(a|c)ss'
- 'src/plugins/vis_type_xy/**/*.s+(a|c)ss'
- 'src/plugins/visualizations/public/wizard/**/*.s+(a|c)ss'
- 'x-pack/plugins/canvas/**/*.s+(a|c)ss'
- 'x-pack/plugins/triggers_actions_ui/**/*.s+(a|c)ss'
- 'x-pack/plugins/lens/**/*.s+(a|c)ss'

View file

@ -128,6 +128,11 @@ in Kibana, e.g. visualizations. It has the form of a flyout panel.
|The legacyExport plugin adds support for the legacy saved objects export format.
|{kib-repo}blob/{branch}/src/plugins/lens_oss/README.md[lensOss]
|The lens_oss plugin registers the lens visualization on OSS.
It is registered as disabled. The x-pack plugin should unregister this.
|{kib-repo}blob/{branch}/src/plugins/management[management]
|WARNING: Missing README.
@ -136,6 +141,11 @@ in Kibana, e.g. visualizations. It has the form of a flyout panel.
|Internal objects used by the Coordinate, Region, and Vega visualizations.
|{kib-repo}blob/{branch}/src/plugins/maps_oss/README.md[mapsOss]
|The maps_oss plugin registers the maps visualization on OSS.
It is registered as disabled. The x-pack plugin should unregister this.
|{kib-repo}blob/{branch}/src/plugins/navigation/README.md[navigation]
|The navigation plugins exports the TopNavMenu component.
It also provides a stateful version of it on the start contract.

View file

@ -9,6 +9,7 @@
```typescript
readonly links: {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;

View file

@ -17,5 +17,5 @@ export interface DocLinksStart
| --- | --- | --- |
| [DOC\_LINK\_VERSION](./kibana-plugin-core-public.doclinksstart.doc_link_version.md) | <code>string</code> | |
| [ELASTIC\_WEBSITE\_URL](./kibana-plugin-core-public.doclinksstart.elastic_website_url.md) | <code>string</code> | |
| [links](./kibana-plugin-core-public.doclinksstart.links.md) | <code>{</code><br/><code> readonly dashboard: {</code><br/><code> readonly drilldowns: string;</code><br/><code> readonly drilldownsTriggerPicker: string;</code><br/><code> readonly urlDrilldownTemplateSyntax: string;</code><br/><code> readonly urlDrilldownVariables: string;</code><br/><code> };</code><br/><code> readonly filebeat: {</code><br/><code> readonly base: string;</code><br/><code> readonly installation: string;</code><br/><code> readonly configuration: string;</code><br/><code> readonly elasticsearchOutput: string;</code><br/><code> readonly startup: string;</code><br/><code> readonly exportedFields: string;</code><br/><code> };</code><br/><code> readonly auditbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly metricbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly heartbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly logstash: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly functionbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly winlogbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly aggs: {</code><br/><code> readonly date_histogram: string;</code><br/><code> readonly date_range: string;</code><br/><code> readonly filter: string;</code><br/><code> readonly filters: string;</code><br/><code> readonly geohash_grid: string;</code><br/><code> readonly histogram: string;</code><br/><code> readonly ip_range: string;</code><br/><code> readonly range: string;</code><br/><code> readonly significant_terms: string;</code><br/><code> readonly terms: string;</code><br/><code> readonly avg: string;</code><br/><code> readonly avg_bucket: string;</code><br/><code> readonly max_bucket: string;</code><br/><code> readonly min_bucket: string;</code><br/><code> readonly sum_bucket: string;</code><br/><code> readonly cardinality: string;</code><br/><code> readonly count: string;</code><br/><code> readonly cumulative_sum: string;</code><br/><code> readonly derivative: string;</code><br/><code> readonly geo_bounds: string;</code><br/><code> readonly geo_centroid: string;</code><br/><code> readonly max: string;</code><br/><code> readonly median: string;</code><br/><code> readonly min: string;</code><br/><code> readonly moving_avg: string;</code><br/><code> readonly percentile_ranks: string;</code><br/><code> readonly serial_diff: string;</code><br/><code> readonly std_dev: string;</code><br/><code> readonly sum: string;</code><br/><code> readonly top_hits: string;</code><br/><code> };</code><br/><code> readonly scriptedFields: {</code><br/><code> readonly scriptFields: string;</code><br/><code> readonly scriptAggs: string;</code><br/><code> readonly painless: string;</code><br/><code> readonly painlessApi: string;</code><br/><code> readonly painlessSyntax: string;</code><br/><code> readonly luceneExpressions: string;</code><br/><code> };</code><br/><code> readonly indexPatterns: {</code><br/><code> readonly loadingData: string;</code><br/><code> readonly introduction: string;</code><br/><code> };</code><br/><code> readonly addData: string;</code><br/><code> readonly kibana: string;</code><br/><code> readonly siem: {</code><br/><code> readonly guide: string;</code><br/><code> readonly gettingStarted: string;</code><br/><code> };</code><br/><code> readonly query: {</code><br/><code> readonly eql: string;</code><br/><code> readonly luceneQuerySyntax: string;</code><br/><code> readonly queryDsl: string;</code><br/><code> readonly kueryQuerySyntax: string;</code><br/><code> };</code><br/><code> readonly date: {</code><br/><code> readonly dateMath: string;</code><br/><code> };</code><br/><code> readonly management: Record&lt;string, string&gt;;</code><br/><code> readonly visualize: Record&lt;string, string&gt;;</code><br/><code> }</code> | |
| [links](./kibana-plugin-core-public.doclinksstart.links.md) | <code>{</code><br/><code> readonly dashboard: {</code><br/><code> readonly guide: string;</code><br/><code> readonly drilldowns: string;</code><br/><code> readonly drilldownsTriggerPicker: string;</code><br/><code> readonly urlDrilldownTemplateSyntax: string;</code><br/><code> readonly urlDrilldownVariables: string;</code><br/><code> };</code><br/><code> readonly filebeat: {</code><br/><code> readonly base: string;</code><br/><code> readonly installation: string;</code><br/><code> readonly configuration: string;</code><br/><code> readonly elasticsearchOutput: string;</code><br/><code> readonly startup: string;</code><br/><code> readonly exportedFields: string;</code><br/><code> };</code><br/><code> readonly auditbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly metricbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly heartbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly logstash: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly functionbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly winlogbeat: {</code><br/><code> readonly base: string;</code><br/><code> };</code><br/><code> readonly aggs: {</code><br/><code> readonly date_histogram: string;</code><br/><code> readonly date_range: string;</code><br/><code> readonly filter: string;</code><br/><code> readonly filters: string;</code><br/><code> readonly geohash_grid: string;</code><br/><code> readonly histogram: string;</code><br/><code> readonly ip_range: string;</code><br/><code> readonly range: string;</code><br/><code> readonly significant_terms: string;</code><br/><code> readonly terms: string;</code><br/><code> readonly avg: string;</code><br/><code> readonly avg_bucket: string;</code><br/><code> readonly max_bucket: string;</code><br/><code> readonly min_bucket: string;</code><br/><code> readonly sum_bucket: string;</code><br/><code> readonly cardinality: string;</code><br/><code> readonly count: string;</code><br/><code> readonly cumulative_sum: string;</code><br/><code> readonly derivative: string;</code><br/><code> readonly geo_bounds: string;</code><br/><code> readonly geo_centroid: string;</code><br/><code> readonly max: string;</code><br/><code> readonly median: string;</code><br/><code> readonly min: string;</code><br/><code> readonly moving_avg: string;</code><br/><code> readonly percentile_ranks: string;</code><br/><code> readonly serial_diff: string;</code><br/><code> readonly std_dev: string;</code><br/><code> readonly sum: string;</code><br/><code> readonly top_hits: string;</code><br/><code> };</code><br/><code> readonly scriptedFields: {</code><br/><code> readonly scriptFields: string;</code><br/><code> readonly scriptAggs: string;</code><br/><code> readonly painless: string;</code><br/><code> readonly painlessApi: string;</code><br/><code> readonly painlessSyntax: string;</code><br/><code> readonly luceneExpressions: string;</code><br/><code> };</code><br/><code> readonly indexPatterns: {</code><br/><code> readonly loadingData: string;</code><br/><code> readonly introduction: string;</code><br/><code> };</code><br/><code> readonly addData: string;</code><br/><code> readonly kibana: string;</code><br/><code> readonly siem: {</code><br/><code> readonly guide: string;</code><br/><code> readonly gettingStarted: string;</code><br/><code> };</code><br/><code> readonly query: {</code><br/><code> readonly eql: string;</code><br/><code> readonly luceneQuerySyntax: string;</code><br/><code> readonly queryDsl: string;</code><br/><code> readonly kueryQuerySyntax: string;</code><br/><code> };</code><br/><code> readonly date: {</code><br/><code> readonly dateMath: string;</code><br/><code> };</code><br/><code> readonly management: Record&lt;string, string&gt;;</code><br/><code> readonly visualize: Record&lt;string, string&gt;;</code><br/><code> }</code> | |

View file

@ -18,7 +18,7 @@
*/
import { ConfigDeprecationLogger } from './types';
import { configDeprecationFactory } from './deprecation_factory';
import { configDeprecationFactory, copyFromRoot } from './deprecation_factory';
describe('DeprecationFactory', () => {
const { rename, unused, renameFromRoot, unusedFromRoot } = configDeprecationFactory;
@ -250,6 +250,89 @@ describe('DeprecationFactory', () => {
});
});
describe('copyFromRoot', () => {
it('copies a property to a different namespace', () => {
const rawConfig = {
originplugin: {
deprecated: 'toberenamed',
valid: 'valid',
},
destinationplugin: {
property: 'value',
},
};
const processed = copyFromRoot('originplugin.deprecated', 'destinationplugin.renamed')(
rawConfig,
'does-not-matter',
logger
);
expect(processed).toEqual({
originplugin: {
deprecated: 'toberenamed',
valid: 'valid',
},
destinationplugin: {
renamed: 'toberenamed',
property: 'value',
},
});
expect(deprecationMessages.length).toEqual(0);
});
it('does not alter config if origin property is not present', () => {
const rawConfig = {
myplugin: {
new: 'new',
valid: 'valid',
},
someOtherPlugin: {
property: 'value',
},
};
const processed = copyFromRoot('myplugin.deprecated', 'myplugin.new')(
rawConfig,
'does-not-matter',
logger
);
expect(processed).toEqual({
myplugin: {
new: 'new',
valid: 'valid',
},
someOtherPlugin: {
property: 'value',
},
});
expect(deprecationMessages.length).toEqual(0);
});
it('does not alter config if they both exist', () => {
const rawConfig = {
myplugin: {
deprecated: 'deprecated',
renamed: 'renamed',
},
someOtherPlugin: {
property: 'value',
},
};
const processed = copyFromRoot('myplugin.deprecated', 'someOtherPlugin.property')(
rawConfig,
'does-not-matter',
logger
);
expect(processed).toEqual({
myplugin: {
deprecated: 'deprecated',
renamed: 'renamed',
},
someOtherPlugin: {
property: 'value',
},
});
});
});
describe('unused', () => {
it('removes the unused property from the config and logs a warning is present', () => {
const rawConfig = {

View file

@ -56,6 +56,26 @@ const _rename = (
return config;
};
const _copy = (
config: Record<string, any>,
rootPath: string,
originKey: string,
destinationKey: string
) => {
const originPath = getPath(rootPath, originKey);
const originValue = get(config, originPath);
if (originValue === undefined) {
return config;
}
const destinationPath = getPath(rootPath, destinationKey);
const destinationValue = get(config, destinationPath);
if (destinationValue === undefined) {
set(config, destinationPath, originValue);
}
return config;
};
const _unused = (
config: Record<string, any>,
rootPath: string,
@ -80,6 +100,12 @@ const renameFromRoot = (oldKey: string, newKey: string, silent?: boolean): Confi
log
) => _rename(config, '', log, oldKey, newKey, silent);
export const copyFromRoot = (originKey: string, destinationKey: string): ConfigDeprecation => (
config,
rootPath,
log
) => _copy(config, '', originKey, destinationKey);
const unused = (unusedKey: string): ConfigDeprecation => (config, rootPath, log) =>
_unused(config, rootPath, log, unusedKey);

View file

@ -24,5 +24,5 @@ export {
ConfigDeprecationFactory,
ConfigDeprecationProvider,
} from './types';
export { configDeprecationFactory } from './deprecation_factory';
export { configDeprecationFactory, copyFromRoot } from './deprecation_factory';
export { applyDeprecations } from './apply_deprecations';

View file

@ -25,6 +25,7 @@ export {
ConfigDeprecationLogger,
ConfigDeprecationProvider,
ConfigDeprecationWithContext,
copyFromRoot,
} from './deprecation';
export { RawConfigurationProvider, RawConfigService, getConfigFromFiles } from './raw';

View file

@ -46,6 +46,7 @@ pageLoadAssetSize:
kibanaUtils: 198829
lens: 96624
licenseManagement: 41961
lensOss: 19341
licensing: 39008
lists: 183665
logstash: 53548
@ -53,6 +54,7 @@ pageLoadAssetSize:
maps: 183754
mapsLegacy: 116961
mapsLegacyLicensing: 20214
mapsOss: 19284
ml: 82187
monitoring: 50000
navigation: 37413

View file

@ -37,6 +37,7 @@ export class DocLinksService {
ELASTIC_WEBSITE_URL,
links: {
dashboard: {
guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/dashboard.html`,
drilldowns: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/drilldowns.html`,
drilldownsTriggerPicker: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/url-drilldown.html#trigger-picker`,
urlDrilldownTemplateSyntax: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/url-drilldown.html#templating`,
@ -135,6 +136,8 @@ export class DocLinksService {
visualize: {
guide: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/visualize.html`,
timelionDeprecation: `${ELASTIC_WEBSITE_URL}guide/en/kibana/${DOC_LINK_VERSION}/dashboard.html#timelion-deprecation`,
lens: `${ELASTIC_WEBSITE_URL}what-is/kibana-lens`,
maps: `${ELASTIC_WEBSITE_URL}maps`,
},
},
});
@ -147,6 +150,7 @@ export interface DocLinksStart {
readonly ELASTIC_WEBSITE_URL: string;
readonly links: {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;

View file

@ -460,6 +460,7 @@ export interface DocLinksStart {
// (undocumented)
readonly links: {
readonly dashboard: {
readonly guide: string;
readonly drilldowns: string;
readonly drilldownsTriggerPicker: string;
readonly urlDrilldownTemplateSyntax: string;

View file

@ -18,8 +18,7 @@
*/
import { i18n } from '@kbn/i18n';
import { BaseVisTypeOptions } from 'src/plugins/visualizations/public';
import { VisGroups, BaseVisTypeOptions } from '../../visualizations/public';
import { createInputControlVisController } from './vis_controller';
import { getControlsTab } from './components/editor/controls_tab';
import { OptionsTab } from './components/editor/options_tab';
@ -37,8 +36,9 @@ export function createInputControlVisTypeDefinition(
defaultMessage: 'Controls',
}),
icon: 'controlsHorizontal',
group: VisGroups.TOOLS,
description: i18n.translate('inputControl.register.controlsDescription', {
defaultMessage: 'Create interactive controls for easy dashboard manipulation.',
defaultMessage: 'Add dropdown menus and range sliders to your dashboard.',
}),
stage: 'experimental',
visualization: InputControlVisController,

View file

@ -0,0 +1,6 @@
# lens_oss
The lens_oss plugin registers the lens visualization on OSS.
It is registered as disabled. The x-pack plugin should unregister this.
`visualizations.unregisterAlias('lensOss')`

View file

@ -0,0 +1,22 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export const APP_NAME = 'lens';
export const PLUGIN_ID_OSS = 'lensOss';
export const APP_PATH = '#/';
export const APP_ICON = 'lensApp';

View file

@ -17,4 +17,4 @@
* under the License.
*/
export { TypeSelection } from './type_selection';
export * from './constants';

View file

@ -0,0 +1,26 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { schema, TypeOf } from '@kbn/config-schema';
export const configSchema = schema.object({
enabled: schema.boolean({ defaultValue: true }),
});
export type ConfigSchema = TypeOf<typeof configSchema>;

View file

@ -0,0 +1,10 @@
{
"id": "lensOss",
"version": "kibana",
"ui": true,
"server": true,
"requiredPlugins": [
"visualizations"
],
"extraPublicDirs": ["common/constants"]
}

View file

@ -0,0 +1,21 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { LensOSSPlugin } from './plugin';
export const plugin = () => new LensOSSPlugin();

View file

@ -16,35 +16,27 @@
* specific language governing permissions and limitations
* under the License.
*/
import { DocLinksStart, CoreSetup } from 'src/core/public';
import { VisualizationsSetup } from '../../visualizations/public';
import { getLensAliasConfig } from './vis_type_alias';
import React from 'react';
import { EuiSpacer, EuiText, EuiTitle } from '@elastic/eui';
interface VisHelpTextProps {
name: string;
title: string;
description?: string;
highlightMsg?: string;
export interface LensPluginSetupDependencies {
visualizations: VisualizationsSetup;
}
export const VisHelpText = ({ name, title, description, highlightMsg }: VisHelpTextProps) => {
return (
<React.Fragment>
<EuiTitle size="s">
<h2>{title}</h2>
</EuiTitle>
<EuiSpacer size="s" />
<div id={`visTypeDescription-${name}`}>
<EuiText>
{highlightMsg && (
<p>
<em>{highlightMsg}</em>
</p>
)}
<p>{description}</p>
</EuiText>
</div>
</React.Fragment>
);
};
export interface LensPluginStartDependencies {
docLinks: DocLinksStart;
}
export class LensOSSPlugin {
setup(
core: CoreSetup<LensPluginStartDependencies>,
{ visualizations }: LensPluginSetupDependencies
) {
core.getStartServices().then(([coreStart]) => {
visualizations.registerAlias(getLensAliasConfig(coreStart.docLinks));
});
}
start() {}
}

View file

@ -0,0 +1,48 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { i18n } from '@kbn/i18n';
import { VisTypeAlias } from 'src/plugins/visualizations/public';
import { DocLinksStart } from 'src/core/public';
import { APP_NAME, PLUGIN_ID_OSS, APP_PATH, APP_ICON } from '../common';
export const getLensAliasConfig = ({ links }: DocLinksStart): VisTypeAlias => ({
aliasPath: APP_PATH,
aliasApp: APP_NAME,
name: PLUGIN_ID_OSS,
title: i18n.translate('lensOss.visTypeAlias.title', {
defaultMessage: 'Lens',
}),
description: i18n.translate('lensOss.visTypeAlias.description', {
defaultMessage:
'Create visualizations with our drag-and-drop editor. Switch between visualization types at any time. Best for most visualizations.',
}),
icon: APP_ICON,
stage: 'production',
disabled: true,
note: i18n.translate('lensOss.visTypeAlias.note', {
defaultMessage: 'Recommended for most users.',
}),
promoTooltip: {
description: i18n.translate('lensOss.visTypeAlias.promoTooltip.description', {
defaultMessage: 'Try Lens for free with Elastic. Learn more.',
}),
link: `${links.visualize.lens}?blade=kibanaossvizwizard`,
},
});

View file

@ -17,25 +17,16 @@
* under the License.
*/
import { EuiIcon, IconType } from '@elastic/eui';
import React from 'react';
import { PluginConfigDescriptor } from 'kibana/server';
import { copyFromRoot } from '@kbn/config';
import { configSchema, ConfigSchema } from '../config';
interface VisTypeIconProps {
icon?: IconType;
image?: string;
}
/**
* This renders the icon for a specific visualization type.
* This currently checks the following:
* - If image is set, use that as the `src` of an image
* - Otherwise use the icon as an EuiIcon or the 'empty' icon if that's not set
*/
export const VisTypeIcon = ({ icon, image }: VisTypeIconProps) => {
return (
<React.Fragment>
{image && <img src={image} alt="" className="visNewVisDialog__typeImage" />}
{!image && <EuiIcon type={icon || 'empty'} size="l" color="secondary" />}
</React.Fragment>
);
export const config: PluginConfigDescriptor<ConfigSchema> = {
schema: configSchema,
deprecations: () => [copyFromRoot('xpack.lens.enabled', 'lens_oss.enabled')],
};
export const plugin = () => ({
setup() {},
start() {},
});

View file

@ -0,0 +1,6 @@
# maps_oss
The maps_oss plugin registers the maps visualization on OSS.
It is registered as disabled. The x-pack plugin should unregister this.
`visualizations.unregisterAlias('mapsOss')`

View file

@ -0,0 +1,22 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export const APP_NAME = 'maps';
export const PLUGIN_ID_OSS = 'mapsOss';
export const APP_PATH = '/map';
export const APP_ICON = 'gisApp';

View file

@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export * from './constants';

View file

@ -0,0 +1,26 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { schema, TypeOf } from '@kbn/config-schema';
export const configSchema = schema.object({
enabled: schema.boolean({ defaultValue: true }),
});
export type ConfigSchema = TypeOf<typeof configSchema>;

View file

@ -0,0 +1,10 @@
{
"id": "mapsOss",
"version": "kibana",
"ui": true,
"server": true,
"requiredPlugins": [
"visualizations"
],
"extraPublicDirs": ["common/constants"]
}

View file

@ -0,0 +1,21 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { MapsOSSPlugin } from './plugin';
export const plugin = () => new MapsOSSPlugin();

View file

@ -0,0 +1,43 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { DocLinksStart, CoreSetup } from 'src/core/public';
import { VisualizationsSetup } from '../../visualizations/public';
import { getMapsAliasConfig } from './vis_type_alias';
export interface MapsPluginSetupDependencies {
visualizations: VisualizationsSetup;
}
export interface MapsPluginStartDependencies {
docLinks: DocLinksStart;
}
export class MapsOSSPlugin {
setup(
core: CoreSetup<MapsPluginStartDependencies>,
{ visualizations }: MapsPluginSetupDependencies
) {
core.getStartServices().then(([coreStart]) => {
visualizations.registerAlias(getMapsAliasConfig(coreStart.docLinks));
});
}
start() {}
}

View file

@ -0,0 +1,44 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { i18n } from '@kbn/i18n';
import { VisTypeAlias } from 'src/plugins/visualizations/public';
import { DocLinksStart } from 'src/core/public';
import { APP_NAME, PLUGIN_ID_OSS, APP_PATH, APP_ICON } from '../common';
export const getMapsAliasConfig = ({ links }: DocLinksStart): VisTypeAlias => ({
aliasPath: APP_PATH,
aliasApp: APP_NAME,
name: PLUGIN_ID_OSS,
title: i18n.translate('mapsOss.visTypeAlias.title', {
defaultMessage: 'Maps',
}),
description: i18n.translate('mapsOss.visTypeAlias.description', {
defaultMessage: 'Plot and style your geo data in a multi layer map.',
}),
icon: APP_ICON,
stage: 'production',
disabled: true,
promoTooltip: {
description: i18n.translate('mapsOss.visTypeAlias.promoTooltip.description', {
defaultMessage: 'Try maps for free with Elastic. Learn more.',
}),
link: `${links.visualize.maps}?blade=kibanaossvizwizard`,
},
});

View file

@ -0,0 +1,32 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { PluginConfigDescriptor } from 'kibana/server';
import { copyFromRoot } from '@kbn/config';
import { configSchema, ConfigSchema } from '../config';
export const config: PluginConfigDescriptor<ConfigSchema> = {
schema: configSchema,
deprecations: () => [copyFromRoot('xpack.maps.enabled', 'maps_oss.enabled')],
};
export const plugin = () => ({
setup() {},
start() {},
});

View file

@ -22,6 +22,7 @@ import { i18n } from '@kbn/i18n';
import { MarkdownOptions } from './markdown_options';
import { SettingsOptions } from './settings_options_lazy';
import { DefaultEditorSize } from '../../vis_default_editor/public';
import { VisGroups } from '../../visualizations/public';
import { toExpressionAst } from './to_ast';
export const markdownVisDefinition = {
@ -29,8 +30,12 @@ export const markdownVisDefinition = {
title: 'Markdown',
isAccessible: true,
icon: 'visText',
group: VisGroups.TOOLS,
titleInWizard: i18n.translate('visTypeMarkdown.markdownTitleInWizard', {
defaultMessage: 'Text',
}),
description: i18n.translate('visTypeMarkdown.markdownDescription', {
defaultMessage: 'Create a document using markdown syntax',
defaultMessage: 'Add text and images to your dashboard.',
}),
toExpressionAst,
visConfig: {

View file

@ -25,15 +25,16 @@ import { EditorController } from './application';
// @ts-ignore
import { PANEL_TYPES } from '../common/panel_types';
import { VisEditor } from './application/components/vis_editor_lazy';
import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
import { VIS_EVENT_TO_TRIGGER, VisGroups } from '../../visualizations/public';
export const metricsVisDefinition = {
name: 'metrics',
title: i18n.translate('visTypeTimeseries.kbnVisTypes.metricsTitle', { defaultMessage: 'TSVB' }),
description: i18n.translate('visTypeTimeseries.kbnVisTypes.metricsDescription', {
defaultMessage: 'Build time-series using a visual pipeline interface',
defaultMessage: 'Perform advanced analysis of your time series data.',
}),
icon: 'visVisualBuilder',
group: VisGroups.PROMOTED,
visConfig: {
defaults: {
id: '61ca57f0-469d-11e7-af02-69e470af7417',

View file

@ -25,7 +25,7 @@ import { VegaVisualizationDependencies } from './plugin';
import { createVegaRequestHandler } from './vega_request_handler';
import { getDefaultSpec } from './default_spec';
import { createInspectorAdapters } from './vega_inspector';
import { VIS_EVENT_TO_TRIGGER } from '../../visualizations/public';
import { VIS_EVENT_TO_TRIGGER, VisGroups } from '../../visualizations/public';
import { toExpressionAst } from './to_ast';
import { VisParams } from './vega_fn';
import { getInfoMessage } from './components/experimental_map_vis_info';
@ -41,10 +41,17 @@ export const createVegaTypeDefinition = (
title: 'Vega',
getInfoMessage,
description: i18n.translate('visTypeVega.type.vegaDescription', {
defaultMessage: 'Create custom visualizations using Vega and Vega-Lite',
defaultMessage: 'Use Vega to create new types of visualizations.',
description: 'Vega and Vega-Lite are product names and should not be translated',
}),
note: i18n.translate('visTypeVega.type.vegaNote', {
defaultMessage: 'Requires knowledge of Vega syntax.',
}),
icon: 'visVega',
group: VisGroups.PROMOTED,
titleInWizard: i18n.translate('visTypeVega.type.vegaTitleInWizard', {
defaultMessage: 'Custom visualization',
}),
visConfig: { defaults: { spec: getDefaultSpec() } },
editorConfig: {
optionsTemplate: VegaVisEditorComponent,

View file

@ -61,8 +61,7 @@ export const gaugeVisTypeDefinition: BaseVisTypeOptions<BasicVislibParams> = {
title: i18n.translate('visTypeVislib.gauge.gaugeTitle', { defaultMessage: 'Gauge' }),
icon: 'visGauge',
description: i18n.translate('visTypeVislib.gauge.gaugeDescription', {
defaultMessage:
"Gauges indicate the status of a metric. Use it to show how a metric's value relates to reference threshold values.",
defaultMessage: 'Gauges indicate the status of a metric.',
}),
toExpressionAst,
visConfig: {

View file

@ -1,3 +1,2 @@
@import 'wizard/index';
@import 'embeddable/index';
@import 'components/index';

View file

@ -36,6 +36,7 @@ export { getSchemas as getVisSchemas } from './legacy/build_pipeline';
/** @public types */
export { VisualizationsSetup, VisualizationsStart };
export { VisGroups } from './vis_types';
export type { VisTypeAlias, VisType, BaseVisTypeOptions, ReactVisTypeOptions } from './vis_types';
export { VisParams, SerializedVis, SerializedVisData, VisData } from './vis';
export type VisualizeEmbeddableFactoryContract = PublicContract<VisualizeEmbeddableFactory>;

View file

@ -41,6 +41,8 @@ const createStartContract = (): VisualizationsStart => ({
get: jest.fn(),
all: jest.fn(),
getAliases: jest.fn(),
getByGroup: jest.fn(),
unRegisterAlias: jest.fn(),
savedVisualizationsLoader: {
get: jest.fn(),
} as any,

View file

@ -49,6 +49,7 @@ import {
setOverlays,
setSavedSearchLoader,
setEmbeddable,
setDocLinks,
} from './services';
import {
VISUALIZE_EMBEDDABLE_TYPE,
@ -172,6 +173,7 @@ export class VisualizationsPlugin
setCapabilities(core.application.capabilities);
setHttp(core.http);
setSavedObjects(core.savedObjects);
setDocLinks(core.docLinks);
setIndexPatterns(data.indexPatterns);
setSearch(data.search);
setFilterManager(data.query.filterManager);

View file

@ -26,6 +26,7 @@ import {
IUiSettingsClient,
OverlayStart,
SavedObjectsStart,
DocLinksStart,
} from '../../../core/public';
import { TypesStart } from './vis_types';
import { createGetterSetter } from '../../../plugins/kibana_utils/public';
@ -60,6 +61,8 @@ export const [getTypes, setTypes] = createGetterSetter<TypesStart>('Types');
export const [getI18n, setI18n] = createGetterSetter<I18nStart>('I18n');
export const [getDocLinks, setDocLinks] = createGetterSetter<DocLinksStart>('DocLinks');
export const [getFilterManager, setFilterManager] = createGetterSetter<FilterManager>(
'FilterManager'
);

View file

@ -20,7 +20,7 @@
import { defaultsDeep } from 'lodash';
import { ISchemas } from 'src/plugins/vis_default_editor/public';
import { VisParams } from '../types';
import { VisType, VisTypeOptions } from './types';
import { VisType, VisTypeOptions, VisGroups } from './types';
interface CommonBaseVisTypeOptions<TVisParams>
extends Pick<
@ -41,7 +41,14 @@ interface CommonBaseVisTypeOptions<TVisParams>
>,
Pick<
Partial<VisType<TVisParams>>,
'editorConfig' | 'hidden' | 'stage' | 'useCustomNoDataScreen' | 'visConfig'
| 'editorConfig'
| 'hidden'
| 'stage'
| 'useCustomNoDataScreen'
| 'visConfig'
| 'group'
| 'titleInWizard'
| 'note'
> {
options?: Partial<VisType<TVisParams>['options']>;
}
@ -72,10 +79,13 @@ export class BaseVisType<TVisParams = VisParams> implements VisType<TVisParams>
public readonly name;
public readonly title;
public readonly description;
public readonly note;
public readonly getSupportedTriggers;
public readonly icon;
public readonly image;
public readonly stage;
public readonly group;
public readonly titleInWizard;
public readonly options;
public readonly visualization;
public readonly visConfig;
@ -98,6 +108,7 @@ export class BaseVisType<TVisParams = VisParams> implements VisType<TVisParams>
this.name = opts.name;
this.description = opts.description ?? '';
this.note = opts.note ?? '';
this.getSupportedTriggers = opts.getSupportedTriggers;
this.title = opts.title;
this.icon = opts.icon;
@ -108,6 +119,8 @@ export class BaseVisType<TVisParams = VisParams> implements VisType<TVisParams>
this.editorConfig = defaultsDeep({}, opts.editorConfig, { collections: {} });
this.options = defaultsDeep({}, opts.options, defaultOptions);
this.stage = opts.stage ?? 'production';
this.group = opts.group ?? VisGroups.AGGBASED;
this.titleInWizard = opts.titleInWizard ?? '';
this.hidden = opts.hidden ?? false;
this.requestHandler = opts.requestHandler ?? 'courier';
this.responseHandler = opts.responseHandler ?? 'none';

View file

@ -18,6 +18,7 @@
*/
export * from './types_service';
export { VisGroups } from './types';
export type { VisType } from './types';
export type { BaseVisTypeOptions } from './base_vis_type';
export type { ReactVisTypeOptions } from './react_vis_type';

View file

@ -33,21 +33,63 @@ export interface VisTypeOptions {
hierarchicalData: boolean;
}
export enum VisGroups {
PROMOTED = 'promoted',
TOOLS = 'tools',
AGGBASED = 'aggbased',
}
/**
* A visualization type representing one specific type of "classical"
* visualizations (i.e. not Lens visualizations).
*/
export interface VisType<TVisParams = unknown> {
/**
* Visualization unique name
*/
readonly name: string;
/**
* It is the displayed text on the wizard and the vis listing
*/
readonly title: string;
/**
* If given, it will be diplayed on the wizard vis card as the main description.
*/
readonly description?: string;
/**
* If given, it will be diplayed on the wizard vis card as a note in italic.
*/
readonly note: string;
/**
* If given, it will return the supported triggers for this vis.
*/
readonly getSupportedTriggers?: () => Array<keyof TriggerContextMapping>;
readonly isAccessible?: boolean;
readonly requestHandler?: string | unknown;
readonly responseHandler?: string | unknown;
/**
* It is the visualization icon, displayed on the wizard.
*/
readonly icon?: IconType;
/**
* Except from an icon, an image can be passed
*/
readonly image?: string;
/**
* Describes the visualization stage
*/
readonly stage: 'experimental' | 'beta' | 'production';
/**
* Describes the experience group that the visualization belongs.
* It can be on tools, aggregation based or promoted group.
*/
readonly group: VisGroups;
/**
* If given, it will be displayed on the wizard instead of the title.
* We use it because we want to differentiate the vis title from the
* way it is presented on the wizard
*/
readonly titleInWizard: string;
readonly requiresSearch: boolean;
readonly useCustomNoDataScreen: boolean;
readonly hierarchicalData?: boolean | ((vis: { params: TVisParams }) => boolean);

View file

@ -20,7 +20,7 @@
import { visTypeAliasRegistry, VisTypeAlias } from './vis_type_alias_registry';
import { BaseVisType, BaseVisTypeOptions } from './base_vis_type';
import { ReactVisType, ReactVisTypeOptions } from './react_vis_type';
import { VisType } from './types';
import { VisType, VisGroups } from './types';
/**
* Vis Types Service
@ -101,6 +101,21 @@ export class TypesService {
* returns all registered aliases
*/
getAliases: visTypeAliasRegistry.get,
/**
* unregisters a visualization alias by its name
* alias is a visualization type without implementation, it just redirects somewhere in kibana
* @param {string} visTypeAliasName - visualization alias name
*/
unRegisterAlias: visTypeAliasRegistry.remove,
/**
* returns all visualizations of specific group
* @param {VisGroups} group - group type (aggbased | other | tools)
*/
getByGroup: (group: VisGroups) => {
return Object.values(this.types).filter((type) => {
return type.group === group;
});
},
};
}

View file

@ -43,9 +43,9 @@ export interface VisualizationsAppExtension {
}) => VisualizationListItem;
}
export interface VisTypeAliasPromotion {
export interface VisTypeAliasPromoTooltip {
description: string;
buttonText: string;
link: string;
}
export interface VisTypeAlias {
@ -54,8 +54,11 @@ export interface VisTypeAlias {
name: string;
title: string;
icon: string;
promotion?: VisTypeAliasPromotion;
promotion?: boolean;
promoTooltip?: VisTypeAliasPromoTooltip;
description: string;
note?: string;
disabled?: boolean;
getSupportedTriggers?: () => Array<keyof TriggerContextMapping>;
stage: 'experimental' | 'beta' | 'production';
@ -65,11 +68,13 @@ export interface VisTypeAlias {
};
}
const registry: VisTypeAlias[] = [];
let registry: VisTypeAlias[] = [];
const discardOnRegister: string[] = [];
interface VisTypeAliasRegistry {
get: () => VisTypeAlias[];
add: (newVisTypeAlias: VisTypeAlias) => void;
remove: (visTypeAliasName: string) => void;
}
export const visTypeAliasRegistry: VisTypeAliasRegistry = {
@ -78,6 +83,22 @@ export const visTypeAliasRegistry: VisTypeAliasRegistry = {
if (registry.find((visTypeAlias) => visTypeAlias.name === newVisTypeAlias.name)) {
throw new Error(`${newVisTypeAlias.name} already registered`);
}
registry.push(newVisTypeAlias);
// if it exists on discardOnRegister array then we don't allow it to be registered
const isToBeDiscarded = discardOnRegister.some(
(aliasName) => aliasName === newVisTypeAlias.name
);
if (!isToBeDiscarded) {
registry.push(newVisTypeAlias);
}
},
remove: (visTypeAliasName) => {
const isAliasPresent = registry.find((visTypeAlias) => visTypeAlias.name === visTypeAliasName);
// in case the alias has not registered yet we store it on an array, in order to not allow it to
// be registered in case of a race condition
if (!isAliasPresent) {
discardOnRegister.push(visTypeAliasName);
} else {
registry = registry.filter((visTypeAlias) => visTypeAlias.name !== visTypeAliasName);
}
},
};

View file

@ -1 +0,0 @@
@import 'dialog';

View file

@ -0,0 +1,122 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { TypesStart, VisType, VisGroups } from '../../vis_types';
import { AggBasedSelection } from './agg_based_selection';
describe('AggBasedSelection', () => {
const defaultVisTypeParams = {
hidden: false,
visualization: class Controller {
public render = jest.fn();
public destroy = jest.fn();
},
requiresSearch: false,
requestHandler: 'none',
responseHandler: 'none',
};
const _visTypes = [
{
name: 'vis1',
title: 'Vis Type 1',
stage: 'production',
group: VisGroups.PROMOTED,
...defaultVisTypeParams,
},
{
name: 'vis2',
title: 'Vis Type 2',
group: VisGroups.AGGBASED,
stage: 'production',
...defaultVisTypeParams,
},
{
name: 'vis3',
title: 'Vis Type 3',
stage: 'production',
group: VisGroups.AGGBASED,
},
{
name: 'visWithSearch',
title: 'Vis with search',
group: VisGroups.AGGBASED,
stage: 'production',
...defaultVisTypeParams,
},
] as VisType[];
const visTypes: TypesStart = {
get<T>(id: string): VisType<T> {
return _visTypes.find((vis) => vis.name === id) as VisType<T>;
},
all: () => {
return (_visTypes as unknown) as VisType[];
},
getAliases: () => [],
unRegisterAlias: () => [],
getByGroup: (group: VisGroups) => {
return _visTypes.filter((type) => {
return type.group === group;
}) as VisType[];
},
};
beforeAll(() => {
Object.defineProperty(window, 'location', {
value: {
assign: jest.fn(),
},
});
});
beforeEach(() => {
jest.clearAllMocks();
});
it('should call the toggleGroups if the user clicks the goBack link', () => {
const toggleGroups = jest.fn();
const wrapper = mountWithIntl(
<AggBasedSelection
visTypesRegistry={visTypes}
toggleGroups={toggleGroups}
onVisTypeSelected={jest.fn()}
/>
);
const aggBasedGroupCard = wrapper.find('[data-test-subj="goBackLink"]').at(0);
aggBasedGroupCard.simulate('click');
expect(toggleGroups).toHaveBeenCalled();
});
describe('filter for visualization types', () => {
it('should render as expected', () => {
const wrapper = mountWithIntl(
<AggBasedSelection
visTypesRegistry={visTypes}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
/>
);
const searchBox = wrapper.find('input[data-test-subj="filterVisType"]');
searchBox.simulate('change', { target: { value: 'with' } });
expect(wrapper.find('[data-test-subj="visType-visWithSearch"]').exists()).toBe(true);
});
});
});

View file

@ -0,0 +1,163 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { orderBy } from 'lodash';
import React, { ChangeEvent } from 'react';
import {
EuiFieldSearch,
EuiFlexGrid,
EuiFlexItem,
EuiScreenReaderOnly,
EuiSpacer,
EuiIcon,
EuiCard,
EuiModalBody,
EuiModalHeader,
EuiModalHeaderTitle,
} from '@elastic/eui';
import { memoizeLast } from '../../legacy/memoize';
import type { VisType, TypesStart } from '../../vis_types';
import { VisGroups } from '../../vis_types';
import { DialogNavigation } from '../dialog_navigation';
interface VisTypeListEntry {
type: VisType;
highlighted: boolean;
}
interface AggBasedSelectionProps {
onVisTypeSelected: (visType: VisType) => void;
visTypesRegistry: TypesStart;
toggleGroups: (flag: boolean) => void;
}
interface AggBasedSelectionState {
query: string;
}
class AggBasedSelection extends React.Component<AggBasedSelectionProps, AggBasedSelectionState> {
public state: AggBasedSelectionState = {
query: '',
};
private readonly getFilteredVisTypes = memoizeLast(this.filteredVisTypes);
public render() {
const { query } = this.state;
const visTypes = this.getFilteredVisTypes(this.props.visTypesRegistry, query);
return (
<>
<EuiModalHeader>
<EuiModalHeaderTitle>
<FormattedMessage
id="visualizations.newVisWizard.title"
defaultMessage="New visualization"
/>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<DialogNavigation goBack={() => this.props.toggleGroups(true)} />
<EuiFieldSearch
placeholder="Filter"
value={query}
onChange={this.onQueryChange}
fullWidth
data-test-subj="filterVisType"
aria-label={i18n.translate('visualizations.newVisWizard.filterVisTypeAriaLabel', {
defaultMessage: 'Filter for a visualization type',
})}
/>
<EuiSpacer />
<EuiScreenReaderOnly>
<span aria-live="polite">
{query && (
<FormattedMessage
id="visualizations.newVisWizard.resultsFound"
defaultMessage="{resultCount, plural, one {type} other {types}} found"
values={{
resultCount: visTypes.filter((type) => type.highlighted).length,
}}
/>
)}
</span>
</EuiScreenReaderOnly>
<EuiFlexGrid columns={3} data-test-subj="visNewDialogTypes">
{visTypes.map(this.renderVisType)}
</EuiFlexGrid>
</EuiModalBody>
</>
);
}
private filteredVisTypes(visTypes: TypesStart, query: string): VisTypeListEntry[] {
const types = visTypes.getByGroup(VisGroups.AGGBASED).filter((type) => {
// Filter out hidden visualizations and visualizations that are only aggregations based
return !type.hidden;
});
let entries: VisTypeListEntry[];
if (!query) {
entries = types.map((type) => ({ type, highlighted: false }));
} else {
const q = query.toLowerCase();
entries = types.map((type) => {
const matchesQuery =
type.name.toLowerCase().includes(q) ||
type.title.toLowerCase().includes(q) ||
(typeof type.description === 'string' && type.description.toLowerCase().includes(q));
return { type, highlighted: matchesQuery };
});
}
return orderBy(entries, ['highlighted', 'type.title'], ['desc', 'asc']);
}
private renderVisType = (visType: VisTypeListEntry) => {
const isDisabled = this.state.query !== '' && !visType.highlighted;
const onClick = () => this.props.onVisTypeSelected(visType.type);
return (
<EuiFlexItem key={visType.type.name}>
<EuiCard
titleSize="xs"
title={<span data-test-subj="visTypeTitle">{visType.type.title}</span>}
onClick={onClick}
data-test-subj={`visType-${visType.type.name}`}
data-vis-stage={visType.type.stage}
aria-label={`visType-${visType.type.name}`}
description={visType.type.description || ''}
layout="horizontal"
isDisabled={isDisabled}
icon={<EuiIcon type={visType.type.icon || 'empty'} size="l" color="secondary" />}
/>
</EuiFlexItem>
);
};
private onQueryChange = (ev: ChangeEvent<HTMLInputElement>) => {
this.setState({
query: ev.target.value,
});
};
}
export { AggBasedSelection };

View file

@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export { AggBasedSelection } from './agg_based_selection';

View file

@ -0,0 +1,48 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { EuiLink, EuiIcon, EuiSpacer, EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
interface DialogNavigationProps {
goBack: () => void;
}
function DialogNavigation(props: DialogNavigationProps) {
return (
<>
<EuiLink data-test-subj="goBackLink" onClick={props.goBack}>
<EuiFlexGroup alignItems="center" gutterSize="s" responsive={false}>
<EuiFlexItem grow={false}>
<EuiIcon type="arrowLeft" />
</EuiFlexItem>
<EuiFlexItem>
{i18n.translate('visualizations.newVisWizard.goBackLink', {
defaultMessage: 'Go back',
})}
</EuiFlexItem>
</EuiFlexGroup>
</EuiLink>
<EuiSpacer />
</>
);
}
export { DialogNavigation };

View file

@ -0,0 +1,30 @@
.visNewVisDialogGroupSelection__body {
// override EUI specificity
.euiModalBody__overflow {
padding: 0 !important; // sass-lint:disable-line no-important
}
}
.visNewVisDialogGroupSelection__visGroups {
padding: $euiSizeS $euiSizeXL 0;
}
.visNewVisDialogGroupSelection__footer {
@include euiBreakpoint('xs', 's') {
background: $euiColorEmptyShade;
}
padding: 0 $euiSizeXL $euiSizeL;
background: $euiColorLightestShade;
}
.visNewVisDialogGroupSelection__footerDescriptionList {
@include euiBreakpoint('xs', 's') {
padding-top: $euiSizeL;
}
}
.visNewVisDialogGroupSelection__footerDescriptionListTitle {
// override EUI specificity
width: auto !important; // sass-lint:disable-line no-important
}

View file

@ -0,0 +1,321 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { TypesStart, VisType, VisGroups } from '../../vis_types';
import { GroupSelection } from './group_selection';
import { DocLinksStart } from '../../../../../core/public';
describe('GroupSelection', () => {
const defaultVisTypeParams = {
hidden: false,
visualization: class Controller {
public render = jest.fn();
public destroy = jest.fn();
},
requiresSearch: false,
requestHandler: 'none',
responseHandler: 'none',
};
const _visTypes = [
{
name: 'vis1',
title: 'Vis Type 1',
description: 'Vis Type 1',
stage: 'production',
group: VisGroups.PROMOTED,
...defaultVisTypeParams,
},
{
name: 'vis2',
title: 'Vis Type 2',
description: 'Vis Type 2',
group: VisGroups.PROMOTED,
stage: 'production',
...defaultVisTypeParams,
},
{
name: 'visWithAliasUrl',
title: 'Vis with alias Url',
aliasApp: 'aliasApp',
aliasPath: '#/aliasApp',
disabled: true,
promoTooltip: {
description: 'Learn More',
link: '#/anotherUrl',
},
description: 'Vis with alias Url',
stage: 'production',
group: VisGroups.PROMOTED,
},
{
name: 'visAliasWithPromotion',
title: 'Vis alias with promotion',
description: 'Vis alias with promotion',
stage: 'production',
group: VisGroups.PROMOTED,
aliasApp: 'anotherApp',
aliasPath: '#/anotherUrl',
promotion: true,
} as unknown,
] as VisType[];
const visTypesRegistry = (visTypes: VisType[]): TypesStart => {
return {
get<T>(id: string): VisType<T> {
return (visTypes.find((vis) => vis.name === id) as unknown) as VisType<T>;
},
all: () => {
return (visTypes as unknown) as VisType[];
},
getAliases: () => [],
unRegisterAlias: () => [],
getByGroup: (group: VisGroups) => {
return (visTypes.filter((type) => {
return type.group === group;
}) as unknown) as VisType[];
},
};
};
const docLinks = {
links: {
dashboard: {
guide: 'test',
},
},
};
beforeAll(() => {
Object.defineProperty(window, 'location', {
value: {
assign: jest.fn(),
},
});
});
beforeEach(() => {
jest.clearAllMocks();
});
it('should render the header title', () => {
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry(_visTypes)}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="groupModalHeader"]').at(0).text()).toBe(
'New visualization'
);
});
it('should not render the aggBased group card if no aggBased visualization is registered', () => {
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry(_visTypes)}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visGroup-aggbased"]').exists()).toBe(false);
});
it('should render the aggBased group card if an aggBased group vis is registered', () => {
const aggBasedVisType = {
name: 'visWithSearch',
title: 'Vis with search',
group: VisGroups.AGGBASED,
stage: 'production',
...defaultVisTypeParams,
};
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry([..._visTypes, aggBasedVisType] as VisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visGroup-aggbased"]').exists()).toBe(true);
});
it('should not render the tools group card if no tools visualization is registered', () => {
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry(_visTypes)}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visGroup-tools"]').exists()).toBe(false);
});
it('should render the tools group card if a tools group vis is registered', () => {
const toolsVisType = {
name: 'vis3',
title: 'Vis3',
stage: 'production',
group: VisGroups.TOOLS,
...defaultVisTypeParams,
};
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry([..._visTypes, toolsVisType] as VisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visGroup-tools"]').exists()).toBe(true);
});
it('should call the toggleGroups if the aggBased group card is clicked', () => {
const toggleGroups = jest.fn();
const aggBasedVisType = {
name: 'visWithSearch',
title: 'Vis with search',
group: VisGroups.AGGBASED,
stage: 'production',
...defaultVisTypeParams,
};
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry([..._visTypes, aggBasedVisType] as VisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={toggleGroups}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
const aggBasedGroupCard = wrapper.find('[data-test-subj="visGroupAggBasedExploreLink"]').at(0);
aggBasedGroupCard.simulate('click');
expect(toggleGroups).toHaveBeenCalled();
});
it('should sort promoted visualizations first', () => {
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry(_visTypes as VisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
const cards = [
...new Set(
wrapper.find('[data-test-subj^="visType-"]').map((card) => card.prop('data-test-subj'))
),
];
expect(cards).toEqual([
'visType-visAliasWithPromotion',
'visType-vis1',
'visType-vis2',
'visType-visWithAliasUrl',
]);
});
it('should render disabled aliases with a disabled class', () => {
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry(_visTypes as VisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visType-visWithAliasUrl"]').exists()).toBe(true);
expect(
wrapper
.find('[data-test-subj="visType-visWithAliasUrl"]')
.at(1)
.hasClass('euiCard-isDisabled')
).toBe(true);
});
it('should render a basic badge with link for disabled aliases with promoTooltip', () => {
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry(_visTypes as VisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visTypeBadge"]').exists()).toBe(true);
expect(wrapper.find('[data-test-subj="visTypeBadge"]').at(0).prop('tooltipContent')).toBe(
'Learn More'
);
});
it('should not show tools experimental visualizations if showExperimentalis false', () => {
const expVis = {
name: 'visExp',
title: 'Experimental Vis',
group: VisGroups.TOOLS,
stage: 'experimental',
...defaultVisTypeParams,
};
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry([..._visTypes, expVis] as VisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={false}
/>
);
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(false);
});
it('should show tools experimental visualizations if showExperimental is true', () => {
const expVis = {
name: 'visExp',
title: 'Experimental Vis',
group: VisGroups.TOOLS,
stage: 'experimental',
...defaultVisTypeParams,
};
const wrapper = mountWithIntl(
<GroupSelection
visTypesRegistry={visTypesRegistry([..._visTypes, expVis] as VisType[])}
docLinks={docLinks as DocLinksStart}
toggleGroups={jest.fn()}
onVisTypeSelected={jest.fn()}
showExperimental={true}
/>
);
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(true);
});
});

View file

@ -0,0 +1,294 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { FormattedMessage } from '@kbn/i18n/react';
import React, { useCallback, useMemo } from 'react';
import { orderBy } from 'lodash';
import {
EuiFlexGroup,
EuiFlexGrid,
EuiFlexItem,
EuiCard,
EuiIcon,
EuiModalHeader,
EuiModalBody,
EuiModalHeaderTitle,
EuiLink,
EuiText,
EuiSpacer,
EuiBetaBadge,
EuiTitle,
EuiDescriptionListTitle,
EuiDescriptionListDescription,
EuiDescriptionList,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { DocLinksStart } from '../../../../../core/public';
import { VisTypeAlias } from '../../vis_types/vis_type_alias_registry';
import type { VisType, TypesStart } from '../../vis_types';
import { VisGroups } from '../../vis_types';
import './group_selection.scss';
interface GroupSelectionProps {
onVisTypeSelected: (visType: VisType | VisTypeAlias) => void;
visTypesRegistry: TypesStart;
docLinks: DocLinksStart;
toggleGroups: (flag: boolean) => void;
showExperimental: boolean;
}
interface VisCardProps {
onVisTypeSelected: (visType: VisType | VisTypeAlias) => void;
visType: VisType | VisTypeAlias;
showExperimental?: boolean | undefined;
}
function isVisTypeAlias(type: VisType | VisTypeAlias): type is VisTypeAlias {
return 'aliasPath' in type;
}
function GroupSelection(props: GroupSelectionProps) {
const visualizeGuideLink = props.docLinks.links.dashboard.guide;
const promotedVisGroups = useMemo(
() =>
orderBy(
[
...props.visTypesRegistry.getAliases(),
...props.visTypesRegistry.getByGroup(VisGroups.PROMOTED),
],
['promotion', 'title'],
['asc', 'asc']
),
[props.visTypesRegistry]
);
return (
<>
<EuiModalHeader>
<EuiModalHeaderTitle data-test-subj="groupModalHeader">
<FormattedMessage
id="visualizations.newVisWizard.title"
defaultMessage="New visualization"
/>
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody className="visNewVisDialogGroupSelection__body">
<div className="visNewVisDialogGroupSelection__visGroups">
<EuiSpacer size="s" />
<EuiFlexGrid columns={2} data-test-subj="visNewDialogGroups">
{promotedVisGroups.map((visType) => (
<VisGroup
visType={visType}
key={visType.name}
onVisTypeSelected={props.onVisTypeSelected}
/>
))}
</EuiFlexGrid>
<EuiSpacer size="l" />
</div>
<div className="visNewVisDialogGroupSelection__footer">
<EuiSpacer size="l" />
<EuiFlexGrid columns={2}>
{props.visTypesRegistry.getByGroup(VisGroups.AGGBASED).length > 0 && (
<EuiFlexItem>
<EuiCard
titleSize="xs"
layout="horizontal"
onClick={() => props.toggleGroups(false)}
title={
<span data-test-subj="visGroupAggBasedTitle">
{i18n.translate('visualizations.newVisWizard.aggBasedGroupTitle', {
defaultMessage: 'Aggregation based',
})}
</span>
}
data-test-subj="visGroup-aggbased"
description={i18n.translate(
'visualizations.newVisWizard.aggBasedGroupDescription',
{
defaultMessage:
'Use our classic visualize library to create charts based on aggregations.',
}
)}
icon={<EuiIcon type="heatmap" size="xl" color="secondary" />}
className="visNewVisDialog__groupsCard"
>
<EuiLink
data-test-subj="visGroupAggBasedExploreLink"
onClick={() => props.toggleGroups(false)}
>
<EuiText size="s">
{i18n.translate('visualizations.newVisWizard.exploreOptionLinkText', {
defaultMessage: 'Explore options',
})}{' '}
<EuiIcon type="sortRight" />
</EuiText>
</EuiLink>
</EuiCard>
</EuiFlexItem>
)}
{props.visTypesRegistry.getByGroup(VisGroups.TOOLS).length > 0 && (
<EuiFlexItem className="visNewVisDialog__toolsCard">
<EuiSpacer size="m" />
<EuiTitle size="xs">
<span data-test-subj="visGroup-tools">
{i18n.translate('visualizations.newVisWizard.toolsGroupTitle', {
defaultMessage: 'Tools',
})}
</span>
</EuiTitle>
<EuiSpacer size="s" />
<div className="visNewVisDialog__toolsCardGroupContainer">
{props.visTypesRegistry.getByGroup(VisGroups.TOOLS).map((visType) => (
<ToolsGroup
visType={visType}
key={visType.name}
onVisTypeSelected={props.onVisTypeSelected}
showExperimental={props.showExperimental}
/>
))}
</div>
</EuiFlexItem>
)}
</EuiFlexGrid>
<EuiSpacer size="m" />
<EuiDescriptionList
className="visNewVisDialogGroupSelection__footerDescriptionList"
type="responsiveColumn"
>
<EuiDescriptionListTitle className="visNewVisDialogGroupSelection__footerDescriptionListTitle">
<FormattedMessage
id="visualizations.newVisWizard.learnMoreText"
defaultMessage="Want to learn more?"
/>
</EuiDescriptionListTitle>
<EuiDescriptionListDescription>
<EuiLink href={visualizeGuideLink} target="_blank" external>
<FormattedMessage
id="visualizations.newVisWizard.readDocumentationLink"
defaultMessage="Read documentation"
/>
</EuiLink>
</EuiDescriptionListDescription>
</EuiDescriptionList>
</div>
</EuiModalBody>
</>
);
}
const VisGroup = ({ visType, onVisTypeSelected }: VisCardProps) => {
const onClick = useCallback(() => {
onVisTypeSelected(visType);
}, [onVisTypeSelected, visType]);
const shouldDisableCard = isVisTypeAlias(visType) && visType.disabled;
const betaBadgeContent =
shouldDisableCard && 'promoTooltip' in visType ? (
<EuiLink
href={visType?.promoTooltip?.link}
target="_blank"
color="text"
className="visNewVisDialog__groupsCardLink"
>
<EuiBetaBadge
data-test-subj="visTypeBadge"
className="visNewVisDialog__groupsCardBetaBadge"
label={i18n.translate('visualizations.newVisWizard.basicTitle', {
defaultMessage: 'Basic',
})}
tooltipContent={visType?.promoTooltip?.description}
/>
</EuiLink>
) : undefined;
return (
<EuiFlexItem className="visNewVisDialog__groupsCardWrapper">
{betaBadgeContent}
<EuiCard
titleSize="xs"
title={
<span data-test-subj="visTypeTitle">
{'titleInWizard' in visType && visType.titleInWizard
? visType.titleInWizard
: visType.title}
</span>
}
onClick={onClick}
isDisabled={shouldDisableCard}
data-test-subj={`visType-${visType.name}`}
data-vis-stage={!('aliasPath' in visType) ? visType.stage : 'alias'}
aria-label={`visType-${visType.name}`}
description={
<>
<span>{visType.description || ''}</span>
<em> {visType.note || ''}</em>
</>
}
layout="horizontal"
icon={<EuiIcon type={visType.icon || 'empty'} size="xl" color="secondary" />}
className="visNewVisDialog__groupsCard"
/>
</EuiFlexItem>
);
};
const ToolsGroup = ({ visType, onVisTypeSelected, showExperimental }: VisCardProps) => {
const onClick = useCallback(() => {
onVisTypeSelected(visType);
}, [onVisTypeSelected, visType]);
// hide the experimental visualization if lab mode is not enabled
if (!showExperimental && visType.stage === 'experimental') {
return null;
}
return (
<EuiFlexGroup alignItems="center" gutterSize="m" responsive={false}>
<EuiFlexItem grow={false}>
<EuiIcon type={visType.icon || 'empty'} size="l" />
</EuiFlexItem>
<EuiFlexItem>
<EuiFlexGroup gutterSize="xs" alignItems="center" responsive={false}>
<EuiFlexItem grow={false}>
<EuiLink data-test-subj={`visType-${visType.name}`} onClick={onClick}>
{'titleInWizard' in visType && visType.titleInWizard
? visType.titleInWizard
: visType.title}
</EuiLink>
</EuiFlexItem>
{visType.stage === 'experimental' && (
<EuiFlexItem grow={false}>
<EuiBetaBadge
iconType="beaker"
tooltipContent={i18n.translate('visualizations.newVisWizard.experimentalTooltip', {
defaultMessage:
'This visualization might be changed or removed in a future release and is not subject to the support SLA.',
})}
label={i18n.translate('visualizations.newVisWizard.experimentalTitle', {
defaultMessage: 'Experimental',
})}
/>
</EuiFlexItem>
)}
</EuiFlexGroup>
<EuiText color="subdued" size="s">
{visType.description}
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
);
};
export { GroupSelection };

View file

@ -0,0 +1,20 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
export { GroupSelection } from './group_selection';

View file

@ -19,9 +19,9 @@
import React from 'react';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { TypesStart, VisType } from '../vis_types';
import { NewVisModal } from './new_vis_modal';
import { ApplicationStart, SavedObjectsStart } from '../../../../core/public';
import { TypesStart, VisType, VisGroups } from '../vis_types';
import NewVisModal from './new_vis_modal';
import { ApplicationStart, SavedObjectsStart, DocLinksStart } from '../../../../core/public';
import { embeddablePluginMock } from '../../../embeddable/public/mocks';
describe('NewVisModal', () => {
@ -36,31 +36,41 @@ describe('NewVisModal', () => {
responseHandler: 'none',
};
const _visTypes = [
{ name: 'vis', title: 'Vis Type 1', stage: 'production', ...defaultVisTypeParams },
{ name: 'visExp', title: 'Experimental Vis', stage: 'experimental', ...defaultVisTypeParams },
{
name: 'visWithSearch',
title: 'Vis with search',
name: 'vis',
title: 'Vis Type 1',
stage: 'production',
group: VisGroups.PROMOTED,
...defaultVisTypeParams,
},
{
name: 'vis2',
title: 'Vis Type 2',
group: VisGroups.PROMOTED,
stage: 'production',
...defaultVisTypeParams,
},
{
name: 'vis3',
title: 'Vis3',
stage: 'production',
group: VisGroups.TOOLS,
...defaultVisTypeParams,
},
{
name: 'visWithAliasUrl',
title: 'Vis with alias Url',
stage: 'production',
group: VisGroups.PROMOTED,
aliasApp: 'otherApp',
aliasPath: '#/aliasUrl',
},
{
name: 'visAliasWithPromotion',
title: 'Vis alias with promotion',
name: 'visWithSearch',
title: 'Vis with search',
group: VisGroups.AGGBASED,
stage: 'production',
aliasApp: 'anotherApp',
aliasPath: '#/anotherUrl',
promotion: {
description: 'promotion description',
buttonText: 'another app',
},
...defaultVisTypeParams,
},
];
const visTypes: TypesStart = {
@ -71,10 +81,23 @@ describe('NewVisModal', () => {
return (_visTypes as unknown) as VisType[];
},
getAliases: () => [],
unRegisterAlias: () => [],
getByGroup: (group: VisGroups) => {
return (_visTypes.filter((type) => {
return type.group === group;
}) as unknown) as VisType[];
},
};
const addBasePath = (url: string) => `testbasepath${url}`;
const settingsGet = jest.fn();
const uiSettings: any = { get: settingsGet };
const docLinks = {
links: {
dashboard: {
guide: 'test',
},
},
};
beforeAll(() => {
Object.defineProperty(window, 'location', {
@ -88,7 +111,7 @@ describe('NewVisModal', () => {
jest.clearAllMocks();
});
it('should render as expected', () => {
it('should show the aggbased group but not the visualization assigned to this group', () => {
const wrapper = mountWithIntl(
<NewVisModal
isOpen={true}
@ -97,13 +120,15 @@ describe('NewVisModal', () => {
addBasePath={addBasePath}
uiSettings={uiSettings}
application={{} as ApplicationStart}
docLinks={docLinks as DocLinksStart}
savedObjects={{} as SavedObjectsStart}
/>
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.find('[data-test-subj="visGroup-aggbased"]').exists()).toBe(true);
expect(wrapper.find('[data-test-subj="visType-visWithSearch"]').exists()).toBe(false);
});
it('should show a button for regular visualizations', () => {
it('should show the tools group', () => {
const wrapper = mountWithIntl(
<NewVisModal
isOpen={true}
@ -112,13 +137,14 @@ describe('NewVisModal', () => {
addBasePath={addBasePath}
uiSettings={uiSettings}
application={{} as ApplicationStart}
docLinks={docLinks as DocLinksStart}
savedObjects={{} as SavedObjectsStart}
/>
);
expect(wrapper.find('[data-test-subj="visType-vis"]').exists()).toBe(true);
expect(wrapper.find('[data-test-subj="visGroup-tools"]').exists()).toBe(true);
});
it('should sort promoted visualizations first', () => {
it('should display the visualizations of the other group', () => {
const wrapper = mountWithIntl(
<NewVisModal
isOpen={true}
@ -127,19 +153,11 @@ describe('NewVisModal', () => {
addBasePath={addBasePath}
uiSettings={uiSettings}
application={{} as ApplicationStart}
docLinks={docLinks as DocLinksStart}
savedObjects={{} as SavedObjectsStart}
/>
);
expect(
wrapper
.find('button[data-test-subj^="visType-"]')
.map((button) => button.prop('data-test-subj'))
).toEqual([
'visType-visAliasWithPromotion',
'visType-vis',
'visType-visWithAliasUrl',
'visType-visWithSearch',
]);
expect(wrapper.find('[data-test-subj="visType-vis2"]').exists()).toBe(true);
});
describe('open editor', () => {
@ -152,11 +170,12 @@ describe('NewVisModal', () => {
addBasePath={addBasePath}
uiSettings={uiSettings}
application={{} as ApplicationStart}
docLinks={docLinks as DocLinksStart}
savedObjects={{} as SavedObjectsStart}
/>
);
const visButton = wrapper.find('button[data-test-subj="visType-vis"]');
visButton.simulate('click');
const visCard = wrapper.find('[data-test-subj="visType-vis"]').at(0);
visCard.simulate('click');
expect(window.location.assign).toBeCalledWith('testbasepath/app/visualize#/create?type=vis');
});
@ -170,11 +189,12 @@ describe('NewVisModal', () => {
addBasePath={addBasePath}
uiSettings={uiSettings}
application={{} as ApplicationStart}
docLinks={docLinks as DocLinksStart}
savedObjects={{} as SavedObjectsStart}
/>
);
const visButton = wrapper.find('button[data-test-subj="visType-vis"]');
visButton.simulate('click');
const visCard = wrapper.find('[data-test-subj="visType-vis"]').at(0);
visCard.simulate('click');
expect(window.location.assign).toBeCalledWith(
'testbasepath/app/visualize#/create?type=vis&foo=true&bar=42'
);
@ -194,12 +214,13 @@ describe('NewVisModal', () => {
addBasePath={addBasePath}
uiSettings={uiSettings}
application={({ navigateToApp } as unknown) as ApplicationStart}
docLinks={docLinks as DocLinksStart}
stateTransfer={stateTransfer}
savedObjects={{} as SavedObjectsStart}
/>
);
const visButton = wrapper.find('button[data-test-subj="visType-visWithAliasUrl"]');
visButton.simulate('click');
const visCard = wrapper.find('[data-test-subj="visType-visWithAliasUrl"]').at(0);
visCard.simulate('click');
expect(stateTransfer.navigateToEditor).toBeCalledWith('otherApp', {
path: '#/aliasUrl',
state: { originatingApp: 'coolJestTestApp' },
@ -219,17 +240,18 @@ describe('NewVisModal', () => {
addBasePath={addBasePath}
uiSettings={uiSettings}
application={({ navigateToApp } as unknown) as ApplicationStart}
docLinks={docLinks as DocLinksStart}
savedObjects={{} as SavedObjectsStart}
/>
);
const visButton = wrapper.find('button[data-test-subj="visType-visWithAliasUrl"]');
visButton.simulate('click');
const visCard = wrapper.find('[data-test-subj="visType-visWithAliasUrl"]').at(0);
visCard.simulate('click');
expect(navigateToApp).toBeCalledWith('otherApp', { path: '#/aliasUrl' });
expect(onClose).toHaveBeenCalled();
});
});
describe('filter for visualization types', () => {
describe('aggBased visualizations', () => {
it('should render as expected', () => {
const wrapper = mountWithIntl(
<NewVisModal
@ -239,46 +261,15 @@ describe('NewVisModal', () => {
addBasePath={addBasePath}
uiSettings={uiSettings}
application={{} as ApplicationStart}
docLinks={docLinks as DocLinksStart}
savedObjects={{} as SavedObjectsStart}
/>
);
const searchBox = wrapper.find('input[data-test-subj="filterVisType"]');
searchBox.simulate('change', { target: { value: 'with' } });
expect(wrapper).toMatchSnapshot();
});
});
describe('experimental visualizations', () => {
it('should not show experimental visualizations if visualize:enableLabs is false', () => {
settingsGet.mockReturnValue(false);
const wrapper = mountWithIntl(
<NewVisModal
isOpen={true}
onClose={() => null}
visTypesRegistry={visTypes}
addBasePath={addBasePath}
uiSettings={uiSettings}
application={{} as ApplicationStart}
savedObjects={{} as SavedObjectsStart}
/>
);
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(false);
});
it('should show experimental visualizations if visualize:enableLabs is true', () => {
settingsGet.mockReturnValue(true);
const wrapper = mountWithIntl(
<NewVisModal
isOpen={true}
onClose={() => null}
visTypesRegistry={visTypes}
addBasePath={addBasePath}
uiSettings={uiSettings}
application={{} as ApplicationStart}
savedObjects={{} as SavedObjectsStart}
/>
);
expect(wrapper.find('[data-test-subj="visType-visExp"]').exists()).toBe(true);
const aggBasedGroupCard = wrapper
.find('[data-test-subj="visGroupAggBasedExploreLink"]')
.at(0);
aggBasedGroupCard.simulate('click');
expect(wrapper.find('[data-test-subj="visType-visWithSearch"]').exists()).toBe(true);
});
});
});

View file

@ -23,13 +23,20 @@ import { EuiModal, EuiOverlayMask } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { METRIC_TYPE, UiStatsMetricType } from '@kbn/analytics';
import { ApplicationStart, IUiSettingsClient, SavedObjectsStart } from '../../../../core/public';
import {
ApplicationStart,
IUiSettingsClient,
SavedObjectsStart,
DocLinksStart,
} from '../../../../core/public';
import { SearchSelection } from './search_selection';
import { TypeSelection } from './type_selection';
import { TypesStart, VisType, VisTypeAlias } from '../vis_types';
import { GroupSelection } from './group_selection';
import { AggBasedSelection } from './agg_based_selection';
import type { TypesStart, VisType, VisTypeAlias } from '../vis_types';
import { UsageCollectionSetup } from '../../../../plugins/usage_collection/public';
import { EmbeddableStateTransfer } from '../../../embeddable/public';
import { VISUALIZE_ENABLE_LABS_SETTING } from '../../common/constants';
import './dialog.scss';
interface TypeSelectionProps {
isOpen: boolean;
@ -38,6 +45,7 @@ interface TypeSelectionProps {
editorParams?: string[];
addBasePath: (path: string) => string;
uiSettings: IUiSettingsClient;
docLinks: DocLinksStart;
savedObjects: SavedObjectsStart;
usageCollection?: UsageCollectionSetup;
application: ApplicationStart;
@ -48,6 +56,7 @@ interface TypeSelectionProps {
interface TypeSelectionState {
showSearchVisModal: boolean;
showGroups: boolean;
visType?: VisType;
}
@ -72,6 +81,7 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
this.state = {
showSearchVisModal: false,
showGroups: true,
};
this.trackUiMetric = this.props.usageCollection?.reportUiStats.bind(
@ -93,6 +103,8 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
}
);
const WizardComponent = this.state.showGroups ? GroupSelection : AggBasedSelection;
const selectionModal =
this.state.showSearchVisModal && this.state.visType ? (
<EuiModal onClose={this.onCloseModal} className="visNewVisSearchDialog">
@ -101,19 +113,21 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
visType={this.state.visType}
uiSettings={this.props.uiSettings}
savedObjects={this.props.savedObjects}
goBack={() => this.setState({ showSearchVisModal: false })}
/>
</EuiModal>
) : (
<EuiModal
onClose={this.onCloseModal}
className="visNewVisDialog"
className={this.state.showGroups ? 'visNewVisDialog' : 'visNewVisDialog--aggbased'}
aria-label={visNewVisDialogAriaLabel}
>
<TypeSelection
<WizardComponent
showExperimental={this.isLabsEnabled}
onVisTypeSelected={this.onVisTypeSelected}
visTypesRegistry={this.props.visTypesRegistry}
addBasePath={this.props.addBasePath}
docLinks={this.props.docLinks}
toggleGroups={(flag: boolean) => this.setState({ showGroups: flag })}
/>
</EuiModal>
);
@ -185,4 +199,6 @@ class NewVisModal extends React.Component<TypeSelectionProps, TypeSelectionState
}
}
export { NewVisModal };
// Needed for React.lazy
// eslint-disable-next-line import/no-default-export
export { NewVisModal as default };

View file

@ -24,13 +24,15 @@ import React from 'react';
import { IUiSettingsClient, SavedObjectsStart } from '../../../../../core/public';
import { SavedObjectFinderUi } from '../../../../../plugins/saved_objects/public';
import { VisType } from '../../vis_types';
import type { VisType } from '../../vis_types';
import { DialogNavigation } from '../dialog_navigation';
interface SearchSelectionProps {
onSearchSelected: (searchId: string, searchType: string) => void;
visType: VisType;
uiSettings: IUiSettingsClient;
savedObjects: SavedObjectsStart;
goBack: () => void;
}
export class SearchSelection extends React.Component<SearchSelectionProps> {
@ -54,6 +56,7 @@ export class SearchSelection extends React.Component<SearchSelectionProps> {
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<DialogNavigation goBack={this.props.goBack} />
<SavedObjectFinderUi
key="searchSavedObjectFinder"
onChoose={this.props.onSearchSelected}

View file

@ -17,11 +17,10 @@
* under the License.
*/
import React from 'react';
import React, { lazy, Suspense } from 'react';
import ReactDOM from 'react-dom';
import { EuiPortal, EuiProgress } from '@elastic/eui';
import { I18nProvider } from '@kbn/i18n/react';
import { NewVisModal } from './new_vis_modal';
import {
getHttp,
getSavedObjects,
@ -30,8 +29,11 @@ import {
getUsageCollector,
getApplication,
getEmbeddable,
getDocLinks,
} from '../services';
const NewVisModal = lazy(() => import('./new_vis_modal'));
export interface ShowNewVisModalParams {
editorParams?: string[];
onClose?: () => void;
@ -66,20 +68,29 @@ export function showNewVisModal({
document.body.appendChild(container);
const element = (
<I18nProvider>
<NewVisModal
isOpen={true}
onClose={handleClose}
originatingApp={originatingApp}
stateTransfer={getEmbeddable().getStateTransfer()}
outsideVisualizeApp={outsideVisualizeApp}
editorParams={editorParams}
visTypesRegistry={getTypes()}
addBasePath={getHttp().basePath.prepend}
uiSettings={getUISettings()}
savedObjects={getSavedObjects()}
usageCollection={getUsageCollector()}
application={getApplication()}
/>
<Suspense
fallback={
<EuiPortal>
<EuiProgress size="xs" position="fixed" />
</EuiPortal>
}
>
<NewVisModal
isOpen={true}
onClose={handleClose}
originatingApp={originatingApp}
stateTransfer={getEmbeddable().getStateTransfer()}
outsideVisualizeApp={outsideVisualizeApp}
editorParams={editorParams}
visTypesRegistry={getTypes()}
addBasePath={getHttp().basePath.prepend}
uiSettings={getUISettings()}
savedObjects={getSavedObjects()}
usageCollection={getUsageCollector()}
application={getApplication()}
docLinks={getDocLinks()}
/>
</Suspense>
</I18nProvider>
);
ReactDOM.render(element, container);

View file

@ -1,73 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from 'react';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { NewVisHelp } from './new_vis_help';
describe('NewVisHelp', () => {
it('should render as expected', () => {
expect(
shallowWithIntl(
<NewVisHelp
promotedTypes={[
{
aliasApp: 'myApp',
aliasPath: '/my/fancy/new/thing',
description: 'Some desc',
icon: 'whatever',
name: 'whatever',
promotion: {
buttonText: 'Do it now!',
description: 'Look at this fancy new thing!!!',
},
title: 'Test title',
stage: 'production',
},
]}
onPromotionClicked={() => {}}
/>
)
).toMatchInlineSnapshot(`
<EuiText>
<p>
<FormattedMessage
defaultMessage="Start creating your visualization by selecting a type for that visualization."
id="visualizations.newVisWizard.helpText"
values={Object {}}
/>
</p>
<p>
<strong>
Look at this fancy new thing!!!
</strong>
</p>
<EuiButton
fill={true}
iconSide="right"
iconType="popout"
onClick={[Function]}
size="s"
>
Do it now!
</EuiButton>
</EuiText>
`);
});
});

View file

@ -1,57 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Fragment } from 'react';
import { EuiText, EuiButton } from '@elastic/eui';
import { VisTypeAlias } from '../../vis_types';
interface Props {
promotedTypes: VisTypeAlias[];
onPromotionClicked: (visType: VisTypeAlias) => void;
}
export function NewVisHelp(props: Props) {
return (
<EuiText>
<p>
<FormattedMessage
id="visualizations.newVisWizard.helpText"
defaultMessage="Start creating your visualization by selecting a type for that visualization."
/>
</p>
{props.promotedTypes.map((t) => (
<Fragment key={t.name}>
<p>
<strong>{t.promotion!.description}</strong>
</p>
<EuiButton
onClick={() => props.onPromotionClicked(t)}
fill
size="s"
iconType="popout"
iconSide="right"
>
{t.promotion!.buttonText}
</EuiButton>
</Fragment>
))}
</EuiText>
);
}

View file

@ -1,289 +0,0 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { orderBy } from 'lodash';
import React, { ChangeEvent } from 'react';
import {
EuiFieldSearch,
EuiFlexGroup,
EuiFlexItem,
EuiKeyPadMenu,
EuiKeyPadMenuItem,
EuiModalHeader,
EuiModalHeaderTitle,
EuiScreenReaderOnly,
EuiSpacer,
EuiTitle,
} from '@elastic/eui';
import { memoizeLast } from '../../legacy/memoize';
import { VisTypeAlias } from '../../vis_types/vis_type_alias_registry';
import { NewVisHelp } from './new_vis_help';
import { VisHelpText } from './vis_help_text';
import { VisTypeIcon } from './vis_type_icon';
import { VisType, TypesStart } from '../../vis_types';
interface VisTypeListEntry {
type: VisType | VisTypeAlias;
highlighted: boolean;
}
interface TypeSelectionProps {
addBasePath: (path: string) => string;
onVisTypeSelected: (visType: VisType | VisTypeAlias) => void;
visTypesRegistry: TypesStart;
showExperimental: boolean;
}
interface HighlightedType {
name: string;
title: string;
description?: string;
highlightMsg?: string;
}
interface TypeSelectionState {
highlightedType: HighlightedType | null;
query: string;
}
function isVisTypeAlias(type: VisType | VisTypeAlias): type is VisTypeAlias {
return 'aliasPath' in type;
}
class TypeSelection extends React.Component<TypeSelectionProps, TypeSelectionState> {
public state: TypeSelectionState = {
highlightedType: null,
query: '',
};
private readonly getFilteredVisTypes = memoizeLast(this.filteredVisTypes);
public render() {
const { query, highlightedType } = this.state;
const visTypes = this.getFilteredVisTypes(this.props.visTypesRegistry, query);
return (
<React.Fragment>
<EuiModalHeader>
<EuiModalHeaderTitle>
<FormattedMessage
id="visualizations.newVisWizard.title"
defaultMessage="New Visualization"
/>
</EuiModalHeaderTitle>
</EuiModalHeader>
<div className="visNewVisDialog__body">
<EuiFlexGroup gutterSize="xl">
<EuiFlexItem>
<EuiFlexGroup
className="visNewVisDialog__list"
direction="column"
gutterSize="none"
responsive={false}
>
<EuiFlexItem grow={false} className="visNewVisDialog__searchWrapper">
<EuiFieldSearch
placeholder="Filter"
value={query}
onChange={this.onQueryChange}
fullWidth
data-test-subj="filterVisType"
aria-label={i18n.translate(
'visualizations.newVisWizard.filterVisTypeAriaLabel',
{
defaultMessage: 'Filter for a visualization type',
}
)}
/>
</EuiFlexItem>
<EuiFlexItem grow={1} className="visNewVisDialog__typesWrapper">
<EuiScreenReaderOnly>
<span aria-live="polite">
{query && (
<FormattedMessage
id="visualizations.newVisWizard.resultsFound"
defaultMessage="{resultCount} {resultCount, plural,
one {type}
other {types}
} found"
values={{
resultCount: visTypes.filter((type) => type.highlighted).length,
}}
/>
)}
</span>
</EuiScreenReaderOnly>
<EuiKeyPadMenu
className="visNewVisDialog__types"
data-test-subj="visNewDialogTypes"
>
{visTypes.map(this.renderVisType)}
</EuiKeyPadMenu>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<EuiFlexItem className="visNewVisDialog__description" grow={false}>
{highlightedType ? (
<VisHelpText {...highlightedType} />
) : (
<React.Fragment>
<EuiTitle size="s">
<h2>
<FormattedMessage
id="visualizations.newVisWizard.selectVisType"
defaultMessage="Select a visualization type"
/>
</h2>
</EuiTitle>
<EuiSpacer size="m" />
<NewVisHelp
promotedTypes={visTypes
.map((t) => t.type)
.filter((t): t is VisTypeAlias => isVisTypeAlias(t) && Boolean(t.promotion))}
onPromotionClicked={this.props.onVisTypeSelected}
/>
</React.Fragment>
)}
</EuiFlexItem>
</EuiFlexGroup>
</div>
</React.Fragment>
);
}
private filteredVisTypes(visTypes: TypesStart, query: string): VisTypeListEntry[] {
const types = visTypes.all().filter((type) => {
// Filter out all lab visualizations if lab mode is not enabled
if (!this.props.showExperimental && type.stage === 'experimental') {
return false;
}
// Filter out hidden visualizations
if (type.hidden) {
return false;
}
return true;
});
const allTypes = [...types, ...visTypes.getAliases()];
let entries: VisTypeListEntry[];
if (!query) {
entries = allTypes.map((type) => ({ type, highlighted: false }));
} else {
const q = query.toLowerCase();
entries = allTypes.map((type) => {
const matchesQuery =
type.name.toLowerCase().includes(q) ||
type.title.toLowerCase().includes(q) ||
(typeof type.description === 'string' && type.description.toLowerCase().includes(q));
return { type, highlighted: matchesQuery };
});
}
return orderBy(
entries,
['highlighted', 'type.promotion', 'type.title'],
['desc', 'asc', 'asc']
);
}
private renderVisType = (visType: VisTypeListEntry) => {
let stage = {};
let highlightMsg;
if (!isVisTypeAlias(visType.type) && visType.type.stage === 'experimental') {
stage = {
betaBadgeLabel: i18n.translate('visualizations.newVisWizard.experimentalTitle', {
defaultMessage: 'Experimental',
}),
betaBadgeTooltipContent: i18n.translate('visualizations.newVisWizard.experimentalTooltip', {
defaultMessage:
'This visualization might be changed or removed in a future release and is not subject to the support SLA.',
}),
};
highlightMsg = i18n.translate('visualizations.newVisWizard.experimentalDescription', {
defaultMessage:
'This visualization is experimental. The design and implementation are less mature than stable visualizations and might be subject to change.',
});
} else if (isVisTypeAlias(visType.type) && visType.type.stage === 'beta') {
const aliasDescription = i18n.translate('visualizations.newVisWizard.betaDescription', {
defaultMessage:
'This visualization is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features',
});
stage = {
betaBadgeLabel: i18n.translate('visualizations.newVisWizard.betaTitle', {
defaultMessage: 'Beta',
}),
betaBadgeTooltipContent: aliasDescription,
};
highlightMsg = aliasDescription;
}
const isDisabled = this.state.query !== '' && !visType.highlighted;
const onClick = () => this.props.onVisTypeSelected(visType.type);
const highlightedType: HighlightedType = {
title: visType.type.title,
name: visType.type.name,
description: visType.type.description,
highlightMsg,
};
return (
<EuiKeyPadMenuItem
key={visType.type.name}
label={<span data-test-subj="visTypeTitle">{visType.type.title}</span>}
onClick={onClick}
onFocus={() => this.setHighlightType(highlightedType)}
onMouseEnter={() => this.setHighlightType(highlightedType)}
onMouseLeave={() => this.setHighlightType(null)}
onBlur={() => this.setHighlightType(null)}
className="visNewVisDialog__type"
data-test-subj={`visType-${visType.type.name}`}
data-vis-stage={!isVisTypeAlias(visType.type) ? visType.type.stage : 'alias'}
disabled={isDisabled}
aria-describedby={`visTypeDescription-${visType.type.name}`}
{...stage}
>
<VisTypeIcon
icon={visType.type.icon}
image={'image' in visType.type ? visType.type.image : undefined}
/>
</EuiKeyPadMenuItem>
);
};
private setHighlightType(highlightedType: HighlightedType | null) {
this.setState({
highlightedType,
});
}
private onQueryChange = (ev: ChangeEvent<HTMLInputElement>) => {
this.setState({
query: ev.target.value,
});
};
}
export { TypeSelection };

View file

@ -42,10 +42,11 @@ export default function ({ getService, getPageObjects }) {
});
describe('add new visualization link', () => {
it('adds new visualiztion via the top nav link', async () => {
it('adds new visualization via the top nav link', async () => {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
await PageObjects.dashboard.switchToEditMode();
await dashboardAddPanel.clickCreateNewLink();
await PageObjects.visualize.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationExpectSuccess(
@ -63,6 +64,7 @@ export default function ({ getService, getPageObjects }) {
const originalPanelCount = await PageObjects.dashboard.getPanelCount();
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickAddNewEmbeddableLink('visualization');
await PageObjects.visualize.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationExpectSuccess(

View file

@ -134,6 +134,7 @@ export default function ({ getService, getPageObjects }) {
await dashboardAddPanel.ensureAddPanelIsShowing();
await dashboardAddPanel.clickAddNewEmbeddableLink('visualization');
await PageObjects.visualize.clickAggBasedVisualizations();
await PageObjects.visualize.clickAreaChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visualize.saveVisualizationExpectSuccess('new viz panel', {

View file

@ -74,7 +74,7 @@ export default function ({ getService, getPageObjects }) {
*/
it('should create initial vertical bar chart', async function () {
log.debug('create shakespeare vertical bar chart');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickVerticalBarChart();
await PageObjects.visualize.clickNewSearch('shakespeare');
await PageObjects.visChart.waitForVisualization();

View file

@ -41,7 +41,7 @@ export default function ({ getService, getPageObjects }) {
const initAreaChart = async () => {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickAreaChart');
await PageObjects.visualize.clickAreaChart();
log.debug('clickNewSearch');
@ -390,7 +390,7 @@ export default function ({ getService, getPageObjects }) {
const toTime = 'Jan 1, 2020 @ 00:00:00.000';
it('should render a yearly area with 12 svg paths', async () => {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickAreaChart');
await PageObjects.visualize.clickAreaChart();
log.debug('clickNewSearch');
@ -413,7 +413,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should render monthly areas with 168 svg paths', async () => {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickAreaChart');
await PageObjects.visualize.clickAreaChart();
log.debug('clickNewSearch');

View file

@ -33,10 +33,21 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.visualize.navigateToNewVisualization();
});
it('should show the correct chart types', async function () {
it('should show the promoted vis types for the first step', async function () {
const expectedChartTypes = ['Custom visualization', 'Lens', 'Maps', 'TSVB'];
log.debug('oss= ' + isOss);
// find all the chart types and make sure there all there
const chartTypes = (await PageObjects.visualize.getPromotedVisTypes()).sort();
log.debug('returned chart types = ' + chartTypes);
log.debug('expected chart types = ' + expectedChartTypes);
expect(chartTypes).to.eql(expectedChartTypes);
});
it('should show the correct agg based chart types', async function () {
await PageObjects.visualize.clickAggBasedVisualizations();
let expectedChartTypes = [
'Area',
'Controls',
'Coordinate Map',
'Data Table',
'Gauge',
@ -44,18 +55,14 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
'Heat Map',
'Horizontal Bar',
'Line',
'Markdown',
'Metric',
'Pie',
'Region Map',
'TSVB',
'Tag Cloud',
'Timelion',
'Vega',
'Vertical Bar',
];
if (!isOss) {
expectedChartTypes.push('Maps', 'Lens');
expectedChartTypes = _.remove(expectedChartTypes, function (n) {
return n !== 'Coordinate Map';
});

View file

@ -32,7 +32,7 @@ export default function ({ getService, getPageObjects }) {
before(async function () {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickDataTable');
await PageObjects.visualize.clickDataTable();
log.debug('clickNewSearch');
@ -107,7 +107,7 @@ export default function ({ getService, getPageObjects }) {
}
// load a plain table
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
@ -152,7 +152,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should show correct data when using average pipeline aggregation', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
@ -167,7 +167,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should show correct data for a data table with date histogram', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
@ -189,7 +189,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should show correct data for a data table with date histogram', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
@ -224,7 +224,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should show correct data for a data table with top hits', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
@ -238,7 +238,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should show correct data for a data table with range agg', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
@ -257,7 +257,7 @@ export default function ({ getService, getPageObjects }) {
describe('otherBucket', () => {
before(async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
@ -295,7 +295,7 @@ export default function ({ getService, getPageObjects }) {
describe('metricsOnAllLevels', () => {
before(async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
@ -389,7 +389,7 @@ export default function ({ getService, getPageObjects }) {
describe('split tables', () => {
before(async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();

View file

@ -32,7 +32,7 @@ export default function ({ getService, getPageObjects }) {
before(async function () {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickDataTable');
await PageObjects.visualize.clickDataTable();
log.debug('clickNewSearch');
@ -97,7 +97,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should show correct data when using average pipeline aggregation', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch(
PageObjects.visualize.index.LOGSTASH_NON_TIME_BASED
@ -114,7 +114,7 @@ export default function ({ getService, getPageObjects }) {
describe('data table with date histogram', async () => {
before(async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch(
PageObjects.visualize.index.LOGSTASH_NON_TIME_BASED

View file

@ -40,7 +40,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
before(async function () {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickDataTable');
await PageObjects.visualize.clickDataTable();
log.debug('clickNewSearch');

View file

@ -35,7 +35,7 @@ export default function ({ getService, getPageObjects }) {
describe('embedding', () => {
describe('a data table', () => {
before(async function () {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();

View file

@ -27,7 +27,7 @@ export default ({ getService, getPageObjects }) => {
describe('experimental visualizations', () => {
beforeEach(async () => {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.waitForVisualizationSelectPage();
});

View file

@ -29,7 +29,7 @@ export default function ({ getService, getPageObjects }) {
describe('gauge chart', function indexPatternCreation() {
async function initGaugeVis() {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickGauge');
await PageObjects.visualize.clickGauge();
await PageObjects.visualize.clickNewSearch();

View file

@ -29,7 +29,7 @@ export default function ({ getService, getPageObjects }) {
before(async function () {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickHeatmapChart');
await PageObjects.visualize.clickHeatmapChart();
await PageObjects.visualize.clickNewSearch();

View file

@ -33,7 +33,7 @@ export default function ({ getService, getPageObjects }) {
describe('histogram agg onSearchRequestStart', function () {
before(async function () {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickDataTable');
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickNewSearch();

View file

@ -25,7 +25,7 @@ export default function ({ getService, getPageObjects }) {
describe('inspector', function describeIndexTests() {
before(async function () {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickVerticalBarChart();
await PageObjects.visualize.clickNewSearch();

View file

@ -37,7 +37,7 @@ export default function ({ getService, getPageObjects }) {
const initLineChart = async function () {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickLineChart');
await PageObjects.visualize.clickLineChart();
await PageObjects.visualize.clickNewSearch();
@ -244,7 +244,7 @@ export default function ({ getService, getPageObjects }) {
describe('pipeline aggregations', () => {
before(async () => {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickLineChart');
await PageObjects.visualize.clickLineChart();
await PageObjects.visualize.clickNewSearch();

View file

@ -47,7 +47,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('should create a visualization from a saved search', async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickDataTable();
await PageObjects.visualize.clickSavedSearch(savedSearchName);
await PageObjects.timePicker.setDefaultAbsoluteRange();

View file

@ -29,7 +29,7 @@ export default function ({ getService, getPageObjects }) {
describe('metric chart', function () {
before(async function () {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickMetric');
await PageObjects.visualize.clickMetric();
await PageObjects.visualize.clickNewSearch();

View file

@ -37,7 +37,7 @@ export default function ({ getService, getPageObjects }) {
const vizName1 = 'Visualization PieChart';
before(async function () {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickPieChart');
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
@ -94,7 +94,7 @@ export default function ({ getService, getPageObjects }) {
it('should show other and missing bucket', async function () {
const expectedTableData = ['win 8', 'win xp', 'win 7', 'ios', 'Missing', 'Other'];
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickPieChart');
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
@ -292,7 +292,7 @@ export default function ({ getService, getPageObjects }) {
describe('empty time window', () => {
it('should show no data message when no data on selected timerange', async function () {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickPieChart');
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
@ -321,7 +321,7 @@ export default function ({ getService, getPageObjects }) {
describe('multi series slice', () => {
before(async () => {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickPieChart');
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
@ -443,7 +443,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should still showing pie chart when a subseries have zero data', async function () {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickPieChart');
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
@ -471,7 +471,7 @@ export default function ({ getService, getPageObjects }) {
describe('split chart', () => {
before(async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickPieChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();

View file

@ -36,7 +36,7 @@ export default function ({ getService, getPageObjects }) {
async function initChart() {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickLineChart');
await PageObjects.visualize.clickLineChart();
await PageObjects.visualize.clickNewSearch();
@ -197,7 +197,7 @@ export default function ({ getService, getPageObjects }) {
describe('show values on chart', () => {
before(async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickVerticalBarChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
@ -232,7 +232,7 @@ export default function ({ getService, getPageObjects }) {
const customLabel = 'myLabel';
const axisTitle = 'myTitle';
before(async function () {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickLineChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.visEditor.selectYAxisAggregation('Average', 'bytes', customLabel, 1);

View file

@ -28,7 +28,7 @@ export default function ({ getService, getPageObjects }) {
before(async function () {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickRegionMap');
await PageObjects.visualize.clickRegionMap();
await PageObjects.visualize.clickNewSearch();

View file

@ -43,7 +43,7 @@ export default function ({ getService, getPageObjects }) {
before(async function () {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickTagCloud');
await PageObjects.visualize.clickTagCloud();
await PageObjects.visualize.clickNewSearch();

View file

@ -41,7 +41,7 @@ export default function ({ getService, getPageObjects }) {
await browser.setWindowSize(1280, 1000);
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickTileMap');
await PageObjects.visualize.clickTileMap();
await PageObjects.visualize.clickNewSearch();
@ -61,7 +61,7 @@ export default function ({ getService, getPageObjects }) {
await browser.setWindowSize(1280, 1000);
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickTileMap');
await PageObjects.visualize.clickTileMap();
await PageObjects.visualize.clickNewSearch();
@ -240,7 +240,7 @@ export default function ({ getService, getPageObjects }) {
await browser.setWindowSize(1280, 1000);
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickTileMap');
await PageObjects.visualize.clickTileMap();
await PageObjects.visualize.clickNewSearch();

View file

@ -32,7 +32,7 @@ export default function ({ getService, getPageObjects }) {
const initBarChart = async () => {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickVerticalBarChart');
await PageObjects.visualize.clickVerticalBarChart();
await PageObjects.visualize.clickNewSearch();
@ -64,7 +64,7 @@ export default function ({ getService, getPageObjects }) {
});
it('should not filter out first label after rotation of the chart', async function () {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickVerticalBarChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();
@ -88,7 +88,7 @@ export default function ({ getService, getPageObjects }) {
describe('bar charts range on x axis', () => {
it('should individual bars for each configured range', async function () {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickVerticalBarChart();
await PageObjects.visualize.clickNewSearch();
await PageObjects.timePicker.setDefaultAbsoluteRange();

View file

@ -30,7 +30,7 @@ export default function ({ getService, getPageObjects }) {
const initBarChart = async () => {
log.debug('navigateToApp visualize');
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
log.debug('clickVerticalBarChart');
await PageObjects.visualize.clickVerticalBarChart();
await PageObjects.visualize.clickNewSearch(

View file

@ -47,6 +47,14 @@ export function VisualizePageProvider({ getService, getPageObjects }: FtrProvide
await listingTable.clickNewButton('createVisualizationPromptButton');
}
public async clickAggBasedVisualizations() {
await testSubjects.click('visGroupAggBasedExploreLink');
}
public async goBackToGroups() {
await testSubjects.click('goBackLink');
}
public async createVisualizationPromptButton() {
await testSubjects.click('createVisualizationPromptButton');
}
@ -59,6 +67,21 @@ export function VisualizePageProvider({ getService, getPageObjects }: FtrProvide
.map((chart) => $(chart).findTestSubject('visTypeTitle').text().trim());
}
public async getPromotedVisTypes() {
const chartTypeField = await testSubjects.find('visNewDialogGroups');
const $ = await chartTypeField.parseDomContent();
const promotedVisTypes: string[] = [];
$('button')
.toArray()
.forEach((chart) => {
const title = $(chart).findTestSubject('visTypeTitle').text().trim();
if (title) {
promotedVisTypes.push(title);
}
});
return promotedVisTypes;
}
public async waitForVisualizationSelectPage() {
await retry.try(async () => {
const visualizeSelectTypePage = await testSubjects.find('visNewDialogTypes');
@ -68,10 +91,27 @@ export function VisualizePageProvider({ getService, getPageObjects }: FtrProvide
});
}
public async waitForGroupsSelectPage() {
await retry.try(async () => {
const visualizeSelectGroupStep = await testSubjects.find('visNewDialogGroups');
if (!(await visualizeSelectGroupStep.isDisplayed())) {
throw new Error('wait for vis groups select step');
}
});
}
public async navigateToNewVisualization() {
await common.navigateToApp('visualize');
await header.waitUntilLoadingHasFinished();
await this.clickNewVisualization();
await this.waitForGroupsSelectPage();
}
public async navigateToNewAggBasedVisualization() {
await common.navigateToApp('visualize');
await header.waitUntilLoadingHasFinished();
await this.clickNewVisualization();
await this.clickAggBasedVisualizations();
await this.waitForVisualizationSelectPage();
}

View file

@ -147,6 +147,7 @@ export function DashboardVisualizationProvider({ getService, getPageObjects }: F
await PageObjects.dashboard.switchToEditMode();
}
await this.ensureNewVisualizationDialogIsShowing();
await PageObjects.visualize.clickAggBasedVisualizations();
await PageObjects.visualize.clickMetric();
await find.clickByCssSelector('li.euiListGroupItem:nth-of-type(2)');
await testSubjects.exists('visualizeSaveButton');

View file

@ -30,7 +30,7 @@ export default function ({ getService, getPageObjects }) {
describe('self changing vis', function describeIndexTests() {
before(async () => {
await PageObjects.visualize.navigateToNewVisualization();
await PageObjects.visualize.navigateToNewAggBasedVisualization();
await PageObjects.visualize.clickVisType('self_changing_vis');
});

View file

@ -19,5 +19,5 @@
"optionalPlugins": ["usageCollection", "taskManager", "globalSearch", "savedObjectsTagging"],
"configPath": ["xpack", "lens"],
"extraPublicDirs": ["common/constants"],
"requiredBundles": ["savedObjects", "kibanaUtils", "kibanaReact", "embeddable"]
"requiredBundles": ["savedObjects", "kibanaUtils", "kibanaReact", "embeddable", "lensOss"]
}

View file

@ -9,7 +9,7 @@ import { DataPublicPluginSetup, DataPublicPluginStart } from 'src/plugins/data/p
import { EmbeddableSetup, EmbeddableStart } from 'src/plugins/embeddable/public';
import { DashboardStart } from 'src/plugins/dashboard/public';
import { ExpressionsSetup, ExpressionsStart } from 'src/plugins/expressions/public';
import { VisualizationsSetup } from 'src/plugins/visualizations/public';
import { VisualizationsSetup, VisualizationsStart } from 'src/plugins/visualizations/public';
import { NavigationPublicPluginStart } from 'src/plugins/navigation/public';
import { UrlForwardingSetup } from 'src/plugins/url_forwarding/public';
import { GlobalSearchPluginSetup } from '../../global_search/public';
@ -35,6 +35,7 @@ import {
VISUALIZE_FIELD_TRIGGER,
} from '../../../../src/plugins/ui_actions/public';
import { NOT_INTERNATIONALIZED_PRODUCT_NAME } from '../common';
import { PLUGIN_ID_OSS } from '../../../../src/plugins/lens_oss/common/constants';
import { EditorFrameStart } from './types';
import { getLensAliasConfig } from './vis_type_alias';
import { visualizeFieldAction } from './trigger_actions/visualize_field_actions';
@ -58,6 +59,7 @@ export interface LensPluginStartDependencies {
navigation: NavigationPublicPluginStart;
uiActions: UiActionsStart;
dashboard: DashboardStart;
visualizations: VisualizationsStart;
embeddable: EmbeddableStart;
charts: ChartsPluginStart;
savedObjectsTagging?: SavedObjectTaggingPluginStart;
@ -170,6 +172,8 @@ export class LensPlugin {
start(core: CoreStart, startDependencies: LensPluginStartDependencies) {
this.createEditorFrame = this.editorFrameService.start(core, startDependencies).createInstance;
// unregisters the OSS alias
startDependencies.visualizations.unRegisterAlias(PLUGIN_ID_OSS);
// unregisters the Visualize action and registers the lens one
if (startDependencies.uiActions.hasAction(ACTION_VISUALIZE_FIELD)) {
startDependencies.uiActions.unregisterAction(ACTION_VISUALIZE_FIELD);

View file

@ -12,19 +12,16 @@ export const getLensAliasConfig = (): VisTypeAlias => ({
aliasPath: getBasePath(),
aliasApp: 'lens',
name: 'lens',
promotion: {
description: i18n.translate('xpack.lens.visTypeAlias.promotion.description', {
defaultMessage: 'Try Lens, our new, intuitive way to create visualizations.',
}),
buttonText: i18n.translate('xpack.lens.visTypeAlias.promotion.buttonText', {
defaultMessage: 'Go to Lens',
}),
},
promotion: true,
title: i18n.translate('xpack.lens.visTypeAlias.title', {
defaultMessage: 'Lens',
}),
description: i18n.translate('xpack.lens.visTypeAlias.description', {
defaultMessage: `Lens is a simpler way to create basic visualizations`,
defaultMessage:
'Create visualizations with our drag and drop editor. Switch between visualization types at any time.',
}),
note: i18n.translate('xpack.lens.visTypeAlias.note', {
defaultMessage: 'Recommended for most users.',
}),
icon: 'lensApp',
stage: 'production',

View file

@ -22,5 +22,5 @@
"ui": true,
"server": true,
"extraPublicDirs": ["common/constants"],
"requiredBundles": ["kibanaReact", "kibanaUtils", "home"]
"requiredBundles": ["kibanaReact", "kibanaUtils", "home", "mapsOss"]
}

View file

@ -16,14 +16,6 @@ export function getMapsVisTypeAlias(visualizations, showMapVisualizationTypes) {
defaultMessage: 'Create and style maps with multiple layers and indices.',
});
const legacyMapVisualizationWarning = i18n.translate(
'xpack.maps.visTypeAlias.legacyMapVizWarning',
{
defaultMessage: `Use the Maps app instead of Coordinate Map and Region Map.
The Maps app offers more functionality and is easier to use.`,
}
);
return {
aliasApp: APP_ID,
aliasPath: `/${MAP_PATH}`,
@ -31,9 +23,7 @@ The Maps app offers more functionality and is easier to use.`,
title: i18n.translate('xpack.maps.visTypeAlias.title', {
defaultMessage: 'Maps',
}),
description: showMapVisualizationTypes
? `${description} ${legacyMapVisualizationWarning}`
: description,
description: description,
icon: APP_ICON,
stage: 'production',
};

View file

@ -28,8 +28,12 @@ import { featureCatalogueEntry } from './feature_catalogue_entry';
// @ts-ignore
import { getMapsVisTypeAlias } from './maps_vis_type_alias';
import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public';
import {
VisualizationsSetup,
VisualizationsStart,
} from '../../../../src/plugins/visualizations/public';
import { APP_ICON_SOLUTION, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants';
import { PLUGIN_ID_OSS } from '../../../../src/plugins/maps_oss/common/constants';
import { VISUALIZE_GEO_FIELD_TRIGGER } from '../../../../src/plugins/ui_actions/public';
import {
createMapsUrlGenerator,
@ -72,6 +76,7 @@ export interface MapsPluginStartDependencies {
navigation: NavigationPublicPluginStart;
uiActions: UiActionsStart;
share: SharePluginStart;
visualizations: VisualizationsStart;
savedObjects: SavedObjectsStart;
}
@ -145,6 +150,8 @@ export class MapsPlugin
setLicensingPluginStart(plugins.licensing);
plugins.uiActions.addTriggerAction(VISUALIZE_GEO_FIELD_TRIGGER, visualizeGeoFieldAction);
setStartServices(core, plugins);
// unregisters the OSS alias
plugins.visualizations.unRegisterAlias(PLUGIN_ID_OSS);
return {
createSecurityLayerDescriptors,

View file

@ -2732,7 +2732,6 @@
"inputControl.listControl.disableTooltip": "「{label}」が設定されるまで無効です。",
"inputControl.listControl.unableToFetchTooltip": "用語を取得できません、エラー: {errorMessage}",
"inputControl.rangeControl.unableToFetchTooltip": "範囲 (最低値と最高値) を取得できません、エラー: {errorMessage}",
"inputControl.register.controlsDescription": "ダッシュボードを簡単に操作できるように、インタラクティブなコントロールを作成します。",
"inputControl.register.controlsTitle": "コントロール",
"inputControl.register.tabs.controlsTitle": "コントロール",
"inputControl.register.tabs.optionsTitle": "オプション",
@ -3977,7 +3976,6 @@
"visTypeTimeseries.indexPattern.timeRange.lastValue": "最終値",
"visTypeTimeseries.indexPattern.timeRange.selectTimeRange": "選択してください",
"visTypeTimeseries.indexPatternLabel": "インデックスパターン",
"visTypeTimeseries.kbnVisTypes.metricsDescription": "ビジュアルパイプラインインターフェースを使用して時系列のチャートを作成します。",
"visTypeTimeseries.kbnVisTypes.metricsTitle": "TSVB",
"visTypeTimeseries.markdown.alignOptions.bottomLabel": "一番下",
"visTypeTimeseries.markdown.alignOptions.middleLabel": "真ん中",
@ -4332,7 +4330,6 @@
"visTypeVega.mapView.minZoomAndMaxZoomHaveBeenSwappedWarningMessage": "{minZoomPropertyName} と {maxZoomPropertyName} が交換されました",
"visTypeVega.mapView.resettingPropertyToMaxValueWarningMessage": "{name} を {max} にリセットしています",
"visTypeVega.mapView.resettingPropertyToMinValueWarningMessage": "{name} を {min} にリセットしています",
"visTypeVega.type.vegaDescription": "Vega と Vega-Lite を使用してカスタムビジュアライゼーションを作成します。",
"visTypeVega.urlParser.dataUrlRequiresUrlParameterInFormErrorMessage": "{dataUrlParam} には「{formLink}」の形で {urlParam} パラメーターが必要です",
"visTypeVega.urlParser.urlShouldHaveQuerySubObjectWarningMessage": "{urlObject} を使用するには {subObjectName} サブオブジェクトが必要です",
"visTypeVega.vegaParser.autoSizeDoesNotAllowFalse": "{autoSizeParam}が有効です。無効にするには、{autoSizeParam}を{noneParam}に設定してください",
@ -4480,7 +4477,6 @@
"visTypeVislib.gauge.alignmentAutomaticTitle": "自動",
"visTypeVislib.gauge.alignmentHorizontalTitle": "横",
"visTypeVislib.gauge.alignmentVerticalTitle": "縦",
"visTypeVislib.gauge.gaugeDescription": "ゲージはメトリックのステータスを示します。メトリックの値とリファレンスしきい値との関連性を示すのに使用します。",
"visTypeVislib.gauge.gaugeTitle": "ゲージ",
"visTypeVislib.gauge.gaugeTypes.arcText": "弧形",
"visTypeVislib.gauge.gaugeTypes.circleText": "円",
@ -4563,21 +4559,16 @@
"visualizations.function.visDimension.help": "visConfig ディメンションオブジェクトを生成します",
"visualizations.functions.visualization.help": "シンプルなビジュアライゼーションです",
"visualizations.initializeWithoutIndexPatternErrorMessage": "インデックスパターンなしで集約を初期化しようとしています",
"visualizations.newVisWizard.betaDescription": "このビジュアライゼーションはベータ段階で、変更される可能性があります。デザインとコードはオフィシャル GA 機能よりも完成度が低く、現状のまま保証なしで提供されています。ベータ機能にはオフィシャル GA 機能の SLA が適用されません",
"visualizations.newVisWizard.betaTitle": "ベータ",
"visualizations.newVisWizard.chooseSourceTitle": "ソースの選択",
"visualizations.newVisWizard.experimentalDescription": "このビジュアライゼーションは実験的なものです。デザインと導入は安定したビジュアライゼーションよりも完成度が低く、変更される可能性があります。",
"visualizations.newVisWizard.experimentalTitle": "実験的",
"visualizations.newVisWizard.experimentalTooltip": "このビジュアライゼーションは今後のリリースで変更または削除される可能性があり、SLA のサポート対象になりません。",
"visualizations.newVisWizard.filterVisTypeAriaLabel": "ビジュアライゼーションのタイプでフィルタリング",
"visualizations.newVisWizard.helpText": "タイプを選択してビジュアライゼーションの作成を始めましょう。",
"visualizations.newVisWizard.helpTextAriaLabel": "タイプを選択してビジュアライゼーションの作成を始めましょう。ESC を押してこのモーダルを閉じます。Tab キーを押して次に進みます。",
"visualizations.newVisWizard.newVisTypeTitle": "新規 {visTypeName}",
"visualizations.newVisWizard.resultsFound": "{resultCount} 個の{resultCount, plural, one {タイプ} other {タイプ} } が見つかりました",
"visualizations.newVisWizard.searchSelection.notFoundLabel": "一致インデックスまたは保存した検索が見つかりません。",
"visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "インデックスパターン",
"visualizations.newVisWizard.searchSelection.savedObjectType.search": "保存検索",
"visualizations.newVisWizard.selectVisType": "ビジュアライゼーションのタイプを選択してください",
"visualizations.newVisWizard.title": "新規ビジュアライゼーション",
"visualizations.noResultsFoundTitle": "結果が見つかりませんでした",
"visualizations.savedObjectName": "ビジュアライゼーション",
@ -10923,9 +10914,6 @@
"xpack.lens.sugegstion.refreshSuggestionLabel": "更新",
"xpack.lens.suggestion.refreshSuggestionTooltip": "選択したビジュアライゼーションに基づいて、候補を更新します。",
"xpack.lens.suggestions.currentVisLabel": "現在",
"xpack.lens.visTypeAlias.description": "レンズは基本的なビジュアライゼーションを作成するシンプルな方法です",
"xpack.lens.visTypeAlias.promotion.buttonText": "レンズに移動",
"xpack.lens.visTypeAlias.promotion.description": "レンズは直感的に使える新しいビジュアライゼーションの作成方法です。お試しください。",
"xpack.lens.visTypeAlias.title": "レンズビジュアライゼーション",
"xpack.lens.visTypeAlias.type": "レンズ",
"xpack.lens.xyChart.addLayer": "レイヤーを追加",
@ -11686,8 +11674,6 @@
"xpack.maps.viewControl.latLabel": "緯度:",
"xpack.maps.viewControl.lonLabel": "経度:",
"xpack.maps.viewControl.zoomLabel": "ズーム:",
"xpack.maps.visTypeAlias.description": "マップを作成し、複数のレイヤーとインデックスを使用して、スタイルを設定します。",
"xpack.maps.visTypeAlias.legacyMapVizWarning": "座標マップと地域マップの代わりに、マップアプリを使用します。\nマップアプリはより機能が豊富で、使いやすくなっています。",
"xpack.maps.visTypeAlias.title": "マップ",
"xpack.maps.xyztmssource.attributionLink": "属性テキストにはリンクが必要です",
"xpack.maps.xyztmssource.attributionLinkLabel": "属性リンク",

View file

@ -2733,7 +2733,6 @@
"inputControl.listControl.disableTooltip": "设置 “{label}” 之前禁用。",
"inputControl.listControl.unableToFetchTooltip": "无法提取词,错误:{errorMessage}",
"inputControl.rangeControl.unableToFetchTooltip": "无法提取范围最小值和最大值,错误:{errorMessage}",
"inputControl.register.controlsDescription": "创建交互控件,以方便仪表板操控。",
"inputControl.register.controlsTitle": "控件",
"inputControl.register.tabs.controlsTitle": "控件",
"inputControl.register.tabs.optionsTitle": "选项",
@ -3978,7 +3977,6 @@
"visTypeTimeseries.indexPattern.timeRange.lastValue": "最后值",
"visTypeTimeseries.indexPattern.timeRange.selectTimeRange": "选择",
"visTypeTimeseries.indexPatternLabel": "索引模式",
"visTypeTimeseries.kbnVisTypes.metricsDescription": "使用可视化管道界面构建时间序列",
"visTypeTimeseries.kbnVisTypes.metricsTitle": "TSVB",
"visTypeTimeseries.markdown.alignOptions.bottomLabel": "下",
"visTypeTimeseries.markdown.alignOptions.middleLabel": "中",
@ -4333,7 +4331,6 @@
"visTypeVega.mapView.minZoomAndMaxZoomHaveBeenSwappedWarningMessage": "已交换 {minZoomPropertyName} 和 {maxZoomPropertyName}",
"visTypeVega.mapView.resettingPropertyToMaxValueWarningMessage": "将 {name} 重置为 {max}",
"visTypeVega.mapView.resettingPropertyToMinValueWarningMessage": "将 {name} 重置为 {min}",
"visTypeVega.type.vegaDescription": "使用 Vega 和 Vega-Lite 创建定制可视化",
"visTypeVega.urlParser.dataUrlRequiresUrlParameterInFormErrorMessage": "{dataUrlParam} 需要以“{formLink}”形式的 {urlParam} 参数",
"visTypeVega.urlParser.urlShouldHaveQuerySubObjectWarningMessage": "使用 {urlObject} 应具有 {subObjectName} 子对象",
"visTypeVega.vegaParser.autoSizeDoesNotAllowFalse": "{autoSizeParam} 已启用,只能通过将 {autoSizeParam} 设置为 {noneParam} 来禁用",
@ -4482,7 +4479,6 @@
"visTypeVislib.gauge.alignmentAutomaticTitle": "自动",
"visTypeVislib.gauge.alignmentHorizontalTitle": "水平",
"visTypeVislib.gauge.alignmentVerticalTitle": "垂直",
"visTypeVislib.gauge.gaugeDescription": "仪表盘图指示指标的状态。用于显示指标值与参考阈值的相关程度。",
"visTypeVislib.gauge.gaugeTitle": "仪表盘图",
"visTypeVislib.gauge.gaugeTypes.arcText": "弧形",
"visTypeVislib.gauge.gaugeTypes.circleText": "圆形",
@ -4565,21 +4561,16 @@
"visualizations.function.visDimension.help": "生成 visConfig 维度对象",
"visualizations.functions.visualization.help": "简单可视化",
"visualizations.initializeWithoutIndexPatternErrorMessage": "正在尝试在不使用索引模式的情况下初始化聚合",
"visualizations.newVisWizard.betaDescription": "此可视化为公测版,可能会进行更改。设计和代码相对于正式发行版功能还不够成熟,将按原样提供,且不提供任何保证。公测版功能不受正式发行版功能支持 SLA 的约束",
"visualizations.newVisWizard.betaTitle": "公测版",
"visualizations.newVisWizard.chooseSourceTitle": "选择源",
"visualizations.newVisWizard.experimentalDescription": "这是实验性可视化。与稳定的可视化相比,其设计和实现均不够成熟,可能会随时发生更改。",
"visualizations.newVisWizard.experimentalTitle": "实验性",
"visualizations.newVisWizard.experimentalTooltip": "未来版本可能会更改或删除此可视化,其不受支持 SLA 的约束。",
"visualizations.newVisWizard.filterVisTypeAriaLabel": "筛留可视化类型",
"visualizations.newVisWizard.helpText": "通过为该可视化选择类型,来开始创建您的可视化。",
"visualizations.newVisWizard.helpTextAriaLabel": "通过为该可视化选择类型,来开始创建您的可视化。按 Esc 键关闭此模式。按 Tab 键继续。",
"visualizations.newVisWizard.newVisTypeTitle": "新建{visTypeName}",
"visualizations.newVisWizard.resultsFound": "找到了 {resultCount} 个{resultCount, plural, one {类型} other {类型} }",
"visualizations.newVisWizard.searchSelection.notFoundLabel": "未找到匹配的索引或已保存搜索。",
"visualizations.newVisWizard.searchSelection.savedObjectType.indexPattern": "索引模式",
"visualizations.newVisWizard.searchSelection.savedObjectType.search": "已保存搜索",
"visualizations.newVisWizard.selectVisType": "选择可视化类型",
"visualizations.newVisWizard.title": "新建可视化",
"visualizations.noResultsFoundTitle": "找不到结果",
"visualizations.savedObjectName": "可视化",
@ -10936,9 +10927,6 @@
"xpack.lens.sugegstion.refreshSuggestionLabel": "刷新",
"xpack.lens.suggestion.refreshSuggestionTooltip": "基于选定可视化刷新建议。",
"xpack.lens.suggestions.currentVisLabel": "当前",
"xpack.lens.visTypeAlias.description": "Lens 简化了基本可视化的创建",
"xpack.lens.visTypeAlias.promotion.buttonText": "前往 Lens",
"xpack.lens.visTypeAlias.promotion.description": "试用 Lens以全新直观的方式创建可视化。",
"xpack.lens.visTypeAlias.title": "Lens 可视化",
"xpack.lens.visTypeAlias.type": "Lens",
"xpack.lens.xyChart.addLayer": "添加图层",
@ -11699,8 +11687,6 @@
"xpack.maps.viewControl.latLabel": "纬度:",
"xpack.maps.viewControl.lonLabel": "经度:",
"xpack.maps.viewControl.zoomLabel": "缩放:",
"xpack.maps.visTypeAlias.description": "使用多个图层和索引创建地图并提供样式。",
"xpack.maps.visTypeAlias.legacyMapVizWarning": "使用 Maps 应用而非坐标地图和区域地图。\nMaps 应用提供更多的功能并更加易用。",
"xpack.maps.visTypeAlias.title": "Maps",
"xpack.maps.xyztmssource.attributionLink": "属性文本必须附带链接",
"xpack.maps.xyztmssource.attributionLinkLabel": "属性链接",

View file

@ -65,6 +65,7 @@ require('@kbn/test').runTestsCli([
require.resolve('../test/security_solution_endpoint_api_int/config.ts'),
require.resolve('../test/ingest_manager_api_integration/config.ts'),
require.resolve('../test/functional_enterprise_search/without_host_configured.config.ts'),
require.resolve('../test/functional_vis_wizard/config.ts'),
require.resolve('../test/saved_object_tagging/functional/config.ts'),
require.resolve('../test/saved_object_tagging/api_integration/security_and_spaces/config.ts'),
require.resolve('../test/saved_object_tagging/api_integration/tagging_api/config.ts'),

Some files were not shown because too many files have changed in this diff Show more