From e0f8e27c3b0cca82e9c4edc2d4aa92f0bc551059 Mon Sep 17 00:00:00 2001 From: Junqiu Lei Date: Mon, 6 Feb 2023 01:22:38 -0800 Subject: [PATCH] Add map as embeddable to dashboard Signed-off-by: Junqiu Lei --- common/constants/shared.ts | 4 +- common/index.ts | 7 +- opensearch_dashboards.json | 2 +- package-lock.json | 249 ++++++++++++++++++ .../add_layer_panel/add_layer_panel.tsx | 52 ++-- public/components/app.tsx | 16 +- .../layer_control_panel.tsx | 131 +++++---- .../map_container/map_container.scss | 8 +- .../map_container/map_container.tsx | 32 ++- public/components/map_page/index.ts | 2 +- public/components/map_page/map_page.tsx | 58 ++-- .../components/map_top_nav/top_nav_menu.tsx | 23 +- public/components/maps_list/maps_list.tsx | 6 +- public/embeddable/index.ts | 7 + public/embeddable/map_component.tsx | 48 ++++ public/embeddable/map_embeddable.tsx | 91 +++++++ public/embeddable/map_embeddable_factory.tsx | 103 ++++++++ public/model/layerRenderController.ts | 17 +- public/plugin.tsx | 34 ++- public/types.ts | 12 +- public/utils/breadcrumbs.ts | 4 +- 21 files changed, 762 insertions(+), 144 deletions(-) create mode 100644 package-lock.json create mode 100644 public/embeddable/index.ts create mode 100644 public/embeddable/map_component.tsx create mode 100644 public/embeddable/map_embeddable.tsx create mode 100644 public/embeddable/map_embeddable_factory.tsx diff --git a/common/constants/shared.ts b/common/constants/shared.ts index 7d1ed801..1fe05b96 100644 --- a/common/constants/shared.ts +++ b/common/constants/shared.ts @@ -12,5 +12,5 @@ export const MAX_FILE_PAYLOAD_SIZE_IN_MB = 25; export const MAX_FILE_PAYLOAD_SIZE = fromMBtoBytes(MAX_FILE_PAYLOAD_SIZE_IN_MB); export const PLUGIN_ID = 'customImportMap'; export const PLUGIN_NAME = 'customImportMap'; -export const PLUGIN_NAVIGATION_BAR_TILE = 'Maps'; -export const PLUGIN_NAVIGATION_BAR_ID = 'maps-dashboards'; +export const MAPS_APP_DISPLAY_NAME = 'Maps'; +export const MAPS_APP_ID = 'maps-dashboards'; diff --git a/common/index.ts b/common/index.ts index 80f243ba..5fc0986e 100644 --- a/common/index.ts +++ b/common/index.ts @@ -9,7 +9,7 @@ import { MAX_FILE_PAYLOAD_SIZE, MAX_FILE_PAYLOAD_SIZE_IN_MB, PLUGIN_ID, - PLUGIN_NAVIGATION_BAR_ID, + MAPS_APP_ID, PLUGIN_NAME, } from './constants/shared'; @@ -19,7 +19,7 @@ export { MAX_FILE_PAYLOAD_SIZE, MAX_FILE_PAYLOAD_SIZE_IN_MB, PLUGIN_ID, - PLUGIN_NAVIGATION_BAR_ID, + MAPS_APP_ID, PLUGIN_NAME, }; @@ -44,6 +44,9 @@ export const MAX_LAYER_NAME_LIMIT = 35; export const MAX_LONGITUDE = 180; export const MIN_LONGITUDE = -180; export const NEW_MAP_LAYER_DEFAULT_PREFIX = 'New layer'; +export const MAP_SAVED_OBJECT_TYPE = 'map'; +// TODO: Replace with actual app icon +export const MAPS_APP_ICON = 'globe'; // Starting position [lng, lat] and zoom export const MAP_INITIAL_STATE = { diff --git a/opensearch_dashboards.json b/opensearch_dashboards.json index 1723bfde..4ad5ffea 100644 --- a/opensearch_dashboards.json +++ b/opensearch_dashboards.json @@ -4,6 +4,6 @@ "opensearchDashboardsVersion": "3.0.0", "server": true, "ui": true, - "requiredPlugins": ["regionMap", "opensearchDashboardsReact", "navigation", "savedObjects", "data"], + "requiredPlugins": ["regionMap", "opensearchDashboardsReact", "opensearchDashboardsUtils", "navigation", "savedObjects", "data", "embeddable"], "optionalPlugins": [] } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..b786a608 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,249 @@ +{ + "name": "mapsDashboards", + "version": "3.0.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@mapbox/geojson-rewind": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", + "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", + "requires": { + "get-stream": "^6.0.1", + "minimist": "^1.2.6" + } + }, + "@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==" + }, + "@mapbox/mapbox-gl-supported": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-2.0.1.tgz", + "integrity": "sha512-HP6XvfNIzfoMVfyGjBckjiAOQK9WfX0ywdLubuPMPv+Vqf5fj0uCbgBQYpiqcWZT6cbyyRnTSXDheT1ugvF6UQ==" + }, + "@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==" + }, + "@mapbox/tiny-sdf": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.0.5.tgz", + "integrity": "sha512-OhXt2lS//WpLdkqrzo/KwB7SRD8AiNTFFzuo9n14IBupzIMa67yGItcK7I2W9D8Ghpa4T04Sw9FWsKCJG50Bxw==" + }, + "@mapbox/unitbezier": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.1.tgz", + "integrity": "sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==" + }, + "@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "requires": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==" + }, + "@types/geojson": { + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==" + }, + "@types/mapbox__point-geometry": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.2.tgz", + "integrity": "sha512-D0lgCq+3VWV85ey1MZVkE8ZveyuvW5VAfuahVTQRpXFQTxw03SuIf1/K4UQ87MMIXVKzpFjXFiFMZzLj2kU+iA==" + }, + "@types/mapbox__vector-tile": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.0.tgz", + "integrity": "sha512-kDwVreQO5V4c8yAxzZVQLE5tyWF+IPToAanloQaSnwfXmIcJ7cyOrv8z4Ft4y7PsLYmhWXmON8MBV8RX0Rgr8g==", + "requires": { + "@types/geojson": "*", + "@types/mapbox__point-geometry": "*", + "@types/pbf": "*" + } + }, + "@types/pbf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.2.tgz", + "integrity": "sha512-EDrLIPaPXOZqDjrkzxxbX7UlJSeQVgah3i0aA4pOSzmK9zq3BIh7/MZIQxED7slJByvKM4Gc6Hypyu2lJzh3SQ==" + }, + "csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha512-umPSgYwZkdFoUrH5hIq5kf0wPSXiro51nPw0j2K/c83KflkPSTBGMz6NJvMB+07VlL0y7VPo6QJcDjcgKTTm3w==" + }, + "earcut": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.4.tgz", + "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==" + }, + "geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==" + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" + }, + "gl-matrix": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.3.tgz", + "integrity": "sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==" + }, + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "kdbush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", + "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==" + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "maplibre-gl": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-2.4.0.tgz", + "integrity": "sha512-csNFylzntPmHWidczfgCZpvbTSmhaWvLRj9e1ezUDBEPizGgshgm3ea1T5TCNEEBq0roauu7BPuRZjA3wO4KqA==", + "requires": { + "@mapbox/geojson-rewind": "^0.5.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^2.0.1", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^2.0.5", + "@mapbox/unitbezier": "^0.0.1", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "@types/geojson": "^7946.0.10", + "@types/mapbox__point-geometry": "^0.1.2", + "@types/mapbox__vector-tile": "^1.3.0", + "@types/pbf": "^3.0.2", + "csscolorparser": "~1.0.3", + "earcut": "^2.2.4", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.4.3", + "global-prefix": "^3.0.0", + "murmurhash-js": "^1.0.0", + "pbf": "^3.2.1", + "potpack": "^1.0.2", + "quickselect": "^2.0.0", + "supercluster": "^7.1.5", + "tinyqueue": "^2.0.3", + "vt-pbf": "^3.1.3" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha512-TvmkNhkv8yct0SVBSy+o8wYzXjE4Zz3PCesbfs8HiCXXdcTuocApFv11UWlNFWKYsP2okqrhb7JNlSm9InBhIw==" + }, + "pbf": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.1.tgz", + "integrity": "sha512-ClrV7pNOn7rtmoQVF4TS1vyU0WhYRnP92fzbfF75jAIwpnzdJXf8iTd4CMEqO4yUenH6NDqLiwjqlh6QgZzgLQ==", + "requires": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + } + }, + "potpack": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.2.tgz", + "integrity": "sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==" + }, + "protocol-buffers-schema": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.6.0.tgz", + "integrity": "sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==" + }, + "quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, + "resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "requires": { + "protocol-buffers-schema": "^3.3.1" + } + }, + "supercluster": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-7.1.5.tgz", + "integrity": "sha512-EulshI3pGUM66o6ZdH3ReiFcvHpM3vAigyK+vcxdjpJyEbIIrtbmBdY23mGgnI24uXiGFvrGq9Gkum/8U7vJWg==", + "requires": { + "kdbush": "^3.0.0" + } + }, + "tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + }, + "vt-pbf": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", + "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", + "requires": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.2.1" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } + } +} diff --git a/public/components/add_layer_panel/add_layer_panel.tsx b/public/components/add_layer_panel/add_layer_panel.tsx index c06aa078..59838467 100644 --- a/public/components/add_layer_panel/add_layer_panel.tsx +++ b/public/components/add_layer_panel/add_layer_panel.tsx @@ -41,6 +41,7 @@ interface Props { newLayerIndex: number; mapConfig: ConfigSchema; layerCount: number; + inDashboardMode: boolean; } export const AddLayerPanel = ({ @@ -52,6 +53,7 @@ export const AddLayerPanel = ({ newLayerIndex, mapConfig, layerCount, + inDashboardMode, }: Props) => { const [isAddNewLayerModalVisible, setIsAddNewLayerModalVisible] = useState(false); const [highlightItem, setHighlightItem] = useState(null); @@ -109,31 +111,33 @@ export const AddLayerPanel = ({ return (
- - - {isMaxLayerLimitReached() - ? `You've added the maximum number of layers (${MAX_LAYER_LIMIT}).` - : 'Add layer'} -

- } - > - + + {isMaxLayerLimitReached() + ? `You've added the maximum number of layers (${MAX_LAYER_LIMIT}).` + : 'Add layer'} +

+ } > - Add layer -
-
-
+ + Add layer + + + + )} {isAddNewLayerModalVisible && ( diff --git a/public/components/app.tsx b/public/components/app.tsx index 79e07c1c..a0122f46 100644 --- a/public/components/app.tsx +++ b/public/components/app.tsx @@ -24,15 +24,13 @@ export const MapsDashboardsApp = ({ mapConfig }: Props) => { return ( -
- - } - /> - } /> - -
+ + } + /> + } /> +
); diff --git a/public/components/layer_control_panel/layer_control_panel.tsx b/public/components/layer_control_panel/layer_control_panel.tsx index e77a8b26..4c5c25db 100644 --- a/public/components/layer_control_panel/layer_control_panel.tsx +++ b/public/components/layer_control_panel/layer_control_panel.tsx @@ -25,11 +25,12 @@ import { I18nProvider } from '@osd/i18n/react'; import { Map as Maplibre } from 'maplibre-gl'; import './layer_control_panel.scss'; import { isEqual } from 'lodash'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; +import { IndexPattern, TimeRange } from '../../../../../src/plugins/data/public'; import { AddLayerPanel } from '../add_layer_panel'; import { LayerConfigPanel } from '../layer_config'; import { MapLayerSpecification } from '../../model/mapLayerType'; import { + DASHBOARDS_MAPS_LAYER_TYPE, LAYER_ICON_TYPE_MAP, LAYER_PANEL_HIDE_LAYER_ICON, LAYER_PANEL_SHOW_LAYER_ICON, @@ -59,6 +60,8 @@ interface Props { mapState: MapState; zoom: number; mapConfig: ConfigSchema; + inDashboardMode: boolean; + timeRange?: TimeRange; } export const LayerControlPanel = memo( @@ -71,6 +74,8 @@ export const LayerControlPanel = memo( mapState, zoom, mapConfig, + inDashboardMode, + timeRange, }: Props) => { const { services } = useOpenSearchDashboards(); const { @@ -94,6 +99,15 @@ export const LayerControlPanel = memo( const [visibleLayers, setVisibleLayers] = useState([]); useEffect(() => { + if (timeRange) { + layers.forEach((layer: MapLayerSpecification) => { + if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.OPENSEARCH_MAP) { + return; + } + handleDataLayerRender(layer, mapState, services, maplibreRef, undefined, timeRange); + }); + } + if (!isUpdatingLayerRender && initialLayersLoaded) { return; } @@ -118,13 +132,13 @@ export const LayerControlPanel = memo( if (referenceLayerTypeLookup[layer.type]) { handleReferenceLayerRender(layer, maplibreRef, beforeLayerId); } else { - handleDataLayerRender(layer, mapState, services, maplibreRef, beforeLayerId); + handleDataLayerRender(layer, mapState, services, maplibreRef, beforeLayerId, timeRange); } }); setInitialLayersLoaded(true); } setIsUpdatingLayerRender(false); - }, [layers]); + }, [layers, timeRange]); useEffect(() => { const getCurrentVisibleLayers = () => { @@ -196,6 +210,9 @@ export const LayerControlPanel = memo( }; const onClickLayerName = (layer: MapLayerSpecification) => { + if (inDashboardMode) { + return; + } if (hasUnsavedChanges()) { notifications.toasts.addWarning( `You have unsaved changes for ${selectedLayerConfig?.name}` @@ -373,7 +390,11 @@ export const LayerControlPanel = memo( - + {getReverseLayers().map((layer, index) => { const isLayerSelected = isLayerConfigVisible && @@ -386,6 +407,7 @@ export const LayerControlPanel = memo( index={index} draggableId={layer.id} customDragHandle={true} + isDragDisabled={inDashboardMode} > {(provided) => (
@@ -423,54 +445,58 @@ export const LayerControlPanel = memo( /> - - - onLayerVisibilityChange(layer)} - aria-label="Hide or show layer" - color="text" - title={ - layerVisibility.get(layer.id) ? 'Hide layer' : 'Show layer' - } - /> - - - onDeleteLayerIconClick(layer)} - aria-label="Delete layer" - color={layer.id === selectedLayerConfig?.id ? 'text' : 'danger'} - title="Delete layer" - disabled={layer.id === selectedLayerConfig?.id} - /> - - - - - + {!inDashboardMode && ( + + + onLayerVisibilityChange(layer)} + aria-label="Hide or show layer" + color="text" + title={ + layerVisibility.get(layer.id) ? 'Hide layer' : 'Show layer' + } + /> + + + onDeleteLayerIconClick(layer)} + aria-label="Delete layer" + color={ + layer.id === selectedLayerConfig?.id ? 'text' : 'danger' + } + title="Delete layer" + disabled={layer.id === selectedLayerConfig?.id} + /> + + + + + + )}
@@ -503,6 +529,7 @@ export const LayerControlPanel = memo( setIsNewLayer={setIsNewLayer} mapConfig={mapConfig} layerCount={layers.length} + inDashboardMode={inDashboardMode} /> {deleteLayerModal} diff --git a/public/components/map_container/map_container.scss b/public/components/map_container/map_container.scss index 7453ca94..a4ee3065 100644 --- a/public/components/map_container/map_container.scss +++ b/public/components/map_container/map_container.scss @@ -6,10 +6,10 @@ @import "maplibre-gl/dist/maplibre-gl.css"; @import "../../variables"; -/* stylelint-disable no-empty-source */ -.map-container { - width: 100%; - min-height: calc(100vh - #{$mapHeaderOffset}); +.mapAppContainer, .map-page, .map-container, .map-main{ + display: flex; + flex-direction: column; + flex: 1; } .maplibregl-ctrl-top-left { diff --git a/public/components/map_container/map_container.tsx b/public/components/map_container/map_container.tsx index 3d833e5d..acdd917c 100644 --- a/public/components/map_container/map_container.tsx +++ b/public/components/map_container/map_container.tsx @@ -6,16 +6,17 @@ import React, { useEffect, useRef, useState } from 'react'; import { EuiPanel } from '@elastic/eui'; import { LngLat, Map as Maplibre, NavigationControl, Popup, MapEventType } from 'maplibre-gl'; -import { debounce } from 'lodash'; +import { debounce, throttle } from 'lodash'; import { LayerControlPanel } from '../layer_control_panel'; import './map_container.scss'; import { MAP_INITIAL_STATE, DASHBOARDS_MAPS_LAYER_TYPE } from '../../../common'; import { MapLayerSpecification } from '../../model/mapLayerType'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; +import { IndexPattern, TimeRange } from '../../../../../src/plugins/data/public'; import { MapState } from '../../model/mapState'; import { createPopup, getPopupLocation, isTooltipEnabledLayer } from '../tooltip/create_tooltip'; import { handleDataLayerRender } from '../../model/layerRenderController'; import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public'; +import { ResizeChecker } from '../../../../../src/plugins/opensearch_dashboards_utils/public'; import { MapServices } from '../../types'; import { ConfigSchema } from '../../../common/config'; @@ -27,6 +28,8 @@ interface MapContainerProps { maplibreRef: React.MutableRefObject; mapState: MapState; mapConfig: ConfigSchema; + inDashboardMode: boolean; + timeRange?: TimeRange; } export const MapContainer = ({ @@ -37,6 +40,8 @@ export const MapContainer = ({ maplibreRef, mapState, mapConfig, + inDashboardMode, + timeRange, }: MapContainerProps) => { const { services } = useOpenSearchDashboards(); const mapContainer = useRef(null); @@ -68,6 +73,25 @@ export const MapContainer = ({ maplibreInstance.on('move', () => { return setZoom(Number(maplibreInstance.getZoom().toFixed(2))); }); + const mapContainerElement: HTMLElement | null = document.querySelector('.map-page'); + let resizeChecker: ResizeChecker; + if (mapContainerElement) { + resizeChecker = new ResizeChecker(mapContainerElement); + if (inDashboardMode) { + resizeChecker.on( + 'resize', + throttle(() => { + maplibreInstance?.resize(); + }, 300) + ); + } + } + return () => { + maplibreInstance.remove(); + if (resizeChecker) { + resizeChecker.destroy(); + } + }; }, []); // Create onClick tooltip for each layer features that has tooltip enabled @@ -166,7 +190,7 @@ export const MapContainer = ({ }, [layers, mapState, services]); return ( -
+
)}
diff --git a/public/components/map_page/index.ts b/public/components/map_page/index.ts index a79e0689..a43e82ae 100644 --- a/public/components/map_page/index.ts +++ b/public/components/map_page/index.ts @@ -3,4 +3,4 @@ * SPDX-License-Identifier: Apache-2.0 */ -export { MapPage } from './map_page'; +export { MapPage, MapComponent } from './map_page'; diff --git a/public/components/map_page/map_page.tsx b/public/components/map_page/map_page.tsx index 26beef27..119dba40 100644 --- a/public/components/map_page/map_page.tsx +++ b/public/components/map_page/map_page.tsx @@ -17,31 +17,39 @@ import { MAP_LAYER_DEFAULT_NAME, OPENSEARCH_MAP_LAYER, } from '../../../common'; +import { MapLayerSpecification } from '../../model/mapLayerType'; import { getLayerConfigMap, getInitialMapState } from '../../utils/getIntialConfig'; -import { IndexPattern } from '../../../../../src/plugins/data/public'; +import { IndexPattern, TimeRange } from '../../../../../src/plugins/data/public'; import { MapState } from '../../model/mapState'; import { ConfigSchema } from '../../../common/config'; interface Props { mapConfig: ConfigSchema; + mapIdFromSavedObject: string; + timeRange?: TimeRange; + inDashboardMode: boolean; } - -export const MapPage = ({ mapConfig }: Props) => { +export const MapComponent = ({ + mapIdFromSavedObject, + mapConfig, + timeRange, + inDashboardMode, +}: Props) => { const { services } = useOpenSearchDashboards(); const { savedObjects: { client: savedObjectsClient }, } = services; const [layers, setLayers] = useState([]); - const { id: mapIdFromUrl } = useParams<{ id: string }>(); - const [savedMapObject, setSavedMapObject] = - useState | null>(); + const [savedMapObject, setSavedMapObject] = useState | null>(); const [layersIndexPatterns, setLayersIndexPatterns] = useState([]); const maplibreRef = useRef(null); const [mapState, setMapState] = useState(getInitialMapState()); useEffect(() => { - if (mapIdFromUrl) { - savedObjectsClient.get('map', mapIdFromUrl).then((res) => { + if (mapIdFromSavedObject) { + savedObjectsClient.get('map', mapIdFromSavedObject).then((res) => { setSavedMapObject(res); const layerList: MapLayerSpecification[] = JSON.parse(res.attributes.layerList as string); const savedMapState: MapState = JSON.parse(res.attributes.mapState as string); @@ -67,16 +75,21 @@ export const MapPage = ({ mapConfig }: Props) => { }, []); return ( -
- +
+ {inDashboardMode ? null : ( + + )} + { maplibreRef={maplibreRef} mapState={mapState} mapConfig={mapConfig} + inDashboardMode={inDashboardMode} + timeRange={timeRange} />
); }; + +export const MapPage = ({ mapConfig }: Props) => { + const { id: mapId } = useParams<{ id: string }>(); + return ( + + ); +}; diff --git a/public/components/map_top_nav/top_nav_menu.tsx b/public/components/map_top_nav/top_nav_menu.tsx index 15b2fbe3..6e0f260e 100644 --- a/public/components/map_top_nav/top_nav_menu.tsx +++ b/public/components/map_top_nav/top_nav_menu.tsx @@ -6,7 +6,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { SimpleSavedObject } from 'opensearch-dashboards/public'; import { IndexPattern, Query, TimeRange } from '../../../../../src/plugins/data/public'; -import { DASHBOARDS_MAPS_LAYER_TYPE, PLUGIN_NAVIGATION_BAR_ID } from '../../../common'; +import { DASHBOARDS_MAPS_LAYER_TYPE, MAPS_APP_ID } from '../../../common'; import { getTopNavConfig } from './get_top_nav_config'; import { useOpenSearchDashboards } from '../../../../../src/plugins/opensearch_dashboards_react/public'; import { MapServices } from '../../types'; @@ -24,11 +24,15 @@ interface MapTopNavMenuProps { maplibreRef: any; mapState: MapState; setMapState: (mapState: MapState) => void; + inDashboardMode: boolean; + timeRange?: TimeRange; } export const MapTopNavMenu = ({ mapIdFromUrl, savedMapObject, + inDashboardMode, + timeRange, layers, layersIndexPatterns, maplibreRef, @@ -90,13 +94,18 @@ export const MapTopNavMenu = ({ }; useEffect(() => { - setDateFrom(mapState.timeRange.from); - setDateTo(mapState.timeRange.to); + if (!inDashboardMode) { + setDateFrom(mapState.timeRange.from); + setDateTo(mapState.timeRange.to); + } else { + setDateFrom(timeRange!.from); + setDateTo(timeRange!.to); + } setQueryConfig(mapState.query); setIsRefreshPaused(mapState.refreshInterval.pause); setRefreshIntervalValue(mapState.refreshInterval.value); refreshDataLayerRender(); - }, [mapState]); + }, [mapState, timeRange]); const onRefreshChange = useCallback( ({ isPaused, refreshInterval }: { isPaused: boolean; refreshInterval: number }) => { @@ -108,7 +117,7 @@ export const MapTopNavMenu = ({ return ( { const { @@ -39,7 +39,7 @@ export const MapsList = () => { }, [docTitle, navigateToApp, setBreadcrumbs]); const navigateToSavedMapPage = (id: string) => { - navigateToApp(PLUGIN_NAVIGATION_BAR_ID, { path: `/${id}` }); + navigateToApp(MAPS_APP_ID, { path: `/${id}` }); }; const tableColumns = [ @@ -70,7 +70,7 @@ export const MapsList = () => { ]; const navigateToCreateMapPage = () => { - navigateToApp(PLUGIN_NAVIGATION_BAR_ID, { path: APP_PATH.CREATE_MAP }); + navigateToApp(MAPS_APP_ID, { path: APP_PATH.CREATE_MAP }); }; const fetchMaps = useCallback(async (): Promise<{ diff --git a/public/embeddable/index.ts b/public/embeddable/index.ts new file mode 100644 index 00000000..9687b6ce --- /dev/null +++ b/public/embeddable/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './map_embeddable'; +export * from './map_embeddable_factory'; diff --git a/public/embeddable/map_component.tsx b/public/embeddable/map_component.tsx new file mode 100644 index 00000000..fdef8ab4 --- /dev/null +++ b/public/embeddable/map_component.tsx @@ -0,0 +1,48 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React, { useEffect, useState } from 'react'; +import { + withEmbeddableSubscription, + EmbeddableOutput, +} from '../../../../src/plugins/embeddable/public'; +import { MapEmbeddable, MapInput } from './map_embeddable'; +import { MapComponent } from '../components/map_page/'; +import { OpenSearchDashboardsContextProvider } from '../../../../src/plugins/opensearch_dashboards_react/public'; +import { MapServices } from '../types'; + +interface Props { + embeddable: MapEmbeddable; + input: MapInput; + output: EmbeddableOutput; +} +export function MapEmbeddableComponentInner({ embeddable, input, output }: Props) { + const { savedObjectId } = input; + const [timeRange, setTimeRange] = useState(input.timeRange); + const services: MapServices = { + ...embeddable.getServiceSettings(), + }; + + useEffect(() => { + setTimeRange(input.timeRange); + }, [input.timeRange]); + + return ( + + + + ); +} + +export const MapEmbeddableComponent = withEmbeddableSubscription< + MapInput, + EmbeddableOutput, + MapEmbeddable +>(MapEmbeddableComponentInner); diff --git a/public/embeddable/map_embeddable.tsx b/public/embeddable/map_embeddable.tsx new file mode 100644 index 00000000..8401df34 --- /dev/null +++ b/public/embeddable/map_embeddable.tsx @@ -0,0 +1,91 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Subscription } from 'rxjs'; +import { MAP_SAVED_OBJECT_TYPE } from '../../common'; +import { + Embeddable, + EmbeddableInput, + EmbeddableOutput, + IContainer, +} from '../../../../src/plugins/embeddable/public'; +import { MapEmbeddableComponent } from './map_component'; +import { ConfigSchema } from '../../common/config'; +import { MapSavedObjectAttributes } from '../../common/map_saved_object_attributes'; + +export const MAP_EMBEDDABLE = MAP_SAVED_OBJECT_TYPE; + +export interface MapInput extends EmbeddableInput { + search?: string; + savedObjectId: string; +} + +export type MapOutput = EmbeddableOutput; + +function getOutput(input: MapInput, editUrl: string, tittle: string): MapOutput { + return { + editable: true, + editUrl, + defaultTitle: tittle, + }; +} + +export class MapEmbeddable extends Embeddable { + public readonly type = MAP_EMBEDDABLE; + private subscription: Subscription; + private node?: HTMLElement; + private mapConfig: ConfigSchema; + private services: any; + + constructor( + initialInput: MapInput, + { + parent, + services, + mapConfig, + editUrl, + savedMapAttributes, + }: { + parent?: IContainer; + services: any; + mapConfig: ConfigSchema; + editUrl: string; + savedMapAttributes: MapSavedObjectAttributes; + } + ) { + super(initialInput, getOutput(initialInput, editUrl, savedMapAttributes.title), parent); + this.mapConfig = mapConfig; + this.services = services; + this.subscription = this.getInput$().subscribe(() => { + this.updateOutput(getOutput(this.input, editUrl, savedMapAttributes.title)); + }); + } + + public render(node: HTMLElement) { + this.node = node; + if (this.node) { + ReactDOM.unmountComponentAtNode(this.node); + } + ReactDOM.render(, node); + } + + public reload() {} + + public destroy() { + super.destroy(); + this.subscription.unsubscribe(); + if (this.node) { + ReactDOM.unmountComponentAtNode(this.node); + } + } + public getServiceSettings() { + return this.services; + } + public getMapConfig() { + return this.mapConfig; + } +} diff --git a/public/embeddable/map_embeddable_factory.tsx b/public/embeddable/map_embeddable_factory.tsx new file mode 100644 index 00000000..c6a6670a --- /dev/null +++ b/public/embeddable/map_embeddable_factory.tsx @@ -0,0 +1,103 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { i18n } from '@osd/i18n'; +import { + IContainer, + EmbeddableFactoryDefinition, + EmbeddableFactory, + ErrorEmbeddable, + SavedObjectEmbeddableInput, +} from '../../../../src/plugins/embeddable/public'; +import { MAP_EMBEDDABLE, MapInput, MapOutput, MapEmbeddable } from './map_embeddable'; +import { APP_PATH, MAPS_APP_ICON, MAPS_APP_ID } from '../../common'; +import { ConfigSchema } from '../../common/config'; +import { MapSavedObjectAttributes } from '../../common/map_saved_object_attributes'; +import { MAPS_APP_DISPLAY_NAME } from '../../common/constants/shared'; + +interface StartServices { + services: { + application: { + getUrlForApp: (appId: string, options?: { path?: string }) => string; + navigateToApp: (appId: string, options?: { path?: string }) => Promise; + }; + savedObjects: { + client: { + get: (type: string, id: string) => Promise; + }; + }; + }; + mapConfig: ConfigSchema; +} + +export type MapEmbeddableFactory = EmbeddableFactory; + +export class MapEmbeddableFactoryDefinition + implements EmbeddableFactoryDefinition +{ + public readonly type = MAP_EMBEDDABLE; + + public readonly savedObjectMetaData = { + name: MAPS_APP_DISPLAY_NAME, + type: MAP_EMBEDDABLE, + getIconForSavedObject: () => MAPS_APP_ICON, + }; + + constructor(private getStartServices: () => Promise) {} + + public async isEditable() { + return true; + } + + public canCreateNew() { + // TODO: allow users to create a new map from the dashboard. + return false; + } + + public createFromSavedObject = async ( + savedObjectId: string, + input: Partial & { id: string }, + parent?: IContainer + ): Promise => { + try { + const { services, mapConfig } = await this.getStartServices(); + const url = services.application.getUrlForApp(MAPS_APP_ID, { + path: savedObjectId, + }); + const savedMap = await services.savedObjects.client.get(MAP_EMBEDDABLE, savedObjectId); + const savedMapAttributes = savedMap.attributes as MapSavedObjectAttributes; + return new MapEmbeddable( + { + ...input, + savedObjectId, + title: savedMapAttributes.title, + }, + { + parent, + services, + mapConfig, + editUrl: url, + savedMapAttributes, + } + ); + } catch (error) { + return new ErrorEmbeddable(error.message, input); + } + }; + + public async create(initialInput: MapInput, parent?: IContainer) { + const { services } = await this.getStartServices(); + await services.application.navigateToApp(MAPS_APP_ID, { + path: `${APP_PATH.CREATE_MAP}?originatingApp=dashboards`, + }); + return undefined; + } + + public getDisplayName() { + return i18n.translate('maps.displayName', { + defaultMessage: MAPS_APP_DISPLAY_NAME, + }); + } +} diff --git a/public/model/layerRenderController.ts b/public/model/layerRenderController.ts index 7c11bb2d..90c22716 100644 --- a/public/model/layerRenderController.ts +++ b/public/model/layerRenderController.ts @@ -14,6 +14,7 @@ import { getTime, IOpenSearchDashboardsSearchResponse, isCompleteResponse, + TimeRange, } from '../../../../src/plugins/data/common'; import { layersFunctionMap } from './layersFunctions'; import { MapServices } from '../types'; @@ -29,7 +30,8 @@ export const prepareDataLayerSource = ( layer: MapLayerSpecification, mapState: MapState, { data, notifications }: MapServices, - filters: Filter[] = [] + filters: Filter[] = [], + timeRange?: TimeRange ): Promise => { return new Promise(async (resolve, reject) => { if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS) { @@ -42,8 +44,14 @@ export const prepareDataLayerSource = ( sourceFields.push(...sourceConfig.tooltipFields); } let buildQuery; + let selectedTimeRange; if (indexPattern) { - const timeFilters = getTime(indexPattern, mapState.timeRange); + if (timeRange) { + selectedTimeRange = timeRange; + } else { + selectedTimeRange = mapState.timeRange; + } + const timeFilters = getTime(indexPattern, selectedTimeRange); buildQuery = buildOpenSearchQuery( indexPattern, [], @@ -90,7 +98,8 @@ export const handleDataLayerRender = ( mapState: MapState, services: MapServices, maplibreRef: MaplibreRef, - beforeLayerId: string | undefined + beforeLayerId: string | undefined, + timeRange?: TimeRange ) => { const filters: Filter[] = []; const geoField = mapLayer.source.geoFieldName; @@ -106,7 +115,7 @@ export const handleDataLayerRender = ( const geoBoundingBoxFilter: GeoBoundingBoxFilter = buildBBoxFilter(geoField, mapBounds, meta); filters.push(geoBoundingBoxFilter); - return prepareDataLayerSource(mapLayer, mapState, services, filters).then((result) => { + return prepareDataLayerSource(mapLayer, mapState, services, filters, timeRange).then((result) => { const { layer, dataSource } = result; if (layer.type === DASHBOARDS_MAPS_LAYER_TYPE.DOCUMENTS) { layersFunctionMap[layer.type].render(maplibreRef, layer, dataSource, beforeLayerId); diff --git a/public/plugin.tsx b/public/plugin.tsx index ee892f07..8d459a15 100644 --- a/public/plugin.tsx +++ b/public/plugin.tsx @@ -17,34 +17,33 @@ import { CustomImportMapPluginSetup, CustomImportMapPluginStart, } from './types'; -import { - PLUGIN_NAME, - PLUGIN_NAVIGATION_BAR_ID, - PLUGIN_NAVIGATION_BAR_TILE, -} from '../common/constants/shared'; +import { PLUGIN_NAME, MAPS_APP_ID, MAPS_APP_DISPLAY_NAME } from '../common/constants/shared'; import { ConfigSchema } from '../common/config'; import { AppPluginSetupDependencies } from './types'; import { RegionMapVisualizationDependencies } from '../../../src/plugins/region_map/public'; import { VectorUploadOptions } from './components/vector_upload_options'; import { OpenSearchDashboardsContextProvider } from '../../../src/plugins/opensearch_dashboards_react/public'; +import { MAP_SAVED_OBJECT_TYPE } from '../common'; +import { MapEmbeddableFactoryDefinition } from './embeddable'; export class CustomImportMapPlugin - implements Plugin { + implements Plugin +{ readonly _initializerContext: PluginInitializerContext; constructor(initializerContext: PluginInitializerContext) { this._initializerContext = initializerContext; } public setup( core: CoreSetup, - { regionMap }: AppPluginSetupDependencies + { regionMap, embeddable, visualizations }: AppPluginSetupDependencies ): CustomImportMapPluginSetup { const mapConfig: ConfigSchema = { ...this._initializerContext.config.get(), }; // Register an application into the side navigation menu core.application.register({ - id: PLUGIN_NAVIGATION_BAR_ID, - title: PLUGIN_NAVIGATION_BAR_TILE, + id: MAPS_APP_ID, + title: MAPS_APP_DISPLAY_NAME, order: 5100, category: { id: 'opensearch', @@ -74,11 +73,28 @@ export class CustomImportMapPlugin history: params.history, data, }; + params.element.classList.add('mapAppContainer'); // Render the application return renderApp(params, services, mapConfig); }, }); + const mapEmbeddableFactory = new MapEmbeddableFactoryDefinition(async () => { + const [coreStart, depsStart] = await core.getStartServices(); + const { navigation, data } = depsStart as AppPluginStartDependencies; + return { + mapConfig, + services: { + ...coreStart, + navigation, + toastNotifications: coreStart.notifications.toasts, + data, + }, + }; + }); + + embeddable.registerEmbeddableFactory(MAP_SAVED_OBJECT_TYPE, mapEmbeddableFactory as any); + const customSetup = async () => { const [coreStart] = await core.getStartServices(); regionMap.addOptionTab({ diff --git a/public/types.ts b/public/types.ts index d34f7273..c55c48ad 100644 --- a/public/types.ts +++ b/public/types.ts @@ -13,6 +13,8 @@ import { NavigationPublicPluginStart } from '../../../src/plugins/navigation/pub import { DataPublicPluginStart } from '../../../src/plugins/data/public'; import { RegionMapPluginSetup } from '../../../src/plugins/region_map/public'; +import { EmbeddableSetup } from '../../../src/plugins/embeddable/public'; +import { VisualizationsSetup } from '../../../src/plugins/visualizations/public'; export interface AppPluginStartDependencies { navigation: NavigationPublicPluginStart; @@ -28,12 +30,14 @@ export interface MapServices extends CoreStart { toastNotifications: ToastsStart; history: AppMountParameters['history']; data: DataPublicPluginStart; + application: CoreStart['application']; + i18n: CoreStart['i18n']; + savedObjects: SavedObjectsClient; + overlays: CoreStart['overlays']; } // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface CustomImportMapPluginSetup { - getGreeting: () => string; -} +export interface CustomImportMapPluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface CustomImportMapPluginStart {} @@ -44,4 +48,6 @@ export interface AppPluginStartDependencies { export interface AppPluginSetupDependencies { regionMap: RegionMapPluginSetup; + embeddable: EmbeddableSetup; + visualizations: VisualizationsSetup; } diff --git a/public/utils/breadcrumbs.ts b/public/utils/breadcrumbs.ts index daa69ea2..40308ce0 100644 --- a/public/utils/breadcrumbs.ts +++ b/public/utils/breadcrumbs.ts @@ -4,7 +4,7 @@ */ import { i18n } from '@osd/i18n'; -import {PLUGIN_NAVIGATION_BAR_ID} from '../../common'; +import { MAPS_APP_ID } from '../../common'; export function getMapsLandingBreadcrumbs(navigateToApp: any) { return [ @@ -12,7 +12,7 @@ export function getMapsLandingBreadcrumbs(navigateToApp: any) { text: i18n.translate('maps.listing.breadcrumb', { defaultMessage: 'Maps', }), - onClick: () => navigateToApp(PLUGIN_NAVIGATION_BAR_ID), + onClick: () => navigateToApp(MAPS_APP_ID), }, ]; }