mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
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:
parent
71e52d2e1d
commit
1868e6ba9c
120 changed files with 3839 additions and 11261 deletions
304
.github/CODEOWNERS
vendored
Normal file
304
.github/CODEOWNERS
vendored
Normal 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
|
||||
|
598
docs/developer/architecture/code-exploration.asciidoc
Normal file
598
docs/developer/architecture/code-exploration.asciidoc
Normal 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):
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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. |
|
||||
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeNavControls](./kibana-plugin-core-public.chromenavcontrols.md) > [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`
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ChromeStart](./kibana-plugin-core-public.chromestart.md) > [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>`
|
||||
|
|
@ -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 |
|
||||
|
|
|
@ -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(),
|
||||
|
||||
|
|
3
src/core/public/_variables.scss
Normal file
3
src/core/public/_variables.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
@import '@elastic/eui/src/global_styling/variables/header';
|
||||
|
||||
$kbnHeaderOffset: $euiHeaderHeightCompensation * 2;
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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$)
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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}
|
||||
>
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
|
|
139
src/core/public/chrome/ui/header/header_action_menu.test.tsx
Normal file
139
src/core/public/chrome/ui/header/header_action_menu.test.tsx
Normal 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();
|
||||
});
|
||||
});
|
64
src/core/public/chrome/ui/header/header_action_menu.tsx
Normal file
64
src/core/public/chrome/ui/header/header_action_menu.tsx
Normal 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} />;
|
||||
};
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
))}
|
||||
|
|
|
@ -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);
|
|
@ -1,6 +1,6 @@
|
|||
@import './variables';
|
||||
@import './core';
|
||||
@import './chrome/index';
|
||||
@import './overlays/index';
|
||||
@import './rendering/index';
|
||||
@import './styles/index';
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
> * {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
@import './form/index';
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
@import './form';
|
|
@ -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"
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
@import './advanced_settings';
|
||||
|
||||
@import './components/index';
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
.dshAppContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%; // TODO #64541 - can delete this
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -16,10 +16,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.devApp {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.devAppWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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' }],
|
||||
|
|
|
@ -19,3 +19,4 @@
|
|||
|
||||
export { toMountPoint } from './to_mount_point';
|
||||
export { MountPointPortal } from './mount_point_portal';
|
||||
export { useIfMounted } from './utils';
|
||||
|
|
|
@ -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]);
|
||||
|
||||
|
|
38
src/plugins/kibana_react/public/util/utils.ts
Normal file
38
src/plugins/kibana_react/public/util/utils.ts
Normal 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;
|
||||
};
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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">
|
||||
▪
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
@import '@elastic/eui/src/global_styling/variables/header';
|
||||
|
||||
.timApp {
|
||||
position: relative;
|
||||
min-height: calc(100vh - #{$euiHeaderChildSize});
|
||||
background: $euiColorEmptyShade;
|
||||
|
||||
[ng-click] {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>) {
|
||||
|
|
|
@ -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>
|
||||
`;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -20,4 +20,5 @@ export const platformService: PlatformService = {
|
|||
getSavedObjects: noop,
|
||||
getSavedObjectsClient: noop,
|
||||
getUISettings: noop,
|
||||
setFullscreen: noop,
|
||||
};
|
||||
|
|
|
@ -29,6 +29,7 @@ export const ENTERPRISE_SEARCH_PLUGIN = {
|
|||
}),
|
||||
],
|
||||
URL: '/app/enterprise_search/overview',
|
||||
LOGO: 'logoEnterpriseSearch',
|
||||
};
|
||||
|
||||
export const APP_SEARCH_PLUGIN = {
|
||||
|
|
|
@ -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) => {
|
||||
|
|
3
x-pack/plugins/global_search_bar/README.md
Normal file
3
x-pack/plugins/global_search_bar/README.md
Normal 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.)
|
10
x-pack/plugins/global_search_bar/kibana.json
Normal file
10
x-pack/plugins/global_search_bar/kibana.json
Normal 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"]
|
||||
}
|
75
x-pack/plugins/global_search_bar/public/components/__snapshots__/search_bar.test.tsx.snap
generated
Normal file
75
x-pack/plugins/global_search_bar/public/components/__snapshots__/search_bar.test.tsx.snap
generated
Normal 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=""
|
||||
/>
|
||||
`;
|
|
@ -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();
|
||||
});
|
||||
});
|
|
@ -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>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
10
x-pack/plugins/global_search_bar/public/index.ts
Normal file
10
x-pack/plugins/global_search_bar/public/index.ts
Normal 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();
|
46
x-pack/plugins/global_search_bar/public/plugin.tsx
Normal file
46
x-pack/plugins/global_search_bar/public/plugin.tsx
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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">
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
.gphGraph__bar {
|
||||
margin: 0px $euiSizeS $euiSizeS $euiSizeS;
|
||||
margin: $euiSizeS;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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')}>
|
||||
|
|
|
@ -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' ? (
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' ? (
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}>;
|
||||
|
|
|
@ -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
|
||||
? [
|
||||
|
|
|
@ -89,6 +89,7 @@ export async function mountApp(
|
|||
originatingApp={originatingApp}
|
||||
getAppNameFromId={stateTransfer.getAppNameFromId}
|
||||
onAppLeave={params.onAppLeave}
|
||||
setHeaderActionMenu={params.setHeaderActionMenu}
|
||||
history={routeProps.history}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -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}`;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue