[7.x] Stacked headers and navigational search (#72331) (#77407)

Co-authored-by: Poff Poffenberger <poffdeluxe@gmail.com>
Co-authored-by: Ryan Keairns <contactryank@gmail.com>
Co-authored-by: pgayvallet <pierre.gayvallet@elastic.co>
Co-authored-by: cchaos <caroline.horn@elastic.co>
This commit is contained in:
Michail Yasonik 2020-09-15 12:37:16 -04:00 committed by GitHub
parent 71e52d2e1d
commit 1868e6ba9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
120 changed files with 3839 additions and 11261 deletions

304
.github/CODEOWNERS vendored Normal file
View file

@ -0,0 +1,304 @@
# 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/
# App
/x-pack/plugins/dashboard_enhanced/ @elastic/kibana-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/dashboard/ @elastic/kibana-app
/src/plugins/discover/ @elastic/kibana-app
/src/plugins/input_control_vis/ @elastic/kibana-app
/src/plugins/kibana_legacy/ @elastic/kibana-app
/src/plugins/vis_default_editor/ @elastic/kibana-app
/src/plugins/vis_type_markdown/ @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
# 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/advanced_settings/ @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-canvas
/src/plugins/kibana_utils/ @elastic/kibana-app-arch
/src/plugins/management/ @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
/src/plugins/visualizations/ @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
# APM
/x-pack/plugins/apm/ @elastic/apm-ui
/x-pack/test/functional/apps/apm/ @elastic/apm-ui
/src/legacy/core_plugins/apm_oss/ @elastic/apm-ui
/src/plugins/apm_oss/ @elastic/apm-ui
/src/apm.js @watson @vigneshshanmugam
# Client Side Monitoring (lives in APM directories but owned by Uptime)
/x-pack/plugins/apm/e2e/cypress/support/step_definitions/rum @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
# Beats
/x-pack/legacy/plugins/beats_management/ @elastic/beats
# Canvas
/x-pack/plugins/canvas/ @elastic/kibana-canvas
/x-pack/test/functional/apps/canvas/ @elastic/kibana-canvas
# 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
# Exclude tutorial resources folder for now because they are not owned by Kibana app and most will move out soon
/src/legacy/core_plugins/kibana/public/home/*.ts @elastic/kibana-core-ui
/src/legacy/core_plugins/kibana/public/home/np_ready/ @elastic/kibana-core-ui
/x-pack/plugins/global_search_bar/ @elastic/kibana-core-ui
# Observability UIs
/x-pack/legacy/plugins/infra/ @elastic/logs-metrics-ui
/x-pack/plugins/infra/ @elastic/logs-metrics-ui
/x-pack/plugins/ingest_manager/ @elastic/ingest-management
/x-pack/legacy/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/legacy/plugins/ml/ @elastic/ml-ui
/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/legacy/plugins/maps/ @elastic/kibana-gis
/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
# 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
/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
# 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
/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/test/saved_objects_field_count/ @elastic/kibana-platform
/packages/kbn-config-schema/ @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
# Security
/src/core/server/csp/ @elastic/kibana-security @elastic/kibana-platform
/x-pack/legacy/plugins/security/ @elastic/kibana-security
/x-pack/legacy/plugins/spaces/ @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/login_selector_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/saml_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
# 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
# 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
# Enterprise Search
# Shared
/x-pack/plugins/enterprise_search/ @elastic/enterprise-search-frontend
/x-pack/test/functional_enterprise_search/ @elastic/enterprise-search-frontend
# App Search
/x-pack/plugins/enterprise_search/public/applications/app_search @elastic/app-search-frontend
/x-pack/plugins/enterprise_search/server/routes/app_search @elastic/app-search-frontend
/x-pack/plugins/enterprise_search/server/collectors/app_search @elastic/app-search-frontend
/x-pack/plugins/enterprise_search/server/saved_objects/app_search @elastic/app-search-frontend
# Workplace Search
/x-pack/plugins/enterprise_search/public/applications/workplace_search @elastic/workplace-search-frontend
/x-pack/plugins/enterprise_search/server/routes/workplace_search @elastic/workplace-search-frontend
/x-pack/plugins/enterprise_search/server/collectors/workplace_search @elastic/workplace-search-frontend
/x-pack/plugins/enterprise_search/server/saved_objects/workplace_search @elastic/workplace-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/legacy/plugins/cross_cluster_replication/ @elastic/es-ui
/x-pack/plugins/index_lifecycle_management/ @elastic/es-ui
/x-pack/legacy/plugins/index_management/ @elastic/es-ui
/x-pack/legacy/plugins/license_management/ @elastic/es-ui
/x-pack/legacy/plugins/rollup/ @elastic/es-ui
/x-pack/legacy/plugins/snapshot_restore/ @elastic/es-ui
/x-pack/legacy/plugins/upgrade_assistant/ @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
# 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
# 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
# Core design
/src/plugins/dashboard/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/plugins/canvas/**/*.scss @elastic/kibana-core-ui-designers
/src/legacy/core_plugins/kibana/public/home/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/legacy/plugins/security/**/*.scss @elastic/kibana-core-ui-designers
/x-pack/legacy/plugins/spaces/**/*.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
# 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

View file

@ -0,0 +1,598 @@
////
NOTE:
This is an automatically generated file. Please do not edit directly. Instead, run the
following from within the kibana repository:
node scripts/build_plugin_list_docs
You can update the template within packages/kbn-dev-utils/target/plugin_list/generate_plugin_list.js
////
[[code-exploration]]
== Exploring Kibana code
The goals of our folder heirarchy are:
- Easy for developers to know where to add new services, plugins and applications.
- Easy for developers to know where to find the code from services, plugins and applications.
- Easy to browse and understand our folder structure.
To that aim, we strive to:
- Avoid too many files in any given folder.
- Choose clear, unambigious folder names.
- Organize by domain.
- Every folder should contain a README that describes the contents of that folder.
[discrete]
[[kibana-services-applications]]
=== Services and Applications
[discrete]
==== src/plugins
- {kib-repo}blob/{branch}/src/plugins/advanced_settings[advancedSettings]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/apm_oss[apmOss]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/bfetch/README.md[bfetch]
bfetch allows to batch HTTP requests and streams responses back.
- {kib-repo}blob/{branch}/src/plugins/charts/README.md[charts]
The Charts plugin is a way to create easier integration of shared colors, themes, types and other utilities across all Kibana charts and visualizations.
- {kib-repo}blob/{branch}/src/plugins/console[console]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/dashboard/README.md[dashboard]
Contains the dashboard application.
- {kib-repo}blob/{branch}/src/plugins/data/README.md[data]
data plugin provides common data access services.
- {kib-repo}blob/{branch}/src/plugins/dev_tools/README.md[devTools]
The ui/registry/dev_tools is removed in favor of the devTools plugin which exposes a register method in the setup contract.
Registering app works mostly the same as registering apps in core.application.register.
Routing will be handled by the id of the dev tool - your dev tool will be mounted when the URL matches /app/dev_tools#/<YOUR ID>.
This API doesn't support angular, for registering angular dev tools, bootstrap a local module on mount into the given HTML element.
- {kib-repo}blob/{branch}/src/plugins/discover/README.md[discover]
Contains the Discover application and the saved search embeddable.
- {kib-repo}blob/{branch}/src/plugins/embeddable/README.md[embeddable]
Embeddables are re-usable widgets that can be rendered in any environment or plugin. Developers can embed them directly in their plugin. End users can dynamically add them to any embeddable containers.
- {kib-repo}blob/{branch}/src/plugins/es_ui_shared/README.md[esUiShared]
This plugin contains reusable code in the form of self-contained modules (or libraries). Each of these modules exports a set of functionality relevant to the domain of the module.
- {kib-repo}blob/{branch}/src/plugins/expressions/README.md[expressions]
This plugin provides methods which will parse & execute an expression pipeline
string for you, as well as a series of registries for advanced users who might
want to incorporate their own functions, types, and renderers into the service
for use in their own application.
- {kib-repo}blob/{branch}/src/plugins/home/README.md[home]
Moves the legacy ui/registry/feature_catalogue module for registering "features" that should be shown in the home page's feature catalogue to a service within a "home" plugin. The feature catalogue refered to here should not be confused with the "feature" plugin for registering features used to derive UI capabilities for feature controls.
- {kib-repo}blob/{branch}/src/plugins/index_pattern_management[indexPatternManagement]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/input_control_vis/README.md[inputControlVis]
Contains the input control visualization allowing to place custom filter controls on a dashboard.
- {kib-repo}blob/{branch}/src/plugins/inspector/README.md[inspector]
The inspector is a contextual tool to gain insights into different elements
in Kibana, e.g. visualizations. It has the form of a flyout panel.
- {kib-repo}blob/{branch}/src/plugins/kibana_legacy/README.md[kibanaLegacy]
This plugin will contain several helpers and services to integrate pieces of the legacy Kibana app with the new Kibana platform.
- {kib-repo}blob/{branch}/src/plugins/kibana_react/README.md[kibanaReact]
Tools for building React applications in Kibana.
- {kib-repo}blob/{branch}/src/plugins/kibana_usage_collection/README.md[kibanaUsageCollection]
This plugin registers the basic usage collectors from Kibana:
- {kib-repo}blob/{branch}/src/plugins/kibana_utils/README.md[kibanaUtils]
Utilities for building Kibana plugins.
- {kib-repo}blob/{branch}/src/plugins/legacy_export[legacyExport]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/management[management]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/maps_legacy[mapsLegacy]
WARNING: Missing README.
- {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.
- {kib-repo}blob/{branch}/src/plugins/newsfeed[newsfeed]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/region_map[regionMap]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/saved_objects[savedObjects]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/saved_objects_management[savedObjectsManagement]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/share/README.md[share]
Replaces the legacy ui/share module for registering share context menus.
- {kib-repo}blob/{branch}/src/plugins/telemetry/README.md[telemetry]
Telemetry allows Kibana features to have usage tracked in the wild. The general term "telemetry" refers to multiple things:
- {kib-repo}blob/{branch}/src/plugins/telemetry_collection_manager/README.md[telemetryCollectionManager]
Telemetry's collection manager to go through all the telemetry sources when fetching it before reporting.
- {kib-repo}blob/{branch}/src/plugins/telemetry_management_section/README.md[telemetryManagementSection]
This plugin adds the Advanced Settings section for the Usage Data collection (aka Telemetry).
- {kib-repo}blob/{branch}/src/plugins/tile_map[tileMap]
WARNING: Missing README.
- {kib-repo}blob/{branch}/src/plugins/timelion/README.md[timelion]
Contains the deprecated timelion application. For the timelion visualization,
which also contains the timelion APIs and backend, look at the vis_type_timelion plugin.
- {kib-repo}blob/{branch}/src/plugins/ui_actions/README.md[uiActions]
An API for:
- {kib-repo}blob/{branch}/src/plugins/usage_collection/README.md[usageCollection]
Usage Collection allows collecting usage data for other services to consume (telemetry and monitoring).
To integrate with the telemetry services for usage collection of your feature, there are 2 steps:
- {kib-repo}blob/{branch}/src/plugins/vis_type_markdown/README.md[visTypeMarkdown]
The markdown visualization that can be used to place text panels on dashboards.
- {kib-repo}blob/{branch}/src/plugins/vis_type_metric/README.md[visTypeMetric]
Contains the metric visualization.
- {kib-repo}blob/{branch}/src/plugins/vis_type_table/README.md[visTypeTable]
Contains the data table visualization, that allows presenting data in a simple table format.
- {kib-repo}blob/{branch}/src/plugins/vis_type_tagcloud/README.md[visTypeTagcloud]
Contains the tagcloud visualization.
- {kib-repo}blob/{branch}/src/plugins/vis_type_timelion/README.md[visTypeTimelion]
Contains the timelion visualization and the timelion backend.
- {kib-repo}blob/{branch}/src/plugins/vis_type_timeseries/README.md[visTypeTimeseries]
Contains everything around TSVB (the editor, visualizatin implementations and backends).
- {kib-repo}blob/{branch}/src/plugins/vis_type_vega/README.md[visTypeVega]
Contains the Vega visualization.
- {kib-repo}blob/{branch}/src/plugins/vis_type_vislib/README.md[visTypeVislib]
Contains the vislib visualizations. These are the classical area/line/bar, pie, gauge/goal and
heatmap charts.
- {kib-repo}blob/{branch}/src/plugins/vis_type_xy/README.md[visTypeXy]
Contains the new xy-axis chart using the elastic-charts library, which will eventually
replace the vislib xy-axis (bar, area, line) charts.
- {kib-repo}blob/{branch}/src/plugins/visualizations/README.md[visualizations]
Contains most of the visualization infrastructure, e.g. the visualization type registry or the
visualization embeddable.
- {kib-repo}blob/{branch}/src/plugins/visualize/README.md[visualize]
Contains the visualize application which includes the listing page and the app frame,
which will load the visualization's editor.
[discrete]
==== x-pack/plugins
- {kib-repo}blob/{branch}/x-pack/plugins/actions/README.md[actions]
The Kibana actions plugin provides a framework to create executable actions. You can:
- {kib-repo}blob/{branch}/x-pack/plugins/alerting_builtins/README.md[alertingBuiltins]
This plugin provides alertTypes shipped with Kibana for use with the
the alerts plugin. When enabled, it will register
the built-in alertTypes with the alerting plugin, register associated HTTP
routes, etc.
- {kib-repo}blob/{branch}/x-pack/plugins/alerts/README.md[alerts]
The Kibana alerting plugin provides a common place to set up alerts. You can:
- {kib-repo}blob/{branch}/x-pack/plugins/apm/readme.md[apm]
To access an elasticsearch instance that has live data you have two options:
- {kib-repo}blob/{branch}/x-pack/plugins/audit_trail[auditTrail]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/beats_management/readme.md[beatsManagement]
Notes:
Failure to have auth enabled in Kibana will make for a broken UI. UI-based errors not yet in place
- {kib-repo}blob/{branch}/x-pack/plugins/canvas/README.md[canvas]
"Never look back. The past is done. The future is a blank canvas." ― Suzy Kassem, Rise Up and Salute the Sun
- {kib-repo}blob/{branch}/x-pack/plugins/case/README.md[case]
Experimental Feature
- {kib-repo}blob/{branch}/x-pack/plugins/cloud[cloud]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/code[code]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/console_extensions[consoleExtensions]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/cross_cluster_replication/README.md[crossClusterReplication]
You can run a local cluster and simulate a remote cluster within a single Kibana directory.
- {kib-repo}blob/{branch}/x-pack/plugins/dashboard_enhanced/README.md[dashboardEnhanced]
Contains the enhancements to the OSS dashboard app.
- {kib-repo}blob/{branch}/x-pack/plugins/dashboard_mode/README.md[dashboardMode]
The deprecated dashboard only mode.
- {kib-repo}blob/{branch}/x-pack/plugins/data_enhanced[dataEnhanced]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/discover_enhanced/README.md[discoverEnhanced]
Contains the enhancements to the OSS discover app.
- {kib-repo}blob/{branch}/x-pack/plugins/embeddable_enhanced[embeddableEnhanced]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/encrypted_saved_objects/README.md[encryptedSavedObjects]
The purpose of this plugin is to provide a way to encrypt/decrypt attributes on the custom Saved Objects that works with
security and spaces filtering as well as performing audit logging.
- {kib-repo}blob/{branch}/x-pack/plugins/enterprise_search/README.md[enterpriseSearch]
This plugin's goal is to provide a Kibana user interface to the Enterprise Search solution's products (App Search and Workplace Search). In it's current MVP state, the plugin provides the following with the goal of gathering user feedback and raising product awareness:
- {kib-repo}blob/{branch}/x-pack/plugins/event_log/README.md[eventLog]
The purpose of this plugin is to provide a way to persist a history of events
occuring in Kibana, initially just for the Make It Action project - alerts
and actions.
- {kib-repo}blob/{branch}/x-pack/plugins/features[features]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/file_upload[fileUpload]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/global_search/README.md[globalSearch]
The GlobalSearch plugin provides an easy way to search for various objects, such as applications
or dashboards from the Kibana instance, from both server and client-side plugins
- {kib-repo}blob/{branch}/x-pack/plugins/global_search_bar/README.md[globalSearchBar]
The GlobalSearchBar plugin provides a search interface for navigating Kibana. (It is the UI to the GlobalSearch plugin.)
- {kib-repo}blob/{branch}/x-pack/plugins/global_search_providers[globalSearchProviders]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/graph/README.md[graph]
This is the main source folder of the Graph plugin. It contains all of the Kibana server and client source code. x-pack/test/functional/apps/graph contains additional functional tests.
- {kib-repo}blob/{branch}/x-pack/plugins/grokdebugger/README.md[grokdebugger]
- {kib-repo}blob/{branch}/x-pack/plugins/index_lifecycle_management/README.md[indexLifecycleManagement]
You can test that the Frozen badge, phase filtering, and lifecycle information is surfaced in
Index Management by running this series of requests in Console:
- {kib-repo}blob/{branch}/x-pack/plugins/index_management[indexManagement]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/infra/README.md[infra]
This is the home of the infra plugin, which aims to provide a solution for
the infrastructure monitoring use-case within Kibana.
- {kib-repo}blob/{branch}/x-pack/plugins/ingest_manager/README.md[ingestManager]
Fleet needs to have Elasticsearch API keys enabled, and also to have TLS enabled on kibana, (if you want to run Kibana without TLS you can provide the following config flag --xpack.ingestManager.fleet.tlsCheckDisabled=false)
- {kib-repo}blob/{branch}/x-pack/plugins/ingest_pipelines/README.md[ingestPipelines]
The ingest_pipelines plugin provides Kibana support for Elasticsearch's ingest nodes. Please refer to the Elasticsearch documentation for more details.
- {kib-repo}blob/{branch}/x-pack/plugins/lens/readme.md[lens]
Run all tests from the x-pack root directory
- {kib-repo}blob/{branch}/x-pack/plugins/license_management[licenseManagement]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/licensing/README.md[licensing]
The licensing plugin retrieves license data from Elasticsearch at regular configurable intervals.
- {kib-repo}blob/{branch}/x-pack/plugins/lists/README.md[lists]
README.md for developers working on the backend lists on how to get started
using the CURL scripts in the scripts folder.
- {kib-repo}blob/{branch}/x-pack/plugins/logstash[logstash]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/maps/README.md[maps]
Visualize geo data from Elasticsearch or 3rd party geo-services.
- {kib-repo}blob/{branch}/x-pack/plugins/maps_legacy_licensing/README.md[mapsLegacyLicensing]
This plugin provides access to the detailed tile map services from Elastic.
- {kib-repo}blob/{branch}/x-pack/plugins/ml[ml]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/monitoring[monitoring]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/observability/README.md[observability]
This plugin provides shared components and services for use across observability solutions, as well as the observability landing page UI.
- {kib-repo}blob/{branch}/x-pack/plugins/oss_telemetry[ossTelemetry]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/painless_lab[painlessLab]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/remote_clusters[remoteClusters]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/reporting/README.md[reporting]
An awesome Kibana reporting plugin
- {kib-repo}blob/{branch}/x-pack/plugins/rollup/README.md[rollup]
Welcome to the Kibana rollup plugin! This plugin provides Kibana support for Elasticsearch's rollup feature. Please refer to the Elasticsearch documentation to understand rollup indices and how to create rollup jobs.
- {kib-repo}blob/{branch}/x-pack/plugins/searchprofiler[searchprofiler]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/security/README.md[security]
See Configuring security in Kibana.
- {kib-repo}blob/{branch}/x-pack/plugins/security_solution/README.md[securitySolution]
Welcome to the Kibana Security Solution plugin! This README will go over getting started with development and testing.
- {kib-repo}blob/{branch}/x-pack/plugins/snapshot_restore/README.md[snapshotRestore]
or
- {kib-repo}blob/{branch}/x-pack/plugins/spaces[spaces]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/task_manager[taskManager]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/telemetry_collection_xpack/README.md[telemetryCollectionXpack]
Gathers all usage collection, retrieving them from both: OSS and X-Pack plugins.
- {kib-repo}blob/{branch}/x-pack/plugins/transform[transform]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/translations[translations]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/triggers_actions_ui/README.md[triggers_actions_ui]
The Kibana alerts and actions UI plugin provides a user interface for managing alerts and actions.
As a developer you can reuse and extend built-in alerts and actions UI functionality:
- {kib-repo}blob/{branch}/x-pack/plugins/ui_actions_enhanced/README.md[uiActionsEnhanced]
- {kib-repo}blob/{branch}/x-pack/plugins/upgrade_assistant[upgradeAssistant]
WARNING: Missing README.
- {kib-repo}blob/{branch}/x-pack/plugins/uptime/README.md[uptime]
The purpose of this plugin is to provide users of Heartbeat more visibility of what's happening
in their infrastructure.
- {kib-repo}blob/{branch}/x-pack/plugins/watcher/README.md[watcher]
This plugins adopts some conventions in addition to or in place of conventions in Kibana (at the time of the plugin's creation):

View file

@ -341,6 +341,10 @@ and actions.
or dashboards from the Kibana instance, from both server and client-side plugins
|{kib-repo}blob/{branch}/x-pack/plugins/global_search_bar/README.md[globalSearchBar]
|The GlobalSearchBar plugin provides a search interface for navigating Kibana. (It is the UI to the GlobalSearch plugin.)
|{kib-repo}blob/{branch}/x-pack/plugins/global_search_providers[globalSearchProviders]
|WARNING: Missing README.

View file

@ -30,6 +30,7 @@ chrome.navControls.registerLeft({
| Method | Description |
| --- | --- |
| [registerLeft(navControl)](./kibana-plugin-core-public.chromenavcontrols.registerleft.md) | Register a nav control to be presented on the left side of the chrome header. |
| [registerRight(navControl)](./kibana-plugin-core-public.chromenavcontrols.registerright.md) | Register a nav control to be presented on the right side of the chrome header. |
| [registerCenter(navControl)](./kibana-plugin-core-public.chromenavcontrols.registercenter.md) | Register a nav control to be presented on the top-center side of the chrome header. |
| [registerLeft(navControl)](./kibana-plugin-core-public.chromenavcontrols.registerleft.md) | Register a nav control to be presented on the bottom-left side of the chrome header. |
| [registerRight(navControl)](./kibana-plugin-core-public.chromenavcontrols.registerright.md) | Register a nav control to be presented on the top-right side of the chrome header. |

View file

@ -0,0 +1,24 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeNavControls](./kibana-plugin-core-public.chromenavcontrols.md) &gt; [registerCenter](./kibana-plugin-core-public.chromenavcontrols.registercenter.md)
## ChromeNavControls.registerCenter() method
Register a nav control to be presented on the top-center side of the chrome header.
<b>Signature:</b>
```typescript
registerCenter(navControl: ChromeNavControl): void;
```
## Parameters
| Parameter | Type | Description |
| --- | --- | --- |
| navControl | <code>ChromeNavControl</code> | |
<b>Returns:</b>
`void`

View file

@ -4,7 +4,7 @@
## ChromeNavControls.registerLeft() method
Register a nav control to be presented on the left side of the chrome header.
Register a nav control to be presented on the bottom-left side of the chrome header.
<b>Signature:</b>

View file

@ -4,7 +4,7 @@
## ChromeNavControls.registerRight() method
Register a nav control to be presented on the right side of the chrome header.
Register a nav control to be presented on the top-right side of the chrome header.
<b>Signature:</b>

View file

@ -1,17 +0,0 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [ChromeStart](./kibana-plugin-core-public.chromestart.md) &gt; [getNavType$](./kibana-plugin-core-public.chromestart.getnavtype_.md)
## ChromeStart.getNavType$() method
Get the navigation type TODO \#64541 Can delete
<b>Signature:</b>
```typescript
getNavType$(): Observable<NavType>;
```
<b>Returns:</b>
`Observable<NavType>`

View file

@ -59,7 +59,6 @@ core.chrome.setHelpExtension(elem => {
| [getHelpExtension$()](./kibana-plugin-core-public.chromestart.gethelpextension_.md) | Get an observable of the current custom help conttent |
| [getIsNavDrawerLocked$()](./kibana-plugin-core-public.chromestart.getisnavdrawerlocked_.md) | Get an observable of the current locked state of the nav drawer. |
| [getIsVisible$()](./kibana-plugin-core-public.chromestart.getisvisible_.md) | Get an observable of the current visibility state of the chrome. |
| [getNavType$()](./kibana-plugin-core-public.chromestart.getnavtype_.md) | Get the navigation type TODO \#64541 Can delete |
| [removeApplicationClass(className)](./kibana-plugin-core-public.chromestart.removeapplicationclass.md) | Remove a className added with <code>addApplicationClass()</code>. If className is unknown it is ignored. |
| [setAppTitle(appTitle)](./kibana-plugin-core-public.chromestart.setapptitle.md) | Sets the current app's title |
| [setBadge(badge)](./kibana-plugin-core-public.chromestart.setbadge.md) | Override the current badge |

View file

@ -262,7 +262,7 @@ export const schema = Joi.object()
// settings for the find service
layout: Joi.object()
.keys({
fixedHeaderHeight: Joi.number().default(50),
fixedHeaderHeight: Joi.number().default(100),
})
.default(),

View file

@ -0,0 +1,3 @@
@import '@elastic/eui/src/global_styling/variables/header';
$kbnHeaderOffset: $euiHeaderHeightCompensation * 2;

View file

@ -94,8 +94,10 @@ export const AppContainer: FunctionComponent<Props> = ({
// eslint-disable-next-line no-console
console.error(e);
} finally {
setShowSpinner(false);
setIsMounting(false);
if (elementRef.current) {
setShowSpinner(false);
setIsMounting(false);
}
}
};

View file

@ -17,14 +17,7 @@
* under the License.
*/
import { BehaviorSubject } from 'rxjs';
import {
ChromeBadge,
ChromeBrand,
ChromeBreadcrumb,
ChromeService,
InternalChromeStart,
NavType,
} from './';
import { ChromeBadge, ChromeBrand, ChromeBreadcrumb, ChromeService, InternalChromeStart } from './';
const createStartContractMock = () => {
const startContract: DeeplyMockedKeys<InternalChromeStart> = {
@ -50,8 +43,10 @@ const createStartContractMock = () => {
},
navControls: {
registerLeft: jest.fn(),
registerCenter: jest.fn(),
registerRight: jest.fn(),
getLeft$: jest.fn(),
getCenter$: jest.fn(),
getRight$: jest.fn(),
},
setAppTitle: jest.fn(),
@ -70,7 +65,6 @@ const createStartContractMock = () => {
setHelpExtension: jest.fn(),
setHelpSupportUrl: jest.fn(),
getIsNavDrawerLocked$: jest.fn(),
getNavType$: jest.fn(),
getCustomNavLink$: jest.fn(),
setCustomNavLink: jest.fn(),
};
@ -83,7 +77,6 @@ const createStartContractMock = () => {
startContract.getCustomNavLink$.mockReturnValue(new BehaviorSubject(undefined));
startContract.getHelpExtension$.mockReturnValue(new BehaviorSubject(undefined));
startContract.getIsNavDrawerLocked$.mockReturnValue(new BehaviorSubject(false));
startContract.getNavType$.mockReturnValue(new BehaviorSubject('modern' as NavType));
return startContract;
};

View file

@ -37,7 +37,6 @@ import { ChromeNavControls, NavControlsService } from './nav_controls';
import { ChromeNavLinks, NavLinksService, ChromeNavLink } from './nav_links';
import { ChromeRecentlyAccessed, RecentlyAccessedService } from './recently_accessed';
import { Header } from './ui';
import { NavType } from './ui/header';
import { ChromeHelpExtensionMenuLink } from './ui/header/header_help_menu';
export { ChromeNavControls, ChromeRecentlyAccessed, ChromeDocTitle };
@ -172,10 +171,6 @@ export class ChromeService {
const getIsNavDrawerLocked$ = isNavDrawerLocked$.pipe(takeUntil(this.stop$));
// TODO #64541
// Can delete
const getNavType$ = uiSettings.get$('pageNavigation').pipe(takeUntil(this.stop$));
const isIE = () => {
const ua = window.navigator.userAgent;
const msie = ua.indexOf('MSIE '); // IE 10 or older
@ -241,10 +236,10 @@ export class ChromeService {
navLinks$={navLinks.getNavLinks$()}
recentlyAccessed$={recentlyAccessed.get$()}
navControlsLeft$={navControls.getLeft$()}
navControlsCenter$={navControls.getCenter$()}
navControlsRight$={navControls.getRight$()}
onIsLockedUpdate={setIsNavDrawerLocked}
isLocked$={getIsNavDrawerLocked$}
navType$={getNavType$}
/>
),
@ -305,8 +300,6 @@ export class ChromeService {
getIsNavDrawerLocked$: () => getIsNavDrawerLocked$,
getNavType$: () => getNavType$,
getCustomNavLink$: () => customNavLink$.pipe(takeUntil(this.stop$)),
setCustomNavLink: (customNavLink?: ChromeNavLink) => {
@ -468,13 +461,6 @@ export interface ChromeStart {
* Get an observable of the current locked state of the nav drawer.
*/
getIsNavDrawerLocked$(): Observable<boolean>;
/**
* Get the navigation type
* TODO #64541
* Can delete
*/
getNavType$(): Observable<NavType>;
}
/** @internal */

View file

@ -45,14 +45,18 @@ export interface ChromeNavControl {
* @public
*/
export interface ChromeNavControls {
/** Register a nav control to be presented on the left side of the chrome header. */
/** Register a nav control to be presented on the bottom-left side of the chrome header. */
registerLeft(navControl: ChromeNavControl): void;
/** Register a nav control to be presented on the right side of the chrome header. */
/** Register a nav control to be presented on the top-right side of the chrome header. */
registerRight(navControl: ChromeNavControl): void;
/** Register a nav control to be presented on the top-center side of the chrome header. */
registerCenter(navControl: ChromeNavControl): void;
/** @internal */
getLeft$(): Observable<ChromeNavControl[]>;
/** @internal */
getRight$(): Observable<ChromeNavControl[]>;
/** @internal */
getCenter$(): Observable<ChromeNavControl[]>;
}
/** @internal */
@ -62,6 +66,7 @@ export class NavControlsService {
public start() {
const navControlsLeft$ = new BehaviorSubject<ReadonlySet<ChromeNavControl>>(new Set());
const navControlsRight$ = new BehaviorSubject<ReadonlySet<ChromeNavControl>>(new Set());
const navControlsCenter$ = new BehaviorSubject<ReadonlySet<ChromeNavControl>>(new Set());
return {
// In the future, registration should be moved to the setup phase. This
@ -72,6 +77,9 @@ export class NavControlsService {
registerRight: (navControl: ChromeNavControl) =>
navControlsRight$.next(new Set([...navControlsRight$.value.values(), navControl])),
registerCenter: (navControl: ChromeNavControl) =>
navControlsCenter$.next(new Set([...navControlsCenter$.value.values(), navControl])),
getLeft$: () =>
navControlsLeft$.pipe(
map((controls) => sortBy([...controls.values()], 'order')),
@ -82,6 +90,11 @@ export class NavControlsService {
map((controls) => sortBy([...controls.values()], 'order')),
takeUntil(this.stop$)
),
getCenter$: () =>
navControlsCenter$.pipe(
map((controls) => sortBy([...controls.values()], 'order')),
takeUntil(this.stop$)
),
};
}

View file

@ -121,7 +121,7 @@ exports[`CollapsibleNav renders links grouped by category 1`] = `
homeHref="/"
id="collapsibe-nav"
isLocked={false}
isOpen={true}
isNavOpen={true}
navLinks$={
BehaviorSubject {
"_isScalar": false,
@ -2105,7 +2105,7 @@ exports[`CollapsibleNav renders the default nav 1`] = `
homeHref="/"
id="collapsibe-nav"
isLocked={false}
isOpen={false}
isNavOpen={false}
navLinks$={
BehaviorSubject {
"_isScalar": false,
@ -2339,6 +2339,7 @@ exports[`CollapsibleNav renders the default nav 2`] = `
homeHref="/"
id="collapsibe-nav"
isLocked={false}
isNavOpen={false}
isOpen={true}
navLinks$={
BehaviorSubject {
@ -2454,461 +2455,9 @@ exports[`CollapsibleNav renders the default nav 2`] = `
data-test-subj="collapsibleNav"
id="collapsibe-nav"
isDocked={false}
isOpen={true}
isOpen={false}
onClose={[Function]}
>
<EuiWindowEvent
event="keydown"
handler={[Function]}
/>
<EuiOverlayMask
headerZindexLocation="below"
onClick={[Function]}
/>
<EuiFocusTrap
clickOutsideDisables={true}
disabled={false}
>
<div
data-eui="EuiFocusTrap"
>
<nav
aria-label="Primary"
className="euiCollapsibleNav"
data-test-subj="collapsibleNav"
id="collapsibe-nav"
>
<EuiFlexItem
grow={false}
style={
Object {
"flexShrink": 0,
}
}
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
style={
Object {
"flexShrink": 0,
}
}
>
<EuiCollapsibleNavGroup
background="light"
className="eui-yScroll"
style={
Object {
"maxHeight": "40vh",
}
}
>
<div
className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light eui-yScroll"
id="mockId"
style={
Object {
"maxHeight": "40vh",
}
}
>
<div
className="euiCollapsibleNavGroup__children"
>
<EuiListGroup
aria-label="Pinned links"
color="text"
gutterSize="none"
listItems={
Array [
Object {
"href": "/",
"iconType": "home",
"label": "Home",
"onClick": [Function],
},
]
}
maxWidth="none"
size="s"
>
<ul
aria-label="Pinned links"
className="euiListGroup"
style={
Object {
"maxWidth": "none",
}
}
>
<EuiListGroupItem
color="text"
href="/"
iconType="home"
key="title-0"
label="Home"
onClick={[Function]}
showToolTip={false}
size="s"
wrapText={false}
>
<li
className="euiListGroupItem euiListGroupItem--small euiListGroupItem--text euiListGroupItem-isClickable"
>
<a
className="euiListGroupItem__button"
href="/"
onClick={[Function]}
rel="noreferrer"
>
<EuiIcon
className="euiListGroupItem__icon"
type="home"
>
<div
className="euiListGroupItem__icon"
data-euiicon-type="home"
/>
</EuiIcon>
<span
className="euiListGroupItem__label"
title="Home"
>
Home
</span>
</a>
</li>
</EuiListGroupItem>
</ul>
</EuiListGroup>
</div>
</div>
</EuiCollapsibleNavGroup>
</div>
</EuiFlexItem>
<EuiCollapsibleNavGroup
background="light"
data-test-subj="collapsibleNavGroup-recentlyViewed"
initialIsOpen={true}
isCollapsible={true}
key="recentlyViewed"
onToggle={[Function]}
title="Recently viewed"
>
<EuiAccordion
arrowDisplay="right"
buttonClassName="euiCollapsibleNavGroup__heading"
buttonContent={
<EuiFlexGroup
alignItems="center"
gutterSize="m"
responsive={false}
>
<EuiFlexItem>
<EuiTitle
size="xxs"
>
<h3
className="euiCollapsibleNavGroup__title"
id="mockId__title"
>
Recently viewed
</h3>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
}
className="euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading"
data-test-subj="collapsibleNavGroup-recentlyViewed"
id="mockId"
initialIsOpen={true}
isLoading={false}
isLoadingMessage={false}
onToggle={[Function]}
paddingSize="none"
>
<div
className="euiAccordion euiAccordion-isOpen euiCollapsibleNavGroup euiCollapsibleNavGroup--light euiCollapsibleNavGroup--withHeading"
data-test-subj="collapsibleNavGroup-recentlyViewed"
onToggle={[Function]}
>
<div
className="euiAccordion__triggerWrapper"
>
<button
aria-controls="mockId"
aria-expanded={true}
className="euiAccordion__button euiAccordion__buttonReverse euiCollapsibleNavGroup__heading"
onClick={[Function]}
type="button"
>
<span
className="euiAccordion__iconWrapper"
>
<EuiIcon
className="euiAccordion__icon euiAccordion__icon-isOpen"
size="m"
type="arrowRight"
>
<div
className="euiAccordion__icon euiAccordion__icon-isOpen"
data-euiicon-type="arrowRight"
size="m"
/>
</EuiIcon>
</span>
<span
className="euiIEFlexWrapFix"
>
<EuiFlexGroup
alignItems="center"
gutterSize="m"
responsive={false}
>
<div
className="euiFlexGroup euiFlexGroup--gutterMedium euiFlexGroup--alignItemsCenter euiFlexGroup--directionRow"
>
<EuiFlexItem>
<div
className="euiFlexItem"
>
<EuiTitle
size="xxs"
>
<h3
className="euiTitle euiTitle--xxsmall euiCollapsibleNavGroup__title"
id="mockId__title"
>
Recently viewed
</h3>
</EuiTitle>
</div>
</EuiFlexItem>
</div>
</EuiFlexGroup>
</span>
</button>
</div>
<div
className="euiAccordion__childWrapper"
id="mockId"
>
<EuiResizeObserver
onResize={[Function]}
>
<div>
<div
className=""
>
<div
className="euiCollapsibleNavGroup__children"
>
<EuiText
color="subdued"
size="s"
style={
Object {
"padding": "0 8px 8px",
}
}
>
<div
className="euiText euiText--small"
style={
Object {
"padding": "0 8px 8px",
}
}
>
<EuiTextColor
color="subdued"
component="div"
>
<div
className="euiTextColor euiTextColor--subdued"
>
<p>
No recently viewed items
</p>
</div>
</EuiTextColor>
</div>
</EuiText>
</div>
</div>
</div>
</EuiResizeObserver>
</div>
</div>
</EuiAccordion>
</EuiCollapsibleNavGroup>
<EuiHorizontalRule
margin="none"
>
<hr
className="euiHorizontalRule euiHorizontalRule--full"
/>
</EuiHorizontalRule>
<EuiFlexItem
className="eui-yScroll"
>
<div
className="euiFlexItem eui-yScroll"
>
<EuiShowFor
sizes={
Array [
"l",
"xl",
]
}
>
<EuiCollapsibleNavGroup
className="eui-showFor--l eui-showFor--xl"
key=".0"
>
<div
className="euiCollapsibleNavGroup eui-showFor--l eui-showFor--xl"
id="mockId"
>
<div
className="euiCollapsibleNavGroup__children"
>
<EuiListGroup
flush={true}
>
<ul
className="euiListGroup euiListGroup-flush euiListGroup--gutterSmall euiListGroup-maxWidthDefault"
>
<EuiListGroupItem
aria-label="Dock primary navigation"
buttonRef={
Object {
"current": <button
aria-label="Dock primary navigation"
class="euiListGroupItem__button"
data-test-subj="collapsible-nav-lock"
type="button"
>
<div
class="euiListGroupItem__icon"
data-euiicon-type="lockOpen"
/>
<span
class="euiListGroupItem__label"
title="Dock navigation"
>
Dock navigation
</span>
</button>,
}
}
color="subdued"
data-test-subj="collapsible-nav-lock"
iconType="lockOpen"
label="Dock navigation"
onClick={[Function]}
size="xs"
>
<li
className="euiListGroupItem euiListGroupItem--xSmall euiListGroupItem--subdued euiListGroupItem-isClickable"
>
<button
aria-label="Dock primary navigation"
className="euiListGroupItem__button"
data-test-subj="collapsible-nav-lock"
disabled={false}
onClick={[Function]}
type="button"
>
<EuiIcon
className="euiListGroupItem__icon"
type="lockOpen"
>
<div
className="euiListGroupItem__icon"
data-euiicon-type="lockOpen"
/>
</EuiIcon>
<span
className="euiListGroupItem__label"
title="Dock navigation"
>
Dock navigation
</span>
</button>
</li>
</EuiListGroupItem>
</ul>
</EuiListGroup>
</div>
</div>
</EuiCollapsibleNavGroup>
</EuiShowFor>
</div>
</EuiFlexItem>
<EuiScreenReaderOnly
showOnFocus={true}
>
<EuiButtonEmpty
className="euiScreenReaderOnly--showOnFocus euiCollapsibleNav__closeButton"
iconType="cross"
onClick={[Function]}
size="xs"
textProps={
Object {
"className": "euiCollapsibleNav__closeButtonText",
}
}
>
<button
className="euiButtonEmpty euiButtonEmpty--primary euiButtonEmpty--xSmall euiScreenReaderOnly--showOnFocus euiCollapsibleNav__closeButton"
onClick={[Function]}
type="button"
>
<EuiButtonContent
className="euiButtonEmpty__content"
iconSide="left"
iconType="cross"
textProps={
Object {
"className": "euiButtonEmpty__text euiCollapsibleNav__closeButtonText",
}
}
>
<span
className="euiButtonContent euiButtonEmpty__content"
>
<EuiIcon
className="euiButtonContent__icon"
size="m"
type="cross"
>
<div
className="euiButtonContent__icon"
data-euiicon-type="cross"
size="m"
/>
</EuiIcon>
<span
className="euiButtonEmpty__text euiCollapsibleNav__closeButtonText"
>
<EuiI18n
default="close"
token="euiCollapsibleNav.closeButtonLabel"
>
close
</EuiI18n>
</span>
</span>
</EuiButtonContent>
</button>
</EuiButtonEmpty>
</EuiScreenReaderOnly>
</nav>
</div>
</EuiFocusTrap>
</EuiCollapsibleNav>
/>
</CollapsibleNav>
`;
@ -3025,6 +2574,7 @@ exports[`CollapsibleNav renders the default nav 3`] = `
homeHref="/"
id="collapsibe-nav"
isLocked={true}
isNavOpen={false}
isOpen={true}
navLinks$={
BehaviorSubject {
@ -3140,7 +2690,7 @@ exports[`CollapsibleNav renders the default nav 3`] = `
data-test-subj="collapsibleNav"
id="collapsibe-nav"
isDocked={true}
isOpen={true}
isOpen={false}
onClose={[Function]}
>
<EuiWindowEvent

File diff suppressed because it is too large Load diff

View file

@ -1,14 +1,5 @@
@include euiHeaderAffordForFixed;
// TODO #64541
// Delete this block
.chrHeaderWrapper:not(.headerWrapper) {
width: 100%;
position: fixed;
top: 0;
z-index: 10;
}
.chrHeaderHelpMenu__version {
text-transform: none;
}

View file

@ -59,7 +59,7 @@ function mockProps() {
basePath: httpServiceMock.createSetupContract({ basePath: '/test' }).basePath,
id: 'collapsibe-nav',
isLocked: false,
isOpen: false,
isNavOpen: false,
homeHref: '/',
navLinks$: new BehaviorSubject([]),
recentlyAccessed$: new BehaviorSubject([]),
@ -123,7 +123,7 @@ describe('CollapsibleNav', () => {
const component = mount(
<CollapsibleNav
{...mockProps()}
isOpen={true}
isNavOpen={true}
navLinks$={new BehaviorSubject(navLinks)}
recentlyAccessed$={new BehaviorSubject(recentNavLinks)}
customNavLink$={new BehaviorSubject(customNavLink)}
@ -138,7 +138,7 @@ describe('CollapsibleNav', () => {
const component = mount(
<CollapsibleNav
{...mockProps()}
isOpen={true}
isNavOpen={true}
navLinks$={new BehaviorSubject(navLinks)}
recentlyAccessed$={new BehaviorSubject(recentNavLinks)}
/>
@ -147,9 +147,9 @@ describe('CollapsibleNav', () => {
clickGroup(component, 'kibana');
clickGroup(component, 'recentlyViewed');
expectShownNavLinksCount(component, 1);
component.setProps({ isOpen: false });
component.setProps({ isNavOpen: false });
expectNavIsClosed(component);
component.setProps({ isOpen: true });
component.setProps({ isNavOpen: true });
expectShownNavLinksCount(component, 1);
});
@ -160,14 +160,14 @@ describe('CollapsibleNav', () => {
const component = mount(
<CollapsibleNav
{...mockProps()}
isOpen={true}
isNavOpen={true}
navLinks$={new BehaviorSubject(navLinks)}
recentlyAccessed$={new BehaviorSubject(recentNavLinks)}
/>
);
component.setProps({
closeNav: () => {
component.setProps({ isOpen: false });
component.setProps({ isNavOpen: false });
onClose();
},
});
@ -175,11 +175,11 @@ describe('CollapsibleNav', () => {
component.find('[data-test-subj="collapsibleNavGroup-recentlyViewed"] a').simulate('click');
expect(onClose.callCount).toEqual(1);
expectNavIsClosed(component);
component.setProps({ isOpen: true });
component.setProps({ isNavOpen: true });
component.find('[data-test-subj="collapsibleNavGroup-kibana"] a').simulate('click');
expect(onClose.callCount).toEqual(2);
expectNavIsClosed(component);
component.setProps({ isOpen: true });
component.setProps({ isNavOpen: true });
component.find('[data-test-subj="collapsibleNavGroup-noCategory"] a').simulate('click');
expect(onClose.callCount).toEqual(3);
expectNavIsClosed(component);

View file

@ -79,7 +79,7 @@ interface Props {
basePath: HttpStart['basePath'];
id: string;
isLocked: boolean;
isOpen: boolean;
isNavOpen: boolean;
homeHref: string;
navLinks$: Rx.Observable<ChromeNavLink[]>;
recentlyAccessed$: Rx.Observable<ChromeRecentlyAccessedHistoryItem[]>;
@ -94,7 +94,7 @@ export function CollapsibleNav({
basePath,
id,
isLocked,
isOpen,
isNavOpen,
homeHref,
storage = window.localStorage,
onIsLockedUpdate,
@ -129,7 +129,7 @@ export function CollapsibleNav({
aria-label={i18n.translate('core.ui.primaryNav.screenReaderLabel', {
defaultMessage: 'Primary',
})}
isOpen={isOpen}
isOpen={isNavOpen}
isDocked={isLocked}
onClose={closeNav}
>

View file

@ -21,7 +21,6 @@ import React from 'react';
import { act } from 'react-dom/test-utils';
import { BehaviorSubject } from 'rxjs';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import { NavType } from '.';
import { httpServiceMock } from '../../../http/http_service.mock';
import { applicationServiceMock } from '../../../mocks';
import { Header } from './header';
@ -51,10 +50,10 @@ function mockProps() {
helpExtension$: new BehaviorSubject(undefined),
helpSupportUrl$: new BehaviorSubject(''),
navControlsLeft$: new BehaviorSubject([]),
navControlsCenter$: new BehaviorSubject([]),
navControlsRight$: new BehaviorSubject([]),
basePath: http.basePath,
isLocked$: new BehaviorSubject(false),
navType$: new BehaviorSubject('modern' as NavType),
loadingCount$: new BehaviorSubject(0),
onIsLockedUpdate: () => {},
};
@ -71,7 +70,6 @@ describe('Header', () => {
const isVisible$ = new BehaviorSubject(false);
const breadcrumbs$ = new BehaviorSubject([{ text: 'test' }]);
const isLocked$ = new BehaviorSubject(false);
const navType$ = new BehaviorSubject('modern' as NavType);
const navLinks$ = new BehaviorSubject([
{ id: 'kibana', title: 'kibana', baseUrl: '', href: '' },
]);
@ -92,22 +90,19 @@ describe('Header', () => {
navLinks$={navLinks$}
recentlyAccessed$={recentlyAccessed$}
isLocked$={isLocked$}
navType$={navType$}
customNavLink$={customNavLink$}
/>
);
expect(component).toMatchSnapshot();
expect(component.find('EuiHeader').exists()).toBeFalsy();
act(() => isVisible$.next(true));
component.update();
expect(component).toMatchSnapshot();
expect(component.find('EuiHeader').exists()).toBeTruthy();
expect(component.find('nav[aria-label="Primary"]').exists()).toBeFalsy();
act(() => isLocked$.next(true));
component.update();
expect(component).toMatchSnapshot();
act(() => navType$.next('legacy' as NavType));
component.update();
expect(component.find('nav[aria-label="Primary"]').exists()).toBeTruthy();
expect(component).toMatchSnapshot();
});
});

View file

@ -23,8 +23,6 @@ import {
EuiHeaderSectionItem,
EuiHeaderSectionItemButton,
EuiIcon,
EuiNavDrawer,
EuiShowFor,
htmlIdGenerator,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@ -43,14 +41,14 @@ import {
import { InternalApplicationStart } from '../../../application/types';
import { HttpStart } from '../../../http';
import { ChromeHelpExtension } from '../../chrome_service';
import { NavType, OnIsLockedUpdate } from './';
import { OnIsLockedUpdate } from './';
import { CollapsibleNav } from './collapsible_nav';
import { HeaderBadge } from './header_badge';
import { HeaderBreadcrumbs } from './header_breadcrumbs';
import { HeaderHelpMenu } from './header_help_menu';
import { HeaderLogo } from './header_logo';
import { HeaderNavControls } from './header_nav_controls';
import { NavDrawer } from './nav_drawer';
import { HeaderActionMenu } from './header_action_menu';
export interface HeaderProps {
kibanaVersion: string;
@ -68,27 +66,14 @@ export interface HeaderProps {
helpExtension$: Observable<ChromeHelpExtension | undefined>;
helpSupportUrl$: Observable<string>;
navControlsLeft$: Observable<readonly ChromeNavControl[]>;
navControlsCenter$: Observable<readonly ChromeNavControl[]>;
navControlsRight$: Observable<readonly ChromeNavControl[]>;
basePath: HttpStart['basePath'];
isLocked$: Observable<boolean>;
navType$: Observable<NavType>;
loadingCount$: ReturnType<HttpStart['getLoadingCount$']>;
onIsLockedUpdate: OnIsLockedUpdate;
}
function renderMenuTrigger(toggleOpen: () => void) {
return (
<EuiHeaderSectionItemButton
aria-label={i18n.translate('core.ui.chrome.headerGlobalNav.toggleSideNavAriaLabel', {
defaultMessage: 'Toggle side navigation',
})}
onClick={toggleOpen}
>
<EuiIcon type="apps" size="m" />
</EuiHeaderSectionItemButton>
);
}
export function Header({
kibanaVersion,
kibanaDocLink,
@ -98,125 +83,116 @@ export function Header({
homeHref,
...observables
}: HeaderProps) {
const isVisible = useObservable(observables.isVisible$, true);
const navType = useObservable(observables.navType$, 'modern');
const isVisible = useObservable(observables.isVisible$, false);
const isLocked = useObservable(observables.isLocked$, false);
const [isOpen, setIsOpen] = useState(false);
const [isNavOpen, setIsNavOpen] = useState(false);
if (!isVisible) {
return <LoadingIndicator loadingCount$={observables.loadingCount$} />;
}
const navDrawerRef = createRef<EuiNavDrawer>();
const toggleCollapsibleNavRef = createRef<HTMLButtonElement>();
const navId = htmlIdGenerator()();
const className = classnames(
'chrHeaderWrapper', // TODO #64541 - delete this
'hide-for-sharing',
{
'chrHeaderWrapper--navIsLocked': isLocked,
headerWrapper: navType === 'modern',
}
);
const className = classnames('hide-for-sharing', 'headerGlobalNav');
return (
<>
<LoadingIndicator loadingCount$={observables.loadingCount$} />
<header className={className} data-test-subj="headerGlobalNav">
<EuiHeader position="fixed" id="headerGlobalNav">
<EuiHeaderSection grow={false}>
{navType === 'modern' ? (
<div id="globalHeaderBars">
<EuiHeader
theme="dark"
position="fixed"
sections={[
{
items: [
<HeaderLogo
href={homeHref}
forceNavigation$={observables.forceAppSwitcherNavigation$}
navLinks$={observables.navLinks$}
navigateToApp={application.navigateToApp}
/>,
],
borders: 'none',
},
{
...(observables.navControlsCenter$ && {
items: [<HeaderNavControls navControls$={observables.navControlsCenter$} />],
}),
borders: 'none',
},
{
items: [
<HeaderHelpMenu
helpExtension$={observables.helpExtension$}
helpSupportUrl$={observables.helpSupportUrl$}
kibanaDocLink={kibanaDocLink}
kibanaVersion={kibanaVersion}
/>,
<HeaderNavControls navControls$={observables.navControlsRight$} />,
],
borders: 'none',
},
]}
/>
<EuiHeader position="fixed">
<EuiHeaderSection grow={false}>
<EuiHeaderSectionItem border="right" className="header__toggleNavButtonSection">
<EuiHeaderSectionItemButton
data-test-subj="toggleNavButton"
aria-label={i18n.translate('core.ui.primaryNav.toggleNavAriaLabel', {
defaultMessage: 'Toggle primary navigation',
})}
onClick={() => setIsOpen(!isOpen)}
aria-expanded={isOpen}
aria-pressed={isOpen}
onClick={() => setIsNavOpen(!isNavOpen)}
aria-expanded={isNavOpen}
aria-pressed={isNavOpen}
aria-controls={navId}
ref={toggleCollapsibleNavRef}
>
<EuiIcon type="menu" size="m" />
</EuiHeaderSectionItemButton>
</EuiHeaderSectionItem>
) : (
// TODO #64541
// Delete this block
<EuiShowFor sizes={['xs', 's']}>
<EuiHeaderSectionItem border="right">
{renderMenuTrigger(() => navDrawerRef.current?.toggleOpen())}
</EuiHeaderSectionItem>
</EuiShowFor>
)}
<EuiHeaderSectionItem border="right">
<HeaderLogo
href={homeHref}
forceNavigation$={observables.forceAppSwitcherNavigation$}
navLinks$={observables.navLinks$}
navigateToApp={application.navigateToApp}
/>
</EuiHeaderSectionItem>
<EuiHeaderSectionItem border="right" />
<HeaderNavControls side="left" navControls$={observables.navControlsLeft$} />
</EuiHeaderSection>
<HeaderNavControls side="left" navControls$={observables.navControlsLeft$} />
</EuiHeaderSection>
<HeaderBreadcrumbs
appTitle$={observables.appTitle$}
breadcrumbs$={observables.breadcrumbs$}
/>
<HeaderBreadcrumbs
appTitle$={observables.appTitle$}
breadcrumbs$={observables.breadcrumbs$}
/>
<HeaderBadge badge$={observables.badge$} />
<HeaderBadge badge$={observables.badge$} />
<EuiHeaderSection side="right">
<EuiHeaderSectionItem>
<HeaderHelpMenu
helpExtension$={observables.helpExtension$}
helpSupportUrl$={observables.helpSupportUrl$}
kibanaDocLink={kibanaDocLink}
kibanaVersion={kibanaVersion}
/>
</EuiHeaderSectionItem>
<EuiHeaderSection side="right">
<EuiHeaderSectionItem border="none">
<HeaderActionMenu actionMenu$={application.currentActionMenu$} />
</EuiHeaderSectionItem>
</EuiHeaderSection>
</EuiHeader>
</div>
<HeaderNavControls side="right" navControls$={observables.navControlsRight$} />
</EuiHeaderSection>
</EuiHeader>
{navType === 'modern' ? (
<CollapsibleNav
appId$={application.currentAppId$}
id={navId}
isLocked={isLocked}
navLinks$={observables.navLinks$}
recentlyAccessed$={observables.recentlyAccessed$}
isOpen={isOpen}
homeHref={homeHref}
basePath={basePath}
navigateToApp={application.navigateToApp}
onIsLockedUpdate={onIsLockedUpdate}
closeNav={() => {
setIsOpen(false);
if (toggleCollapsibleNavRef.current) {
toggleCollapsibleNavRef.current.focus();
}
}}
customNavLink$={observables.customNavLink$}
/>
) : (
// TODO #64541
// Delete this block
<NavDrawer
isLocked={isLocked}
onIsLockedUpdate={onIsLockedUpdate}
navLinks$={observables.navLinks$}
recentlyAccessed$={observables.recentlyAccessed$}
basePath={basePath}
appId$={application.currentAppId$}
navigateToApp={application.navigateToApp}
ref={navDrawerRef}
/>
)}
<CollapsibleNav
appId$={application.currentAppId$}
id={navId}
isLocked={isLocked}
navLinks$={observables.navLinks$}
recentlyAccessed$={observables.recentlyAccessed$}
isNavOpen={isNavOpen}
homeHref={homeHref}
basePath={basePath}
navigateToApp={application.navigateToApp}
onIsLockedUpdate={onIsLockedUpdate}
closeNav={() => {
setIsNavOpen(false);
if (toggleCollapsibleNavRef.current) {
toggleCollapsibleNavRef.current.focus();
}
}}
customNavLink$={observables.customNavLink$}
/>
</header>
</>
);

View file

@ -0,0 +1,139 @@
/*
* 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 { mount, ReactWrapper } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { BehaviorSubject } from 'rxjs';
import { HeaderActionMenu } from './header_action_menu';
import { MountPoint, UnmountCallback } from '../../../types';
type MockedUnmount = jest.MockedFunction<UnmountCallback>;
describe('HeaderActionMenu', () => {
let component: ReactWrapper;
let menuMount$: BehaviorSubject<MountPoint | undefined>;
let unmounts: Record<string, MockedUnmount>;
beforeEach(() => {
menuMount$ = new BehaviorSubject<MountPoint | undefined>(undefined);
unmounts = {};
});
const refresh = () => {
new Promise(async (resolve) => {
if (component) {
act(() => {
component.update();
});
}
setImmediate(() => resolve(component)); // flushes any pending promises
});
};
const createMountPoint = (id: string, content: string = id): MountPoint => (
root
): MockedUnmount => {
const container = document.createElement('DIV');
// eslint-disable-next-line no-unsanitized/property
container.innerHTML = content;
root.appendChild(container);
const unmount = jest.fn(() => container.remove());
unmounts[id] = unmount;
return unmount;
};
it('mounts the current value of the provided observable', async () => {
component = mount(<HeaderActionMenu actionMenu$={menuMount$} />);
act(() => {
menuMount$.next(createMountPoint('FOO'));
});
await refresh();
expect(component.html()).toMatchInlineSnapshot(
`"<div data-test-subj=\\"headerAppActionMenu\\"><div>FOO</div></div>"`
);
});
it('clears the content of the component when emitting undefined', async () => {
component = mount(<HeaderActionMenu actionMenu$={menuMount$} />);
act(() => {
menuMount$.next(createMountPoint('FOO'));
});
await refresh();
expect(component.html()).toMatchInlineSnapshot(
`"<div data-test-subj=\\"headerAppActionMenu\\"><div>FOO</div></div>"`
);
act(() => {
menuMount$.next(undefined);
});
await refresh();
expect(component.html()).toMatchInlineSnapshot(
`"<div data-test-subj=\\"headerAppActionMenu\\"></div>"`
);
});
it('updates the dom when a new mount point is emitted', async () => {
component = mount(<HeaderActionMenu actionMenu$={menuMount$} />);
act(() => {
menuMount$.next(createMountPoint('FOO'));
});
await refresh();
expect(component.html()).toMatchInlineSnapshot(
`"<div data-test-subj=\\"headerAppActionMenu\\"><div>FOO</div></div>"`
);
act(() => {
menuMount$.next(createMountPoint('BAR'));
});
await refresh();
expect(component.html()).toMatchInlineSnapshot(
`"<div data-test-subj=\\"headerAppActionMenu\\"><div>BAR</div></div>"`
);
});
it('calls the previous mount point `unmount` when mounting a new mount point', async () => {
component = mount(<HeaderActionMenu actionMenu$={menuMount$} />);
act(() => {
menuMount$.next(createMountPoint('FOO'));
});
await refresh();
expect(Object.keys(unmounts)).toEqual(['FOO']);
expect(unmounts.FOO).not.toHaveBeenCalled();
act(() => {
menuMount$.next(createMountPoint('BAR'));
});
await refresh();
expect(Object.keys(unmounts)).toEqual(['FOO', 'BAR']);
expect(unmounts.FOO).toHaveBeenCalledTimes(1);
expect(unmounts.BAR).not.toHaveBeenCalled();
});
});

View file

@ -0,0 +1,64 @@
/*
* 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, { FC, useRef, useLayoutEffect, useState } from 'react';
import { Observable } from 'rxjs';
import { MountPoint, UnmountCallback } from '../../../types';
interface HeaderActionMenuProps {
actionMenu$: Observable<MountPoint | undefined>;
}
export const HeaderActionMenu: FC<HeaderActionMenuProps> = ({ actionMenu$ }) => {
// useObservable relies on useState under the hood. The signature is type SetStateAction<S> = S | ((prevState: S) => S);
// As we got a Observable<Function> here, React's setState setter assume he's getting a `(prevState: S) => S` signature,
// therefore executing the mount method, causing everything to crash.
// piping the observable before calling `useObservable` causes the effect to always having a new reference, as
// the piped observable is a new instance on every render, causing infinite loops.
// this is why we use `useLayoutEffect` manually here.
const [mounter, setMounter] = useState<{ mount: MountPoint | undefined }>({ mount: undefined });
useLayoutEffect(() => {
const s = actionMenu$.subscribe((value) => {
setMounter({ mount: value });
});
return () => s.unsubscribe();
}, [actionMenu$]);
const elementRef = useRef<HTMLDivElement>(null);
const unmountRef = useRef<UnmountCallback | null>(null);
useLayoutEffect(() => {
if (unmountRef.current) {
unmountRef.current();
unmountRef.current = null;
}
if (mounter.mount && elementRef.current) {
try {
unmountRef.current = mounter.mount(elementRef.current);
} catch (e) {
// TODO: use client-side logger when feature is implemented
// eslint-disable-next-line no-console
console.error(e);
}
}
}, [mounter]);
return <div data-test-subj="headerAppActionMenu" ref={elementRef} />;
};

View file

@ -105,6 +105,8 @@ export function HeaderLogo({ href, navigateToApp, ...observables }: Props) {
aria-label={i18n.translate('core.ui.chrome.headerGlobalNav.goHomePageIconAriaLabel', {
defaultMessage: 'Go to home page',
})}
/>
>
Elastic
</EuiHeaderLogo>
);
}

View file

@ -26,7 +26,7 @@ import { HeaderExtension } from './header_extension';
interface Props {
navControls$: Observable<readonly ChromeNavControl[]>;
side: 'left' | 'right';
side?: 'left' | 'right';
}
export function HeaderNavControls({ navControls$, side }: Props) {
@ -41,7 +41,10 @@ export function HeaderNavControls({ navControls$, side }: Props) {
return (
<>
{navControls.map((navControl: ChromeNavControl, index: number) => (
<EuiHeaderSectionItem key={index} border={side === 'left' ? 'right' : 'left'}>
<EuiHeaderSectionItem
key={index}
border={side ? (side === 'left' ? 'right' : 'left') : 'none'}
>
<HeaderExtension extension={navControl.mount} />
</EuiHeaderSectionItem>
))}

View file

@ -1,83 +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 { EuiHorizontalRule, EuiNavDrawer, EuiNavDrawerGroup } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { useObservable } from 'react-use';
import { Observable } from 'rxjs';
import { ChromeNavLink, ChromeRecentlyAccessedHistoryItem, CoreStart } from '../../..';
import { InternalApplicationStart } from '../../../application/types';
import { HttpStart } from '../../../http';
import { OnIsLockedUpdate } from './';
import { createEuiListItem, createRecentNavLink } from './nav_link';
import { RecentLinks } from './recent_links';
export interface Props {
appId$: InternalApplicationStart['currentAppId$'];
basePath: HttpStart['basePath'];
isLocked?: boolean;
navLinks$: Observable<ChromeNavLink[]>;
recentlyAccessed$: Observable<ChromeRecentlyAccessedHistoryItem[]>;
navigateToApp: CoreStart['application']['navigateToApp'];
onIsLockedUpdate?: OnIsLockedUpdate;
}
function NavDrawerRenderer(
{ isLocked, onIsLockedUpdate, basePath, navigateToApp, ...observables }: Props,
ref: React.Ref<EuiNavDrawer>
) {
const appId = useObservable(observables.appId$, '');
const navLinks = useObservable(observables.navLinks$, []).filter((link) => !link.hidden);
const recentNavLinks = useObservable(observables.recentlyAccessed$, []).map((link) =>
createRecentNavLink(link, navLinks, basePath)
);
return (
<EuiNavDrawer
ref={ref}
data-test-subj="navDrawer"
isLocked={isLocked}
onIsLockedUpdate={onIsLockedUpdate}
aria-label={i18n.translate('core.ui.primaryNav.screenReaderLabel', {
defaultMessage: 'Primary',
})}
>
{RecentLinks({ recentNavLinks })}
<EuiHorizontalRule margin="none" />
<EuiNavDrawerGroup
data-test-subj="navDrawerAppsMenu"
listItems={navLinks.map((link) =>
createEuiListItem({
link,
appId,
basePath,
navigateToApp,
dataTestSubj: 'navDrawerAppsMenuLink',
})
)}
aria-label={i18n.translate('core.ui.primaryNavList.screenReaderLabel', {
defaultMessage: 'Primary navigation links',
})}
/>
</EuiNavDrawer>
);
}
export const NavDrawer = React.forwardRef(NavDrawerRenderer);

View file

@ -1,6 +1,6 @@
@import './variables';
@import './core';
@import './chrome/index';
@import './overlays/index';
@import './rendering/index';
@import './styles/index';

View file

@ -268,10 +268,13 @@ export interface ChromeNavControl {
// @public
export interface ChromeNavControls {
// @internal (undocumented)
getCenter$(): Observable<ChromeNavControl[]>;
// @internal (undocumented)
getLeft$(): Observable<ChromeNavControl[]>;
// @internal (undocumented)
getRight$(): Observable<ChromeNavControl[]>;
registerCenter(navControl: ChromeNavControl): void;
registerLeft(navControl: ChromeNavControl): void;
registerRight(navControl: ChromeNavControl): void;
}
@ -341,7 +344,6 @@ export interface ChromeStart {
getHelpExtension$(): Observable<ChromeHelpExtension | undefined>;
getIsNavDrawerLocked$(): Observable<boolean>;
getIsVisible$(): Observable<boolean>;
getNavType$(): Observable<NavType>;
navControls: ChromeNavControls;
navLinks: ChromeNavLinks;
recentlyAccessed: ChromeRecentlyAccessed;

View file

@ -1,5 +1,4 @@
@import '@elastic/eui/src/global_styling/variables/header';
@import '@elastic/eui/src/components/nav_drawer/variables';
@include euiHeaderAffordForFixed($kbnHeaderOffset);
/**
* stretch the root element of the Kibana application to set the base-size that
@ -12,74 +11,11 @@
min-height: 100%;
}
// TODO #64541
// Delete this block
.chrHeaderWrapper:not(.headerWrapper) ~ .app-wrapper {
display: flex;
flex-flow: column nowrap;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 5;
margin: 0 auto;
&:not(.hidden-chrome) {
top: $euiHeaderChildSize;
left: $euiHeaderChildSize;
// HOTFIX: Temporary fix for flyouts not inside portals
// SASSTODO: Find an actual solution
.euiFlyout {
top: $euiHeaderChildSize;
height: calc(100% - #{$euiHeaderChildSize});
}
@include euiBreakpoint('xs', 's') {
left: 0;
}
}
/**
* 1. Dirty, but we need to override the .kbnGlobalNav-isOpen state
* when we're looking at the log-in screen.
*/
&.hidden-chrome {
left: 0 !important; /* 1 */
}
.navbar-right {
margin-right: 0;
}
}
// TODO #64541
// Delete this block
@include euiBreakpoint('xl') {
.chrHeaderWrapper--navIsLocked:not(.headerWrapper) {
~ .app-wrapper:not(.hidden-chrome) {
// Shrink the content from the left so it's no longer overlapped by the nav drawer (ALWAYS)
left: $euiNavDrawerWidthExpanded !important; // sass-lint:disable-line no-important
transition: left $euiAnimSpeedFast $euiAnimSlightResistance;
}
}
}
// TODO #64541
// Remove .headerWrapper and header conditionals
.headerWrapper ~ .app-wrapper,
:not(header) ~ .app-wrapper {
.app-wrapper {
display: flex;
flex-flow: column nowrap;
margin: 0 auto;
min-height: calc(100vh - #{$euiHeaderHeightCompensation});
@include internetExplorerOnly {
// IE specific bug with min-height in flex container, described in the next link
// https://github.com/philipwalton/flexbugs#3-min-height-on-a-flex-container-wont-apply-to-its-flex-items
height: calc(100vh - #{$euiHeaderHeightCompensation});
}
min-height: calc(100vh - #{$kbnHeaderOffset});
&.hidden-chrome {
min-height: 100vh;

View file

@ -7,17 +7,6 @@
// Application Layout
// chrome-context
// TODO #64541
// Delete this block
.chrHeaderWrapper:not(.headerWrapper) .content {
display: flex;
flex-flow: row nowrap;
width: 100%;
height: 100%;
overflow: hidden;
}
.application,
.app-container {
> * {

View file

@ -121,7 +121,7 @@ export class AdvancedSettingsComponent extends Component<
setTimeout(() => {
const id = hash.replace('#', '');
const element = document.getElementById(id);
const globalNavOffset = document.getElementById('headerGlobalNav')?.offsetHeight || 0;
const globalNavOffset = document.getElementById('globalHeaderBars')?.offsetHeight || 0;
if (element) {
element.scrollIntoView();

View file

@ -1 +0,0 @@
@import './form/index';

View file

@ -1,15 +0,0 @@
@import '@elastic/eui/src/global_styling/variables/header';
@import '@elastic/eui/src/components/nav_drawer/variables';
// TODO #64541
// Delete this whole file
.mgtAdvancedSettingsForm__bottomBar {
margin-left: $euiNavDrawerWidthCollapsed;
z-index: 9; // Puts it inuder the nav drawer when expanded
&--pushForNav {
margin-left: $euiNavDrawerWidthExpanded;
}
@include euiBreakpoint('xs', 's') {
margin-left: 0;
}
}

View file

@ -18,7 +18,6 @@
*/
import React, { PureComponent, Fragment } from 'react';
import classNames from 'classnames';
import {
EuiFlexGroup,
@ -45,7 +44,6 @@ import { Field, getEditableValue } from '../field';
import { FieldSetting, SettingsChanges, FieldState } from '../../types';
type Category = string;
const NAV_IS_LOCKED_KEY = 'core.chrome.isLocked';
interface FormProps {
settings: Record<string, FieldSetting[]>;
@ -326,23 +324,8 @@ export class Form extends PureComponent<FormProps> {
renderBottomBar = () => {
const areChangesInvalid = this.areChangesInvalid();
// TODO #64541
// Delete these classes
let bottomBarClasses = '';
const pageNav = this.props.settings.general.find(
(setting) => setting.name === 'pageNavigation'
);
if (pageNav?.value === 'legacy') {
bottomBarClasses = classNames('mgtAdvancedSettingsForm__bottomBar', {
// eslint-disable-next-line @typescript-eslint/naming-convention
'mgtAdvancedSettingsForm__bottomBar--pushForNav':
localStorage.getItem(NAV_IS_LOCKED_KEY) === 'true',
});
}
return (
<EuiBottomBar className={bottomBarClasses} data-test-subj="advancedSetting-bottomBar">
<EuiBottomBar data-test-subj="advancedSetting-bottomBar">
<EuiFlexGroup
justifyContent="spaceBetween"
alignItems="center"

View file

@ -1,3 +1 @@
@import './advanced_settings';
@import './components/index';

View file

@ -1,11 +1,6 @@
// TODO: Move all of the styles here (should be modularised by, e.g., CSS-in-JS or CSS modules).
@import '@elastic/eui/src/global_styling/variables/header';
// This value is calculated to static value using SCSS because calc in calc has issues in IE11
$headerHeightOffset: $euiHeaderHeightCompensation * 2;
#consoleRoot {
height: calc(100vh - #{$headerHeightOffset});
display: flex;
flex: 1 1 auto;
// Make sure the editor actions don't create scrollbars on this container

View file

@ -1,7 +1,6 @@
.dshAppContainer {
display: flex;
flex-direction: column;
height: 100%; // TODO #64541 - can delete this
flex: 1;
}

View file

@ -31,6 +31,7 @@ import {
SavedObjectsClientContract,
PluginInitializerContext,
ScopedHistory,
AppMountParameters,
} from 'kibana/public';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/public';
import { Storage } from '../../../kibana_utils/public';
@ -73,6 +74,7 @@ export interface RenderDeps {
navigateToDefaultApp: UrlForwardingStart['navigateToDefaultApp'];
navigateToLegacyKibanaUrl: UrlForwardingStart['navigateToLegacyKibanaUrl'];
scopedHistory: () => ScopedHistory;
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
savedObjects: SavedObjectsStart;
restorePreviousUrl: () => void;
}

View file

@ -153,6 +153,7 @@ export class DashboardAppController {
i18n: i18nStart,
},
history,
setHeaderActionMenu,
kbnUrlStateStorage,
usageCollection,
navigation,
@ -709,7 +710,13 @@ export class DashboardAppController {
};
const dashboardNavBar = document.getElementById('dashboardChrome');
const updateNavBar = () => {
ReactDOM.render(<navigation.ui.TopNavMenu {...getNavBarProps()} />, dashboardNavBar);
ReactDOM.render(
<navigation.ui.TopNavMenu
{...getNavBarProps()}
{...(isEmbeddedExternally ? {} : { setMenuMountPoint: setHeaderActionMenu })}
/>,
dashboardNavBar
);
};
const unmountNavBar = () => {

View file

@ -310,7 +310,7 @@ export class DashboardPlugin
id: DashboardConstants.DASHBOARDS_ID,
title: 'Dashboard',
order: -1001,
euiIconType: 'dashboardApp',
euiIconType: 'logoKibana',
defaultPath: `#${DashboardConstants.LANDING_PAGE_PATH}`,
updater$: this.appStateUpdater,
category: DEFAULT_APP_CATEGORIES.kibana,
@ -352,6 +352,7 @@ export class DashboardPlugin
localStorage: new Storage(localStorage),
usageCollection,
scopedHistory: () => this.currentHistory!,
setHeaderActionMenu: params.setHeaderActionMenu,
savedObjects,
restorePreviousUrl,
};

View file

@ -16,10 +16,6 @@
}
}
.devApp {
height: 100%;
}
.devAppWrapper {
display: flex;
flex-direction: column;

View file

@ -60,7 +60,7 @@ export class DevToolsPlugin implements Plugin<DevToolsSetup, void> {
defaultMessage: 'Dev Tools',
}),
updater$: this.appStateUpdater,
euiIconType: 'devToolsApp',
euiIconType: 'logoElastic',
order: 9010,
category: DEFAULT_APP_CATEGORIES.management,
mount: async (params: AppMountParameters) => {

View file

@ -5,6 +5,7 @@
<kbn-top-nav
app-name="'discover'"
config="topNavMenu"
set-menu-mount-point="setHeaderActionMenu"
index-patterns="[indexPattern]"
on-query-submit="handleRefresh"
on-saved-query-id-change="updateSavedQueryId"

View file

@ -41,6 +41,7 @@ import {
getRequestInspectorStats,
getResponseInspectorStats,
getServices,
getHeaderActionMenuMounter,
getUrlTracker,
unhashUrl,
subscribeWithScope,
@ -470,6 +471,7 @@ function discoverController($element, $route, $scope, $timeout, $window, Promise
];
};
$scope.topNavMenu = getTopNavLinks();
$scope.setHeaderActionMenu = getHeaderActionMenuMounter();
$scope.searchSource
.setField('index', $scope.indexPattern)

View file

@ -19,7 +19,7 @@
import _ from 'lodash';
import { createHashHistory } from 'history';
import { ScopedHistory } from 'kibana/public';
import { ScopedHistory, AppMountParameters } from 'kibana/public';
import { UiActionsStart } from 'src/plugins/ui_actions/public';
import { DiscoverServices } from './build_services';
import { createGetterSetter } from '../../kibana_utils/public';
@ -58,6 +58,10 @@ export function setServices(newServices: any) {
export const setUiActions = (pluginUiActions: UiActionsStart) => (uiActions = pluginUiActions);
export const getUiActions = () => uiActions;
export const [getHeaderActionMenuMounter, setHeaderActionMenuMounter] = createGetterSetter<
AppMountParameters['setHeaderActionMenu']
>('headerActionMenuMounter');
export const [getUrlTracker, setUrlTracker] = createGetterSetter<{
setTrackedUrl: (url: string) => void;
restorePreviousUrl: () => void;

View file

@ -54,6 +54,7 @@ import {
setUrlTracker,
setAngularModule,
setServices,
setHeaderActionMenuMounter,
setUiActions,
setScopedHistory,
getScopedHistory,
@ -240,7 +241,7 @@ export class DiscoverPlugin
title: 'Discover',
updater$: this.appStateUpdater.asObservable(),
order: -1004,
euiIconType: 'discoverApp',
euiIconType: 'logoKibana',
defaultPath: '#/',
category: DEFAULT_APP_CATEGORIES.kibana,
mount: async (params: AppMountParameters) => {
@ -251,6 +252,7 @@ export class DiscoverPlugin
throw Error('Discover plugin method initializeInnerAngular is undefined');
}
setScopedHistory(params.history);
setHeaderActionMenuMounter(params.setHeaderActionMenu);
syncHistoryLocations();
appMounted();
const {
@ -264,6 +266,7 @@ export class DiscoverPlugin
params.element.classList.add('dscAppWrapper');
const unmount = await renderApp(innerAngularName, params.element);
return () => {
params.element.classList.remove('dscAppWrapper');
unmount();
appUnMounted();
};

View file

@ -74,6 +74,7 @@ export function createTopNavDirective() {
export const createTopNavHelper = ({ TopNavMenu }) => (reactDirective) => {
return reactDirective(TopNavMenu, [
['config', { watchDepth: 'value' }],
['setMenuMountPoint', { watchDepth: 'reference' }],
['disabledButtons', { watchDepth: 'reference' }],
['query', { watchDepth: 'reference' }],

View file

@ -19,3 +19,4 @@
export { toMountPoint } from './to_mount_point';
export { MountPointPortal } from './mount_point_portal';
export { useIfMounted } from './utils';

View file

@ -21,6 +21,7 @@ import { i18n } from '@kbn/i18n';
import React, { useRef, useEffect, useState, Component } from 'react';
import ReactDOM from 'react-dom';
import { MountPoint } from 'kibana/public';
import { useIfMounted } from './utils';
interface MountPointPortalProps {
setMountPoint: (mountPoint: MountPoint<HTMLElement>) => void;
@ -33,20 +34,30 @@ export const MountPointPortal: React.FC<MountPointPortalProps> = ({ children, se
// state used to force re-renders when the element changes
const [shouldRender, setShouldRender] = useState(false);
const el = useRef<HTMLElement>();
const ifMounted = useIfMounted();
useEffect(() => {
setMountPoint((element) => {
el.current = element;
setShouldRender(true);
ifMounted(() => {
el.current = element;
setShouldRender(true);
});
return () => {
setShouldRender(false);
el.current = undefined;
// the component can be unmounted from the dom before the portal target actually
// calls the `unmount` function. This is a no-op but show a scary warning in the console
// so we use a ifMounted effect to avoid it.
ifMounted(() => {
setShouldRender(false);
el.current = undefined;
});
};
});
return () => {
setShouldRender(false);
el.current = undefined;
ifMounted(() => {
setShouldRender(false);
el.current = undefined;
});
};
}, [setMountPoint]);

View file

@ -0,0 +1,38 @@
/*
* 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 { useCallback, useEffect, useRef } from 'react';
export const useIfMounted = () => {
const isMounted = useRef(true);
useEffect(
() => () => {
isMounted.current = false;
},
[]
);
const ifMounted = useCallback((func) => {
if (isMounted.current && func) {
func();
}
}, []);
return ifMounted;
};

View file

@ -74,7 +74,7 @@ export class ManagementPlugin implements Plugin<ManagementSetup, ManagementStart
defaultMessage: 'Stack Management',
}),
order: 9040,
euiIconType: 'managementApp',
euiIconType: 'logoElastic',
category: DEFAULT_APP_CATEGORIES.management,
updater$: this.appUpdater,
async mount(params: AppMountParameters) {

View file

@ -1,15 +1,4 @@
.kbnTopNavMenu__wrapper {
z-index: 5;
.kbnTopNavMenu {
padding: $euiSizeS 0;
.kbnTopNavItemEmphasized {
padding: 0 $euiSizeS;
}
}
.kbnTopNavMenu-isFullScreen {
padding: 0;
}
.kbnTopNavMenu > * > * {
// TEMP fix to adjust spacing between EuiHeaderList__list items
margin: 0 $euiSizeXS;
}

View file

@ -164,7 +164,10 @@ describe('TopNavMenu', () => {
// menu is rendered outside of the component
expect(component.find(TOP_NAV_ITEM_SELECTOR).length).toBe(0);
expect(portalTarget.getElementsByTagName('BUTTON').length).toBe(menuItems.length);
const buttons = portalTarget.querySelectorAll('button');
expect(buttons.length).toBe(menuItems.length + 1); // should be n+1 buttons in mobile for popover button
expect(buttons[buttons.length - 1].getAttribute('aria-label')).toBe('Open navigation menu'); // last button should be mobile button
});
});
});

View file

@ -18,7 +18,7 @@
*/
import React, { ReactElement } from 'react';
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
import { EuiHeaderLinks } from '@elastic/eui';
import classNames from 'classnames';
import { MountPoint } from '../../../../core/public';
@ -81,31 +81,16 @@ export function TopNavMenu(props: TopNavMenuProps): ReactElement | null {
function renderItems(): ReactElement[] | null {
if (!config || config.length === 0) return null;
return config.map((menuItem: TopNavMenuData, i: number) => {
return (
<EuiFlexItem
grow={false}
key={`nav-menu-${i}`}
className={menuItem.emphasize ? 'kbnTopNavItemEmphasized' : ''}
>
<TopNavMenuItem {...menuItem} />
</EuiFlexItem>
);
return <TopNavMenuItem key={`nav-menu-${i}`} {...menuItem} />;
});
}
function renderMenu(className: string): ReactElement | null {
if (!config || config.length === 0) return null;
return (
<EuiFlexGroup
data-test-subj="top-nav"
justifyContent="flexStart"
alignItems="center"
gutterSize="none"
className={className}
responsive={false}
>
<EuiHeaderLinks data-test-subj="top-nav" className={className}>
{renderItems()}
</EuiFlexGroup>
</EuiHeaderLinks>
);
}

View file

@ -19,9 +19,7 @@
import { upperFirst, isFunction } from 'lodash';
import React, { MouseEvent } from 'react';
import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
import { EuiButton } from '@elastic/eui';
import { EuiToolTip, EuiButton, EuiHeaderLink } from '@elastic/eui';
import { TopNavMenuData } from './top_nav_menu_data';
export function TopNavMenuItem(props: TopNavMenuData) {
@ -50,13 +48,13 @@ export function TopNavMenuItem(props: TopNavMenuData) {
};
const btn = props.emphasize ? (
<EuiButton {...commonButtonProps} size="s" fill>
<EuiButton size="s" fill {...commonButtonProps}>
{upperFirst(props.label || props.id!)}
</EuiButton>
) : (
<EuiButtonEmpty {...commonButtonProps} size="xs">
<EuiHeaderLink size="xs" isActive {...commonButtonProps}>
{upperFirst(props.label || props.id!)}
</EuiButtonEmpty>
</EuiHeaderLink>
);
const tooltip = getTooltip();

View file

@ -68,7 +68,7 @@ export const NewsfeedNavButton = ({ apiFetchResult }: Props) => {
aria-label="Newsfeed menu"
onClick={showFlyout}
>
<EuiIcon type="email" size="m" />
<EuiIcon type="cheer" size="m" />
{showBadge ? (
<EuiNotificationBadge className="euiHeaderNotification" data-test-subj="showBadgeNews">
&#9642;

View file

@ -1,8 +1,5 @@
@import '@elastic/eui/src/global_styling/variables/header';
.timApp {
position: relative;
min-height: calc(100vh - #{$euiHeaderChildSize});
background: $euiColorEmptyShade;
[ng-click] {

View file

@ -91,7 +91,7 @@ export class TimelionPlugin implements Plugin<void, void> {
title: 'Timelion',
order: 8000,
defaultPath: '#/',
euiIconType: 'timelionApp',
euiIconType: 'logoKibana',
category: DEFAULT_APP_CATEGORIES.kibana,
updater$: this.appStateUpdater.asObservable(),
mount: async (params: AppMountParameters) => {

View file

@ -61,6 +61,7 @@ const TopNav = ({
}: VisualizeTopNavProps) => {
const { services } = useKibana<VisualizeServices>();
const { TopNavMenu } = services.navigation.ui;
const { setHeaderActionMenu } = services;
const { embeddableHandler, vis } = visInstance;
const [inspectorSession, setInspectorSession] = useState<OverlayRef>();
const openInspector = useCallback(() => {
@ -151,6 +152,7 @@ const TopNav = ({
<TopNavMenu
appName={APP_NAME}
config={config}
setMenuMountPoint={setHeaderActionMenu}
onQuerySubmit={handleRefresh}
savedQueryId={currentAppState.savedQuery}
onSavedQueryIdChange={stateContainer.transitions.updateSavedQuery}
@ -171,6 +173,7 @@ const TopNav = ({
*/
<TopNavMenu
appName={APP_NAME}
setMenuMountPoint={setHeaderActionMenu}
indexPatterns={indexPattern ? [indexPattern] : undefined}
showSearchBar
showSaveQuery={false}

View file

@ -33,6 +33,7 @@ import {
ChromeStart,
ToastsStart,
ScopedHistory,
AppMountParameters,
} from 'kibana/public';
import { NavigationPublicPluginStart as NavigationStart } from 'src/plugins/navigation/public';
import {
@ -112,6 +113,7 @@ export interface VisualizeServices extends CoreStart {
restorePreviousUrl: () => void;
scopedHistory: ScopedHistory;
dashboard: DashboardStart;
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
}
export interface SavedVisInstance {

View file

@ -140,7 +140,7 @@ export class VisualizePlugin
id: 'visualize',
title: 'Visualize',
order: -1002,
euiIconType: 'visualizeApp',
euiIconType: 'logoKibana',
defaultPath: '#/',
category: DEFAULT_APP_CATEGORIES.kibana,
updater$: this.appStateUpdater.asObservable(),
@ -196,12 +196,14 @@ export class VisualizePlugin
scopedHistory: params.history,
restorePreviousUrl,
dashboard: pluginsStart.dashboard,
setHeaderActionMenu: params.setHeaderActionMenu,
};
params.element.classList.add('visAppWrapper');
const { renderApp } = await import('./application');
const unmount = renderApp(params, services);
return () => {
params.element.classList.remove('visAppWrapper');
unlistenParentHistory();
unmount();
appUnMounted();

View file

@ -64,8 +64,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await a11y.testAppSnapshot();
});
// Will be enabling this and field formatters after this issue is addressed: https://github.com/elastic/kibana/issues/60030
it.skip('Edit field type', async () => {
it('Edit field type', async () => {
await PageObjects.settings.clickEditFieldFormat();
await a11y.testAppSnapshot();
});

View file

@ -39,7 +39,7 @@ export function TimePickerProvider({ getService, getPageObjects }: FtrProviderCo
const find = getService('find');
const browser = getService('browser');
const testSubjects = getService('testSubjects');
const { header, common } = getPageObjects(['header', 'common']);
const { header } = getPageObjects(['header']);
const kibanaServer = getService('kibanaServer');
class TimePicker {
@ -127,7 +127,7 @@ export function TimePickerProvider({ getService, getPageObjects }: FtrProviderCo
await testSubjects.click('superDatePickerAbsoluteTab');
await testSubjects.click('superDatePickerAbsoluteDateInput');
await this.inputValue('superDatePickerAbsoluteDateInput', toTime);
await common.sleep(500);
await browser.pressKeys(browser.keys.ESCAPE); // close popover because sometimes browser can't find start input
// set from time
await testSubjects.click('superDatePickerstartDatePopoverButton');

View file

@ -33,7 +33,7 @@ export function ListingTableProvider({ getService, getPageObjects }: FtrProvider
*/
class ListingTable {
private async getSearchFilter() {
const searchFilter = await find.allByCssSelector('.euiFieldSearch');
const searchFilter = await find.allByCssSelector('main .euiFieldSearch');
return searchFilter[0];
}

View file

@ -2,7 +2,10 @@
"prefix": "xpack",
"paths": {
"xpack.actions": "plugins/actions",
"xpack.uiActionsEnhanced": ["plugins/ui_actions_enhanced", "examples/ui_actions_enhanced_examples"],
"xpack.uiActionsEnhanced": [
"plugins/ui_actions_enhanced",
"examples/ui_actions_enhanced_examples"
],
"xpack.alerts": "plugins/alerts",
"xpack.eventLog": "plugins/event_log",
"xpack.alertingBuiltins": "plugins/alerting_builtins",
@ -21,6 +24,7 @@
"xpack.features": "plugins/features",
"xpack.fileUpload": "plugins/file_upload",
"xpack.globalSearch": ["plugins/global_search"],
"xpack.globalSearchBar": ["plugins/global_search_bar"],
"xpack.graph": ["plugins/graph"],
"xpack.grokDebugger": "plugins/grokdebugger",
"xpack.idxMgmt": "plugins/index_management",

View file

@ -105,7 +105,7 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
id: 'apm',
title: 'APM',
order: 8300,
euiIconType: 'apmApp',
euiIconType: 'logoObservability',
appRoute: '/app/apm',
icon: 'plugins/apm/public/icon.svg',
category: DEFAULT_APP_CATEGORIES.observability,
@ -125,6 +125,7 @@ export class ApmPlugin implements Plugin<ApmPluginSetup, ApmPluginStart> {
id: 'csm',
title: 'Client Side Monitoring',
order: 8500,
euiIconType: 'logoObservability',
category: DEFAULT_APP_CATEGORIES.observability,
async mount(params: AppMountParameters<unknown>) {

View file

@ -355,181 +355,3 @@ exports[`Storyshots components/Assets/Asset marker 1`] = `
</div>
</div>
`;
exports[`Storyshots components/Assets/Asset redux 1`] = `
<div
style={
Object {
"width": "215px",
}
}
>
<div
className="euiFlexItem"
>
<div
className="euiPanel euiPanel--paddingSmall canvasAsset"
>
<div
className="canvasAsset__thumb canvasCheckered"
>
<figure
className="euiImage canvasAsset__img "
>
<img
alt="Asset thumbnail"
className="euiImage__img"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1Ni4zMSA1Ni4zMSI+PGRlZnM+PHN0eWxlPi5jbHMtMXtmaWxsOiNmZmY7c3Ryb2tlOiMwMDc4YTA7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLXdpZHRoOjJweDt9PC9zdHlsZT48L2RlZnM+PHRpdGxlPlBsYW5lIEljb248L3RpdGxlPjxnIGlkPSJMYXllcl8yIiBkYXRhLW5hbWU9IkxheWVyIDIiPjxnIGlkPSJMYXllcl8xLTIiIGRhdGEtbmFtZT0iTGF5ZXIgMSI+PHBhdGggY2xhc3M9ImNscy0xIiBkPSJNNDkuNTEsNDguOTMsNDEuMjYsMjIuNTIsNTMuNzYsMTBhNS4yOSw1LjI5LDAsMCwwLTcuNDgtNy40N2wtMTIuNSwxMi41TDcuMzgsNi43OUEuNy43LDAsMCwwLDYuNjksN0wxLjIsMTIuNDVhLjcuNywwLDAsMCwwLDFMMTkuODUsMjlsLTcuMjQsNy4yNC03Ljc0LS42YS43MS43MSwwLDAsMC0uNTMuMkwxLjIxLDM5YS42Ny42NywwLDAsMCwuMDgsMUw5LjQ1LDQ2bC4wNywwYy4xMS4xMy4yMi4yNi4zNC4zOHMuMjUuMjMuMzguMzRhLjM2LjM2LDAsMCwwLDAsLjA3TDE2LjMzLDU1YS42OC42OCwwLDAsMCwxLC4wN0wyMC40OSw1MmEuNjcuNjcsMCwwLDAsLjE5LS41NGwtLjU5LTcuNzQsNy4yNC03LjI0TDQyLjg1LDU1LjA2YS42OC42OCwwLDAsMCwxLDBsNS41LTUuNUEuNjYuNjYsMCwwLDAsNDkuNTEsNDguOTNaIi8+PC9nPjwvZz48L3N2Zz4="
style={Object {}}
/>
</figure>
</div>
<div
className="euiSpacer euiSpacer--s"
/>
<div
className="euiText euiText--extraSmall eui-textBreakAll"
>
<p
className="eui-textBreakAll"
>
<strong>
airplane
</strong>
<br />
<span
className="euiTextColor euiTextColor--subdued"
>
<small>
(
1
kb)
</small>
</span>
</p>
</div>
<div
className="euiSpacer euiSpacer--s"
/>
<div
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsBaseline euiFlexGroup--justifyContentCenter euiFlexGroup--directionRow"
>
<div
className="euiFlexItem euiFlexItem--flexGrowZero asset-create-image"
>
<span
className="euiToolTipAnchor"
onKeyUp={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<button
aria-label="Create image element"
className="euiButtonIcon euiButtonIcon--primary"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
type="button"
>
<div
aria-hidden="true"
className="euiButtonIcon__icon"
data-euiicon-type="vector"
size="m"
/>
</button>
</span>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero asset-download"
>
<span
className="euiToolTipAnchor"
onKeyUp={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<div
className="canvasDownload"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex={0}
>
<button
aria-label="Download"
className="euiButtonIcon euiButtonIcon--primary"
type="button"
>
<div
aria-hidden="true"
className="euiButtonIcon__icon"
data-euiicon-type="sortDown"
size="m"
/>
</button>
</div>
</span>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<span
className="euiToolTipAnchor"
onKeyUp={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<div
className="canvasClipboard"
onClick={[Function]}
onKeyPress={[Function]}
role="button"
tabIndex={0}
>
<button
aria-label="Copy id to clipboard"
className="euiButtonIcon euiButtonIcon--primary"
type="button"
>
<div
aria-hidden="true"
className="euiButtonIcon__icon"
data-euiicon-type="copyClipboard"
size="m"
/>
</button>
</div>
</span>
</div>
<div
className="euiFlexItem euiFlexItem--flexGrowZero"
>
<span
className="euiToolTipAnchor"
onKeyUp={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
>
<button
aria-label="Delete"
className="euiButtonIcon euiButtonIcon--danger"
onBlur={[Function]}
onClick={[Function]}
onFocus={[Function]}
type="button"
>
<div
aria-hidden="true"
className="euiButtonIcon__icon"
data-euiicon-type="trash"
size="m"
/>
</button>
</span>
</div>
</div>
</div>
</div>
</div>
`;

View file

@ -1,5 +1,5 @@
body.canvas-isFullscreen { // sass-lint:disable-line no-qualifying-elements
// following two rules are for overriding the header bar padding
// following two rules are for overriding the header bar padding
&.euiBody--headerIsFixed {
padding-top: 0;
}
@ -13,28 +13,17 @@ body.canvas-isFullscreen { // sass-lint:disable-line no-qualifying-elements
padding-left: 0 !important; // sass-lint:disable-line no-important
}
// hide global loading indicator
.kbnLoadingIndicator {
display: none;
}
// remove space for global nav elements
// TODO #64541
// Can delete this block
.chrHeaderWrapper ~ .app-wrapper {
// Override locked nav at all breakpoints
left: 0 !important; // sass-lint:disable-line no-important
top: 0;
}
// set the background color
.canvasLayout {
background: $euiColorInk;
}
// hide all the interface parts
.chrHeaderWrapper, // K7 global top nav
.canvasLayout__stageHeader,
.canvasLayout__sidebar,
.canvasLayout__footer,

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { platformService } from '../services';
export const fullscreenClass = 'canvas-isFullscreen';
export function setFullscreen(fullscreen, doc = document) {
@ -13,8 +15,10 @@ export function setFullscreen(fullscreen, doc = document) {
const isFullscreen = bodyClassList.contains(fullscreenClass);
if (enabled && !isFullscreen) {
platformService.getService().setFullscreen(false);
bodyClassList.add(fullscreenClass);
} else if (!enabled && isFullscreen) {
bodyClassList.remove(fullscreenClass);
platformService.getService().setFullscreen(true);
}
}

View file

@ -88,7 +88,7 @@ export class CanvasPlugin
category: DEFAULT_APP_CATEGORIES.kibana,
id: 'canvas',
title: 'Canvas',
euiIconType: 'canvasApp',
euiIconType: 'logoKibana',
order: 3000,
updater$: this.appUpdater,
mount: async (params: AppMountParameters) => {

View file

@ -10,6 +10,7 @@ import {
IUiSettingsClient,
ChromeBreadcrumb,
IBasePath,
ChromeStart,
} from '../../../../../src/core/public';
import { CanvasServiceFactory } from '.';
@ -22,6 +23,7 @@ export interface PlatformService {
getUISetting: (key: string, defaultValue?: any) => any;
setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void;
setRecentlyAccessed: (link: string, label: string, id: string) => void;
setFullscreen: ChromeStart['setIsVisible'];
// TODO: these should go away. We want thin accessors, not entire objects.
// Entire objects are hard to mock, and hide our dependency on the external service.
@ -45,6 +47,7 @@ export const platformServiceFactory: CanvasServiceFactory<PlatformService> = (
getUISetting: coreStart.uiSettings.get.bind(coreStart.uiSettings),
setBreadcrumbs: coreStart.chrome.setBreadcrumbs,
setRecentlyAccessed: coreStart.chrome.recentlyAccessed.add,
setFullscreen: coreStart.chrome.setIsVisible,
// TODO: these should go away. We want thin accessors, not entire objects.
// Entire objects are hard to mock, and hide our dependency on the external service.

View file

@ -20,4 +20,5 @@ export const platformService: PlatformService = {
getSavedObjects: noop,
getSavedObjectsClient: noop,
getUISettings: noop,
setFullscreen: noop,
};

View file

@ -29,6 +29,7 @@ export const ENTERPRISE_SEARCH_PLUGIN = {
}),
],
URL: '/app/enterprise_search/overview',
LOGO: 'logoEnterpriseSearch',
};
export const APP_SEARCH_PLUGIN = {

View file

@ -5,26 +5,25 @@
*/
import {
Plugin,
PluginInitializerContext,
AppMountParameters,
CoreSetup,
CoreStart,
AppMountParameters,
HttpSetup,
Plugin,
PluginInitializerContext,
} from 'src/core/public';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
import {
FeatureCatalogueCategory,
HomePublicPluginSetup,
} from '../../../../src/plugins/home/public';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/public';
import { LicensingPluginSetup } from '../../licensing/public';
import { IInitialAppData } from '../common/types';
import {
ENTERPRISE_SEARCH_PLUGIN,
APP_SEARCH_PLUGIN,
ENTERPRISE_SEARCH_PLUGIN,
WORKPLACE_SEARCH_PLUGIN,
} from '../common/constants';
import { IInitialAppData } from '../common/types';
import { ExternalUrl, IExternalUrl } from './applications/shared/enterprise_search_url';
export interface ClientConfigType {
@ -73,6 +72,7 @@ export class EnterpriseSearchPlugin implements Plugin {
core.application.register({
id: APP_SEARCH_PLUGIN.ID,
title: APP_SEARCH_PLUGIN.NAME,
euiIconType: ENTERPRISE_SEARCH_PLUGIN.LOGO,
appRoute: APP_SEARCH_PLUGIN.URL,
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
mount: async (params: AppMountParameters) => {
@ -92,6 +92,7 @@ export class EnterpriseSearchPlugin implements Plugin {
core.application.register({
id: WORKPLACE_SEARCH_PLUGIN.ID,
title: WORKPLACE_SEARCH_PLUGIN.NAME,
euiIconType: ENTERPRISE_SEARCH_PLUGIN.LOGO,
appRoute: WORKPLACE_SEARCH_PLUGIN.URL,
category: DEFAULT_APP_CATEGORIES.enterpriseSearch,
mount: async (params: AppMountParameters) => {

View file

@ -0,0 +1,3 @@
# Kibana GlobalSearchBar plugin
The GlobalSearchBar plugin provides a search interface for navigating Kibana. (It is the UI to the GlobalSearch plugin.)

View file

@ -0,0 +1,10 @@
{
"id": "globalSearchBar",
"version": "8.0.0",
"kibanaVersion": "kibana",
"server": false,
"ui": true,
"requiredPlugins": ["globalSearch"],
"optionalPlugins": [],
"configPath": ["xpack", "global_search_bar"]
}

View file

@ -0,0 +1,75 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SearchBar correctly filters and sorts results 1`] = `
Array [
Object {
"append": undefined,
"className": "euiSelectableTemplateSitewide__listItem",
"key": "Canvas",
"label": "Canvas",
"prepend": undefined,
"title": "Canvasundefinedundefined",
"url": "/app/test/Canvas",
},
Object {
"append": undefined,
"className": "euiSelectableTemplateSitewide__listItem",
"key": "Discover",
"label": "Discover",
"prepend": undefined,
"title": "Discoverundefinedundefined",
"url": "/app/test/Discover",
},
Object {
"append": undefined,
"className": "euiSelectableTemplateSitewide__listItem",
"key": "Graph",
"label": "Graph",
"prepend": undefined,
"title": "Graphundefinedundefined",
"url": "/app/test/Graph",
},
]
`;
exports[`SearchBar correctly filters and sorts results 2`] = `
Array [
Object {
"append": undefined,
"className": "euiSelectableTemplateSitewide__listItem",
"key": "Discover",
"label": "Discover",
"prepend": undefined,
"title": "Discoverundefinedundefined",
"url": "/app/test/Discover",
},
Object {
"append": undefined,
"className": "euiSelectableTemplateSitewide__listItem",
"key": "My Dashboard",
"label": "My Dashboard",
"meta": Array [
Object {
"text": "Test",
},
],
"prepend": undefined,
"title": "My Dashboard • Test",
"url": "/app/test/My Dashboard",
},
]
`;
exports[`SearchBar supports keyboard shortcuts 1`] = `
<input
aria-activedescendant=""
aria-haspopup="listbox"
aria-label="Filter options"
autocomplete="off"
class="euiFieldSearch euiFieldSearch--fullWidth euiFieldSearch--compressed euiSelectableSearch euiSelectableTemplateSitewide__search"
data-test-subj="header-search"
placeholder="Search Elastic"
type="search"
value=""
/>
`;

View file

@ -0,0 +1,97 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { wait } from '@testing-library/react';
import { of } from 'rxjs';
import { mountWithIntl } from 'test_utils/enzyme_helpers';
import {
GlobalSearchBatchedResults,
GlobalSearchPluginStart,
GlobalSearchResult,
} from '../../../global_search/public';
import { globalSearchPluginMock } from '../../../global_search/public/mocks';
import { SearchBar } from '../components/search_bar';
type Result = { id: string; type: string } | string;
const createResult = (result: Result): GlobalSearchResult => {
const id = typeof result === 'string' ? result : result.id;
const type = typeof result === 'string' ? 'application' : result.type;
return {
id,
type,
title: id,
url: `/app/test/${id}`,
score: 42,
};
};
const createBatch = (...results: Result[]): GlobalSearchBatchedResults => ({
results: results.map(createResult),
});
jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({
htmlIdGenerator: () => () => 'mockId',
}));
const getSelectableProps: any = (component: any) => component.find('EuiSelectable').props();
const getSearchProps: any = (component: any) => component.find('EuiFieldSearch').props();
describe('SearchBar', () => {
let searchService: GlobalSearchPluginStart;
let findSpy: jest.SpyInstance;
beforeEach(() => {
searchService = globalSearchPluginMock.createStartContract();
findSpy = jest.spyOn(searchService, 'find');
jest.useFakeTimers();
});
it('correctly filters and sorts results', async () => {
const navigate = jest.fn();
findSpy
.mockReturnValueOnce(
of(
createBatch('Discover', 'Canvas'),
createBatch({ id: 'Visualize', type: 'test' }, 'Graph')
)
)
.mockReturnValueOnce(of(createBatch('Discover', { id: 'My Dashboard', type: 'test' })));
const component = mountWithIntl(
<SearchBar globalSearch={searchService.find} navigateToUrl={navigate} />
);
expect(findSpy).toHaveBeenCalledTimes(0);
component.find('input[data-test-subj="header-search"]').simulate('focus');
jest.runAllTimers();
component.update();
expect(findSpy).toHaveBeenCalledTimes(1);
expect(findSpy).toHaveBeenCalledWith('', {});
expect(getSelectableProps(component).options).toMatchSnapshot();
await wait(() => getSearchProps(component).onSearch('d'));
jest.runAllTimers();
component.update();
expect(getSelectableProps(component).options).toMatchSnapshot();
expect(findSpy).toHaveBeenCalledTimes(2);
expect(findSpy).toHaveBeenCalledWith('d', {});
});
it('supports keyboard shortcuts', () => {
mountWithIntl(<SearchBar globalSearch={searchService.find} navigateToUrl={jest.fn()} />);
const searchEvent = new KeyboardEvent('keydown', {
key: '/',
ctrlKey: true,
metaKey: true,
} as any);
window.dispatchEvent(searchEvent);
expect(document.activeElement).toMatchSnapshot();
});
});

View file

@ -0,0 +1,217 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import {
EuiBadge,
EuiFlexGroup,
EuiFlexItem,
EuiSelectableTemplateSitewide,
EuiSelectableTemplateSitewideOption,
EuiText,
EuiSelectableMessage,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { ApplicationStart } from 'kibana/public';
import React, { useCallback, useState } from 'react';
import useDebounce from 'react-use/lib/useDebounce';
import useEvent from 'react-use/lib/useEvent';
import useMountedState from 'react-use/lib/useMountedState';
import { GlobalSearchPluginStart, GlobalSearchResult } from '../../../global_search/public';
interface Props {
globalSearch: GlobalSearchPluginStart['find'];
navigateToUrl: ApplicationStart['navigateToUrl'];
}
const clearField = (field: HTMLInputElement) => {
const nativeInputValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value');
const nativeInputValueSetter = nativeInputValue ? nativeInputValue.set : undefined;
if (nativeInputValueSetter) {
nativeInputValueSetter.call(field, '');
}
field.dispatchEvent(new Event('change'));
};
const cleanMeta = (str: string) => (str.charAt(0).toUpperCase() + str.slice(1)).replace(/-/g, ' ');
const blurEvent = new FocusEvent('blur');
export function SearchBar({ globalSearch, navigateToUrl }: Props) {
const isMounted = useMountedState();
const [searchValue, setSearchValue] = useState<string>('');
const [searchRef, setSearchRef] = useState<HTMLInputElement | null>(null);
const [options, _setOptions] = useState([] as EuiSelectableTemplateSitewideOption[]);
const isMac = navigator.platform.toLowerCase().indexOf('mac') >= 0;
const setOptions = useCallback(
(_options: GlobalSearchResult[]) => {
if (!isMounted()) return;
_setOptions([
..._options.map((option) => ({
key: option.id,
label: option.title,
url: option.url,
...(option.icon && { icon: { type: option.icon } }),
...(option.type &&
option.type !== 'application' && { meta: [{ text: cleanMeta(option.type) }] }),
})),
]);
},
[isMounted, _setOptions]
);
useDebounce(
() => {
let arr: GlobalSearchResult[] = [];
globalSearch(searchValue, {}).subscribe({
next: ({ results }) => {
if (searchValue.length > 0) {
arr = [...results, ...arr].sort((a, b) => {
if (a.score < b.score) return 1;
if (a.score > b.score) return -1;
return 0;
});
setOptions(arr);
return;
}
// if searchbar is empty, filter to only applications and sort alphabetically
results = results.filter(({ type }: GlobalSearchResult) => type === 'application');
arr = [...results, ...arr].sort((a, b) => {
const titleA = a.title.toUpperCase(); // ignore upper and lowercase
const titleB = b.title.toUpperCase(); // ignore upper and lowercase
if (titleA < titleB) return -1;
if (titleA > titleB) return 1;
return 0;
});
setOptions(arr);
},
error: () => {
// TODO #74430 - add telemetry to see if errors are happening
// Not doing anything on error right now because it'll either just show the previous
// results or empty results which is basically what we want anyways
},
complete: () => {},
});
},
250,
[searchValue]
);
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === '/' && (isMac ? event.metaKey : event.ctrlKey)) {
if (searchRef) {
event.preventDefault();
searchRef.focus();
}
}
};
const onChange = (selected: EuiSelectableTemplateSitewideOption[]) => {
// @ts-ignore - ts error is "union type is too complex to express"
const { url } = selected.find(({ checked }) => checked === 'on');
navigateToUrl(url);
(document.activeElement as HTMLElement).blur();
if (searchRef) {
clearField(searchRef);
searchRef.dispatchEvent(blurEvent);
}
};
useEvent('keydown', onKeyDown);
return (
<EuiSelectableTemplateSitewide
onChange={onChange}
options={options}
searchProps={{
onSearch: setSearchValue,
'data-test-subj': 'header-search',
inputRef: setSearchRef,
compressed: true,
placeholder: i18n.translate('xpack.globalSearchBar.searchBar.placeholder', {
defaultMessage: 'Search Elastic',
}),
}}
emptyMessage={
<EuiSelectableMessage style={{ minHeight: 300 }}>
<p>
<FormattedMessage
id="xpack.globalSearchBar.searchBar.noResultsHeading"
defaultMessage="No results found"
/>
</p>
<p>
<FormattedMessage
id="xpack.globalSearchBar.searchBar.noResults"
defaultMessage="Try searching for applications and saved objects by name."
/>
</p>
</EuiSelectableMessage>
}
popoverFooter={
<EuiText color="subdued" size="xs">
<EuiFlexGroup
alignItems="center"
justifyContent="flexEnd"
gutterSize="s"
responsive={false}
wrap
>
<FormattedMessage
id="xpack.globalSearchBar.searchBar.shortcutDescription.shortcutDetail"
defaultMessage="{shortcutDescription}{commandDescription}"
values={{
shortcutDescription: (
<EuiFlexItem grow={false}>
<FormattedMessage
id="xpack.globalSearchBar.searchBar.shortcutDescription.shortcutInstructionDescription"
defaultMessage="Shortcut"
/>
</EuiFlexItem>
),
commandDescription: (
<EuiFlexItem grow={false}>
<EuiBadge>
{isMac ? (
<FormattedMessage
id="xpack.globalSearchBar.searchBar.shortcutDescription.macCommandDescription"
defaultMessage="Command + /"
/>
) : (
<FormattedMessage
id="xpack.globalSearchBar.searchBar.shortcutDescription.windowsCommandDescription"
defaultMessage="Control + /"
/>
)}
</EuiBadge>
</EuiFlexItem>
),
}}
/>
<FormattedMessage
id="xpack.globalSearchBar.searchBar.shortcut"
defaultMessage="{what}{how}"
values={{
what: <EuiFlexItem grow={false}>Shortcut</EuiFlexItem>,
how: (
<EuiFlexItem grow={false}>
<EuiBadge>{isMac ? 'Command + /' : 'Control + /'}</EuiBadge>
</EuiFlexItem>
),
}}
/>
</EuiFlexGroup>
</EuiText>
}
/>
);
}

View file

@ -0,0 +1,10 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { PluginInitializer } from 'src/core/public';
import { GlobalSearchBarPlugin } from './plugin';
export const plugin: PluginInitializer<{}, {}, {}, {}> = () => new GlobalSearchBarPlugin();

View file

@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { CoreStart, Plugin } from 'src/core/public';
import React from 'react';
import { I18nProvider } from '@kbn/i18n/react';
import ReactDOM from 'react-dom';
import { ApplicationStart } from 'kibana/public';
import { SearchBar } from '../public/components/search_bar';
import { GlobalSearchPluginStart } from '../../global_search/public';
export interface GlobalSearchBarPluginStartDeps {
globalSearch: GlobalSearchPluginStart;
}
export class GlobalSearchBarPlugin implements Plugin<{}, {}> {
public async setup() {
return {};
}
public start(core: CoreStart, { globalSearch }: GlobalSearchBarPluginStartDeps) {
core.chrome.navControls.registerCenter({
order: 1000,
mount: (target) => this.mount(target, globalSearch, core.application.navigateToUrl),
});
return {};
}
private mount(
targetDomElement: HTMLElement,
globalSearch: GlobalSearchPluginStart,
navigateToUrl: ApplicationStart['navigateToUrl']
) {
ReactDOM.render(
<I18nProvider>
<SearchBar globalSearch={globalSearch.find} navigateToUrl={navigateToUrl} />
</I18nProvider>,
targetDomElement
);
return () => ReactDOM.unmountComponentAtNode(targetDomElement);
}
}

View file

@ -1,6 +1,6 @@
<main id="graphBasic" ng-controller="graphuiPlugin" aria-labelledby="graphHeading">
<!-- Local nav. -->
<kbn-top-nav name="workspacesTopNav" config="topNavMenu">
<kbn-top-nav name="workspacesTopNav" config="topNavMenu" set-menu-mount-point="setHeaderActionMenu">
</kbn-top-nav>
<div class="gphGraph__menus" ng-show="menus.showInspect">

View file

@ -53,6 +53,7 @@ export function initGraphApp(angularModule, deps) {
graphSavePolicy,
overlays,
savedObjects,
setHeaderActionMenu,
} = deps;
const app = angularModule;
@ -465,6 +466,7 @@ export function initGraphApp(angularModule, deps) {
};
// ===== Menubar configuration =========
$scope.setHeaderActionMenu = setHeaderActionMenu;
$scope.topNavMenu = [];
$scope.topNavMenu.push({
key: 'new',

View file

@ -27,6 +27,7 @@ import {
SavedObjectsClientContract,
ToastsStart,
OverlayStart,
AppMountParameters,
} from 'kibana/public';
// @ts-ignore
import { initGraphApp } from './app';
@ -73,6 +74,7 @@ export interface GraphDependencies {
overlays: OverlayStart;
savedObjects: SavedObjectsStart;
kibanaLegacy: KibanaLegacyStart;
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
}
export const renderApp = ({ appBasePath, element, kibanaLegacy, ...deps }: GraphDependencies) => {

View file

@ -1,3 +1,3 @@
.gphGraph__bar {
margin: 0px $euiSizeS $euiSizeS $euiSizeS;
margin: $euiSizeS;
}

View file

@ -74,7 +74,7 @@ export class GraphPlugin
title: 'Graph',
order: 6000,
appRoute: '/app/graph',
euiIconType: 'graphApp',
euiIconType: 'logoKibana',
category: DEFAULT_APP_CATEGORIES.kibana,
mount: async (params: AppMountParameters) => {
const [coreStart, pluginsStart] = await core.getStartServices();

View file

@ -52,7 +52,7 @@ export class Plugin implements InfraClientPluginClass {
title: i18n.translate('xpack.infra.logs.pluginTitle', {
defaultMessage: 'Logs',
}),
euiIconType: 'logsApp',
euiIconType: 'logoObservability',
order: 8100,
appRoute: '/app/logs',
category: DEFAULT_APP_CATEGORIES.observability,
@ -70,7 +70,7 @@ export class Plugin implements InfraClientPluginClass {
title: i18n.translate('xpack.infra.metrics.pluginTitle', {
defaultMessage: 'Metrics',
}),
euiIconType: 'metricsApp',
euiIconType: 'logoObservability',
order: 8200,
appRoute: '/app/metrics',
category: DEFAULT_APP_CATEGORIES.observability,

View file

@ -5,11 +5,11 @@
*/
import React from 'react';
import styled from 'styled-components';
import { EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiIcon, EuiButtonEmpty } from '@elastic/eui';
import { EuiTabs, EuiTab, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { Section } from '../sections';
import { AlphaMessaging, SettingFlyout } from '../components';
import { useLink, useConfig, useCore } from '../hooks';
import { useLink, useConfig } from '../hooks';
interface Props {
showSettings?: boolean;
@ -42,7 +42,6 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({
}) => {
const { getHref } = useLink();
const { fleet } = useConfig();
const { uiSettings } = useCore();
const [isSettingsFlyoutOpen, setIsSettingsFlyoutOpen] = React.useState(false);
return (
@ -58,11 +57,6 @@ export const DefaultLayout: React.FunctionComponent<Props> = ({
<div>
<Nav>
<EuiFlexGroup gutterSize="l" alignItems="center">
{uiSettings.get('pageNavigation') === 'legacy' ? (
<EuiFlexItem grow={false}>
<EuiIcon type="savedObjectsApp" size="l" />
</EuiFlexItem>
) : null}
<EuiFlexItem>
<EuiTabs display="condensed">
<EuiTab isSelected={section === 'overview'} href={getHref('overview')}>

View file

@ -56,8 +56,6 @@ const StepsWithLessPadding = styled(EuiSteps)`
export const CreatePackagePolicyPage: React.FunctionComponent = () => {
const {
notifications,
chrome: { getIsNavDrawerLocked$ },
uiSettings,
application: { navigateToApp },
} = useCore();
const {
@ -70,15 +68,6 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => {
const history = useHistory();
const routeState = useIntraAppState<CreatePackagePolicyRouteState>();
const from: CreatePackagePolicyFrom = policyId ? 'policy' : 'package';
const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false);
useEffect(() => {
const subscription = getIsNavDrawerLocked$().subscribe((newIsNavDrawerLocked: boolean) => {
setIsNavDrawerLocked(newIsNavDrawerLocked);
});
return () => subscription.unsubscribe();
});
// Agent policy and package info states
const [agentPolicy, setAgentPolicy] = useState<AgentPolicy>();
@ -398,16 +387,7 @@ export const CreatePackagePolicyPage: React.FunctionComponent = () => {
)}
<StepsWithLessPadding steps={steps} />
<EuiSpacer size="l" />
{/* TODO #64541 - Remove classes */}
<EuiBottomBar
className={
uiSettings.get('pageNavigation') === 'legacy'
? isNavDrawerLocked
? 'ingestManager__bottomBar-isNavDrawerLocked'
: 'ingestManager__bottomBar'
: undefined
}
>
<EuiBottomBar>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
{!isLoadingSecondStep && agentPolicy && packageInfo && formState === 'INVALID' ? (

View file

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React, { memo, useState, useEffect } from 'react';
import React, { memo, useState } from 'react';
import { useHistory } from 'react-router-dom';
import styled from 'styled-components';
import { EuiBottomBar, EuiFlexGroup, EuiFlexItem, EuiButtonEmpty, EuiButton } from '@elastic/eui';
@ -33,11 +33,7 @@ const FormWrapper = styled.div`
export const SettingsView = memo<{ agentPolicy: AgentPolicy }>(
({ agentPolicy: originalAgentPolicy }) => {
const {
notifications,
chrome: { getIsNavDrawerLocked$ },
uiSettings,
} = useCore();
const { notifications } = useCore();
const {
fleet: { enabled: isFleetEnabled },
} = useConfig();
@ -45,7 +41,6 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>(
const { getPath } = useLink();
const hasWriteCapabilites = useCapabilities().write;
const refreshAgentPolicy = useAgentPolicyRefresh();
const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false);
const [agentPolicy, setAgentPolicy] = useState<AgentPolicy>({
...originalAgentPolicy,
});
@ -55,14 +50,6 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>(
const [withSysMonitoring, setWithSysMonitoring] = useState<boolean>(true);
const validation = agentPolicyFormValidation(agentPolicy);
useEffect(() => {
const subscription = getIsNavDrawerLocked$().subscribe((newIsNavDrawerLocked: boolean) => {
setIsNavDrawerLocked(newIsNavDrawerLocked);
});
return () => subscription.unsubscribe();
});
const updateAgentPolicy = (updatedFields: Partial<AgentPolicy>) => {
setAgentPolicy({
...agentPolicy,
@ -152,17 +139,9 @@ export const SettingsView = memo<{ agentPolicy: AgentPolicy }>(
history.push(getPath('policies_list'));
}}
/>
{/* TODO #64541 - Remove classes */}
{hasChanges ? (
<EuiBottomBar
className={
uiSettings.get('pageNavigation') === 'legacy'
? isNavDrawerLocked
? 'ingestManager__bottomBar-isNavDrawerLocked'
: 'ingestManager__bottomBar'
: undefined
}
>
<EuiBottomBar>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem>
<FormattedMessage

View file

@ -43,11 +43,7 @@ import { StepConfigurePackagePolicy } from '../create_package_policy_page/step_c
import { StepDefinePackagePolicy } from '../create_package_policy_page/step_define_package_policy';
export const EditPackagePolicyPage: React.FunctionComponent = () => {
const {
notifications,
chrome: { getIsNavDrawerLocked$ },
uiSettings,
} = useCore();
const { notifications } = useCore();
const {
fleet: { enabled: isFleetEnabled },
} = useConfig();
@ -56,15 +52,6 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
} = useRouteMatch<{ policyId: string; packagePolicyId: string }>();
const history = useHistory();
const { getHref, getPath } = useLink();
const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false);
useEffect(() => {
const subscription = getIsNavDrawerLocked$().subscribe((newIsNavDrawerLocked: boolean) => {
setIsNavDrawerLocked(newIsNavDrawerLocked);
});
return () => subscription.unsubscribe();
});
// Agent policy, package info, and package policy states
const [isLoadingData, setIsLoadingData] = useState<boolean>(true);
@ -346,16 +333,7 @@ export const EditPackagePolicyPage: React.FunctionComponent = () => {
)}
{configurePackage}
<EuiSpacer size="l" />
{/* TODO #64541 - Remove classes */}
<EuiBottomBar
className={
uiSettings.get('pageNavigation') === 'legacy'
? isNavDrawerLocked
? 'ingestManager__bottomBar-isNavDrawerLocked'
: 'ingestManager__bottomBar'
: undefined
}
>
<EuiBottomBar>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
{agentPolicy && packageInfo && formState === 'INVALID' ? (

View file

@ -77,7 +77,7 @@ export class IngestManagerPlugin
category: DEFAULT_APP_CATEGORIES.management,
title: i18n.translate('xpack.ingestManager.appTitle', { defaultMessage: 'Ingest Manager' }),
order: 9020,
euiIconType: 'savedObjectsApp',
euiIconType: 'logoElastic',
async mount(params: AppMountParameters) {
const [coreStart, startDeps] = (await core.getStartServices()) as [
CoreStart,

View file

@ -135,6 +135,7 @@ describe('Lens App', () => {
redirectTo: (id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => void;
originatingApp: string | undefined;
onAppLeave: AppMountParameters['onAppLeave'];
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
history: History;
getAppNameFromId?: (appId: string) => string | undefined;
}> {
@ -175,6 +176,7 @@ describe('Lens App', () => {
},
redirectTo: jest.fn((id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => {}),
onAppLeave: jest.fn(),
setHeaderActionMenu: jest.fn(),
history: createMemoryHistory(),
} as unknown) as jest.Mocked<{
navigation: typeof navigationStartMock;
@ -187,6 +189,7 @@ describe('Lens App', () => {
redirectTo: (id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => void;
originatingApp: string | undefined;
onAppLeave: AppMountParameters['onAppLeave'];
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
history: History;
getAppNameFromId?: (appId: string) => string | undefined;
}>;

View file

@ -72,6 +72,7 @@ export function App({
originatingApp,
navigation,
onAppLeave,
setHeaderActionMenu,
history,
getAppNameFromId,
}: {
@ -85,6 +86,7 @@ export function App({
redirectTo: (id?: string, returnToOrigin?: boolean, newlyCreated?: boolean) => void;
originatingApp?: string | undefined;
onAppLeave: AppMountParameters['onAppLeave'];
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
history: History;
getAppNameFromId?: (appId: string) => string | undefined;
}) {
@ -419,6 +421,7 @@ export function App({
<div className="lnsApp">
<div className="lnsApp__header">
<TopNavMenu
setMenuMountPoint={setHeaderActionMenu}
config={[
...(!!state.originatingApp && lastKnownDoc?.id
? [

View file

@ -89,6 +89,7 @@ export async function mountApp(
originatingApp={originatingApp}
getAppNameFromId={stateTransfer.getAppNameFromId}
onAppLeave={params.onAppLeave}
setHeaderActionMenu={params.setHeaderActionMenu}
history={routeProps.history}
/>
);

View file

@ -26,6 +26,7 @@ export const EMS_TILES_VECTOR_TILE_PATH = 'vector/tile';
export const MAP_SAVED_OBJECT_TYPE = 'map';
export const APP_ID = 'maps';
export const APP_ICON = 'gisApp';
export const APP_ICON_SOLUTION = 'logoKibana';
export const INITIAL_LAYERS_KEY = 'initialLayers';
export const MAPS_APP_PATH = `app/${APP_ID}`;

View file

@ -1,10 +1,10 @@
@import '@elastic/eui/src/global_styling/variables/header';
@import '../../../../src/core/public/variables';
// sass-lint:disable no-ids
#maps-plugin {
display: flex;
flex-direction: column;
height: calc(100vh - #{$euiHeaderHeightCompensation});
height: calc(100vh - #{$kbnHeaderOffset});
width: 100%;
overflow: hidden;
}

View file

@ -30,7 +30,7 @@ import { featureCatalogueEntry } from './feature_catalogue_entry';
import { getMapsVisTypeAlias } from './maps_vis_type_alias';
import { HomePublicPluginSetup } from '../../../../src/plugins/home/public';
import { VisualizationsSetup } from '../../../../src/plugins/visualizations/public';
import { APP_ICON, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants';
import { APP_ICON_SOLUTION, APP_ID, MAP_SAVED_OBJECT_TYPE } from '../common/constants';
import { VISUALIZE_GEO_FIELD_TRIGGER } from '../../../../src/plugins/ui_actions/public';
import { createMapsUrlGenerator } from './url_generator';
import { visualizeGeoFieldAction } from './trigger_actions/visualize_geo_field_action';
@ -121,7 +121,7 @@ export class MapsPlugin
title: getAppTitle(),
order: 4000,
icon: `plugins/${APP_ID}/icon.svg`,
euiIconType: APP_ICON,
euiIconType: APP_ICON_SOLUTION,
category: DEFAULT_APP_CATEGORIES.kibana,
async mount(context, params) {
const { renderApp } = await lazyLoadMapModules();

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