From d223cd7df58f7d0da4a80b607b715f480ab2f4b5 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Wed, 11 Dec 2024 06:12:29 +1100 Subject: [PATCH] [8.x] [Dashboard] [Collapsable Panels] Add embeddable support (#198413) (#203652) # Backport This will backport the following commits from `main` to `8.x`: - [[Dashboard] [Collapsable Panels] Add embeddable support (#198413)](https://github.com/elastic/kibana/pull/198413) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Hannah Mudge --- .../components/add_button.tsx | 5 +- examples/embeddable_examples/public/index.ts | 2 + examples/grid_example/kibana.jsonc | 2 +- examples/grid_example/public/app.tsx | 140 +- .../public/logs_dashboard_panels.json | 4884 +++++++++++++++++ examples/grid_example/public/plugin.ts | 17 +- .../public/serialized_grid_layout.ts | 22 +- examples/grid_example/public/types.ts | 9 +- .../public/use_mock_dashboard_api.tsx | 51 +- examples/grid_example/public/utils.ts | 5 +- examples/grid_example/tsconfig.json | 16 +- .../grid/grid_height_smoother.tsx | 17 +- packages/kbn-grid-layout/grid/grid_layout.tsx | 5 +- .../grid/grid_panel/drag_handle.tsx | 88 +- .../grid/grid_panel/grid_panel.tsx | 87 +- .../grid/grid_panel/resize_handle.tsx | 9 +- .../grid/grid_row/grid_row.tsx | 142 +- .../kbn-grid-layout/grid/test_utils/mocks.tsx | 2 + packages/kbn-grid-layout/grid/types.ts | 1 + .../grid/use_grid_layout_events.ts | 55 +- .../grid/use_grid_layout_state.ts | 10 +- src/plugins/embeddable/kibana.jsonc | 21 +- .../react_embeddable_renderer.tsx | 1 + .../presentation_panel_header.tsx | 14 +- .../presentation_panel_hover_actions.tsx | 49 +- .../presentation_panel_internal.tsx | 28 +- .../public/panel_component/types.ts | 7 + 27 files changed, 5389 insertions(+), 300 deletions(-) create mode 100644 examples/grid_example/public/logs_dashboard_panels.json diff --git a/examples/embeddable_examples/public/app/presentation_container_example/components/add_button.tsx b/examples/embeddable_examples/public/app/presentation_container_example/components/add_button.tsx index 3bef0d4e1192e..570cd50f44ea2 100644 --- a/examples/embeddable_examples/public/app/presentation_container_example/components/add_button.tsx +++ b/examples/embeddable_examples/public/app/presentation_container_example/components/add_button.tsx @@ -10,9 +10,8 @@ import React, { ReactElement, useEffect, useState } from 'react'; import { EuiButton, EuiContextMenuItem, EuiContextMenuPanel, EuiPopover } from '@elastic/eui'; import { ADD_PANEL_TRIGGER, UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { PageApi } from '../types'; -export function AddButton({ pageApi, uiActions }: { pageApi: PageApi; uiActions: UiActionsStart }) { +export function AddButton({ pageApi, uiActions }: { pageApi: unknown; uiActions: UiActionsStart }) { const [isPopoverOpen, setIsPopoverOpen] = useState(false); const [items, setItems] = useState([]); @@ -73,7 +72,7 @@ export function AddButton({ pageApi, uiActions }: { pageApi: PageApi; uiActions: setIsPopoverOpen(!isPopoverOpen); }} > - Add + Add panel } isOpen={isPopoverOpen} diff --git a/examples/embeddable_examples/public/index.ts b/examples/embeddable_examples/public/index.ts index 1705ec84680f9..3e0db5b6ab248 100644 --- a/examples/embeddable_examples/public/index.ts +++ b/examples/embeddable_examples/public/index.ts @@ -9,4 +9,6 @@ import { EmbeddableExamplesPlugin } from './plugin'; +export { AddButton as AddEmbeddableButton } from './app/presentation_container_example/components/add_button'; + export const plugin = () => new EmbeddableExamplesPlugin(); diff --git a/examples/grid_example/kibana.jsonc b/examples/grid_example/kibana.jsonc index ecaae63268e7a..046838a4393ca 100644 --- a/examples/grid_example/kibana.jsonc +++ b/examples/grid_example/kibana.jsonc @@ -7,7 +7,7 @@ "id": "gridExample", "server": false, "browser": true, - "requiredPlugins": ["developerExamples"], + "requiredPlugins": ["developerExamples", "embeddable", "uiActions", "embeddableExamples"], "requiredBundles": [] } } diff --git a/examples/grid_example/public/app.tsx b/examples/grid_example/public/app.tsx index 1a44d2cb4f8c1..9519fd2af43a9 100644 --- a/examples/grid_example/public/app.tsx +++ b/examples/grid_example/public/app.tsx @@ -11,27 +11,28 @@ import deepEqual from 'fast-deep-equal'; import React, { useCallback, useEffect, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; import { combineLatest, debounceTime } from 'rxjs'; -import { v4 as uuidv4 } from 'uuid'; import { EuiBadge, EuiButton, EuiButtonEmpty, + EuiButtonGroup, EuiCallOut, EuiFlexGroup, EuiFlexItem, EuiPageTemplate, - EuiProvider, EuiSpacer, - EuiButtonGroup, - EuiButtonIcon, } from '@elastic/eui'; import { AppMountParameters } from '@kbn/core-application-browser'; import { CoreStart } from '@kbn/core-lifecycle-browser'; -import { GridLayout, GridLayoutData, GridAccessMode } from '@kbn/grid-layout'; +import { AddEmbeddableButton } from '@kbn/embeddable-examples-plugin/public'; +import { ReactEmbeddableRenderer } from '@kbn/embeddable-plugin/public'; +import { GridLayout, GridLayoutData } from '@kbn/grid-layout'; import { i18n } from '@kbn/i18n'; +import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing'; +import { KibanaRenderContextProvider } from '@kbn/react-kibana-context-render'; +import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; -import { getPanelId } from './get_panel_id'; import { clearSerializedDashboardState, getSerializedDashboardState, @@ -45,23 +46,37 @@ const DASHBOARD_MARGIN_SIZE = 8; const DASHBOARD_GRID_HEIGHT = 20; const DASHBOARD_GRID_COLUMN_COUNT = 48; -export const GridExample = ({ coreStart }: { coreStart: CoreStart }) => { +export const GridExample = ({ + coreStart, + uiActions, +}: { + coreStart: CoreStart; + uiActions: UiActionsStart; +}) => { const savedState = useRef(getSerializedDashboardState()); const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false); - const [expandedPanelId, setExpandedPanelId] = useState(); - const [accessMode, setAccessMode] = useState('EDIT'); const [currentLayout, setCurrentLayout] = useState( dashboardInputToGridLayout(savedState.current) ); const mockDashboardApi = useMockDashboardApi({ savedState: savedState.current }); + const [viewMode, expandedPanelId] = useBatchedPublishingSubjects( + mockDashboardApi.viewMode, + mockDashboardApi.expandedPanelId + ); useEffect(() => { combineLatest([mockDashboardApi.panels$, mockDashboardApi.rows$]) .pipe(debounceTime(0)) // debounce to avoid subscribe being called twice when both panels$ and rows$ publish .subscribe(([panels, rows]) => { const hasChanges = !( - deepEqual(panels, savedState.current.panels) && deepEqual(rows, savedState.current.rows) + deepEqual( + Object.values(panels).map(({ gridData }) => ({ row: 0, ...gridData })), + Object.values(savedState.current.panels).map(({ gridData }) => ({ + row: 0, // if row is undefined, then default to 0 + ...gridData, + })) + ) && deepEqual(rows, savedState.current.rows) ); setHasUnsavedChanges(hasChanges); setCurrentLayout(dashboardInputToGridLayout({ panels, rows })); @@ -69,58 +84,31 @@ export const GridExample = ({ coreStart }: { coreStart: CoreStart }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - const renderBasicPanel = useCallback( - (id: string) => { + const renderPanelContents = useCallback( + (id: string, setDragHandles?: (refs: Array) => void) => { + const currentPanels = mockDashboardApi.panels$.getValue(); + return ( - <> -
{id}
- { - setExpandedPanelId(undefined); - mockDashboardApi.removePanel(id); - }} - > - {i18n.translate('examples.gridExample.deletePanelButton', { - defaultMessage: 'Delete panel', - })} - - { - setExpandedPanelId(undefined); - const newPanelId = await getPanelId({ - coreStart, - suggestion: id, - }); - if (newPanelId) mockDashboardApi.replacePanel(id, newPanelId); - }} - > - {i18n.translate('examples.gridExample.replacePanelButton', { - defaultMessage: 'Replace panel', - })} - - setExpandedPanelId((expandedId) => (expandedId ? undefined : id))} - aria-label={ - expandedPanelId - ? i18n.translate('examples.gridExample.minimizePanel', { - defaultMessage: 'Minimize panel {id}', - values: { id }, - }) - : i18n.translate('examples.gridExample.maximizePanel', { - defaultMessage: 'Maximize panel {id}', - values: { id }, - }) - } - /> - + mockDashboardApi} + panelProps={{ + showBadges: true, + showBorder: true, + showNotifications: true, + showShadow: false, + setDragHandles, + }} + /> ); }, - [coreStart, mockDashboardApi, setExpandedPanelId, expandedPanelId] + [mockDashboardApi] ); return ( - + { { - { - setExpandedPanelId(undefined); - const panelId = await getPanelId({ - coreStart, - suggestion: uuidv4(), - }); - if (panelId) mockDashboardApi.addNewPanel({ id: panelId }); - }} - > - {i18n.translate('examples.gridExample.addPanelButton', { - defaultMessage: 'Add a panel', - })} - + @@ -180,7 +155,7 @@ export const GridExample = ({ coreStart }: { coreStart: CoreStart }) => { })} options={[ { - id: 'VIEW', + id: 'view', label: i18n.translate('examples.gridExample.viewOption', { defaultMessage: 'View', }), @@ -188,16 +163,16 @@ export const GridExample = ({ coreStart }: { coreStart: CoreStart }) => { 'The layout adjusts when the window is resized. Panel interactivity, such as moving and resizing within the grid, is disabled.', }, { - id: 'EDIT', + id: 'edit', label: i18n.translate('examples.gridExample.editOption', { defaultMessage: 'Edit', }), toolTipContent: 'The layout does not adjust when the window is resized.', }, ]} - idSelected={accessMode} + idSelected={viewMode} onChange={(id) => { - setAccessMode(id as GridAccessMode); + mockDashboardApi.viewMode.next(id); }} /> @@ -245,7 +220,7 @@ export const GridExample = ({ coreStart }: { coreStart: CoreStart }) => { { rowHeight: DASHBOARD_GRID_HEIGHT, columnCount: DASHBOARD_GRID_COLUMN_COUNT, }} - renderPanelContents={renderBasicPanel} + renderPanelContents={renderPanelContents} onLayoutChange={(newLayout) => { - const { panels, rows } = gridLayoutToDashboardPanelMap(newLayout); + const { panels, rows } = gridLayoutToDashboardPanelMap( + mockDashboardApi.panels$.getValue(), + newLayout + ); mockDashboardApi.panels$.next(panels); mockDashboardApi.rows$.next(rows); }} /> - + ); }; export const renderGridExampleApp = ( element: AppMountParameters['element'], - coreStart: CoreStart + deps: { uiActions: UiActionsStart; coreStart: CoreStart } ) => { - ReactDOM.render(, element); + ReactDOM.render(, element); return () => ReactDOM.unmountComponentAtNode(element); }; diff --git a/examples/grid_example/public/logs_dashboard_panels.json b/examples/grid_example/public/logs_dashboard_panels.json new file mode 100644 index 0000000000000..29d63baa2d33b --- /dev/null +++ b/examples/grid_example/public/logs_dashboard_panels.json @@ -0,0 +1,4884 @@ +{ + "343f0bef-0b19-452e-b1c8-59beb18b6f0c": { + "type": "visualization", + "gridData": { + "x": 0, + "y": 0, + "w": 18, + "h": 8, + "i": "343f0bef-0b19-452e-b1c8-59beb18b6f0c" + }, + "explicitInput": { + "id": "343f0bef-0b19-452e-b1c8-59beb18b6f0c", + "hidePanelTitles": true, + "enhancements": { + "dynamicActions": { + "events": [] + } + }, + "savedVis": { + "title": "[Logs] Markdown Instructions", + "description": "", + "type": "markdown", + "params": { + "fontSize": 12, + "openLinksInNewTab": true, + "markdown": "#### Sample Logs Data\n### Request Sizes\nThis dashboard uses the `[Logs]` sample data for you to play with and includes visualizations related to the sizes (in bytes) of the sample requests. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html)." + }, + "uiState": {}, + "data": { + "aggs": [], + "searchSource": { + "query": { + "query": "", + "language": "kuery" + }, + "filter": [] + } + } + } + } + }, + "2874670c-61d5-4dca-b842-fdf76977cc02": { + "type": "lens", + "gridData": { + "x": 18, + "y": 0, + "w": 30, + "h": 13, + "i": "2874670c-61d5-4dca-b842-fdf76977cc02" + }, + "explicitInput": { + "id": "2874670c-61d5-4dca-b842-fdf76977cc02", + "enhancements": { + "dynamicActions": { + "events": [] + } + }, + "syncColors": false, + "syncCursor": true, + "syncTooltips": false, + "searchSessionId": "52b991d3-bfec-474c-a9ee-860b58baffd2", + "filters": [], + "query": { + "query": "", + "language": "kuery" + }, + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsXY", + "type": "lens", + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-9159b5ef-429c-4c92-b587-7247fbdadd4d", + "type": "index-pattern" + } + ], + "state": { + "visualization": { + "legend": { + "isVisible": true, + "position": "right" + }, + "valueLabels": "hide", + "fittingFunction": "None", + "axisTitlesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "labelsOrientation": { + "x": 0, + "yLeft": 0, + "yRight": 0 + }, + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "preferredSeriesType": "area", + "layers": [ + { + "layerId": "9159b5ef-429c-4c92-b587-7247fbdadd4d", + "accessors": ["e950618a-874e-435d-85da-30dd52504120"], + "position": "top", + "seriesType": "area", + "showGridlines": false, + "layerType": "data", + "xAccessor": "f136fcf3-2bda-42a0-8bae-231543b236ab" + } + ] + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "9159b5ef-429c-4c92-b587-7247fbdadd4d": { + "columns": { + "f136fcf3-2bda-42a0-8bae-231543b236ab": { + "label": "timestamp", + "dataType": "date", + "operationType": "date_histogram", + "sourceField": "timestamp", + "isBucketed": true, + "scale": "interval", + "params": { + "interval": "auto", + "includeEmptyRows": true, + "dropPartials": false + } + }, + "e950618a-874e-435d-85da-30dd52504120": { + "label": "Median of bytes", + "dataType": "number", + "operationType": "median", + "sourceField": "bytes", + "isBucketed": false, + "scale": "ratio", + "params": { + "emptyAsNull": true + } + } + }, + "columnOrder": [ + "f136fcf3-2bda-42a0-8bae-231543b236ab", + "e950618a-874e-435d-85da-30dd52504120" + ], + "incompleteColumns": {}, + "sampling": 1 + } + } + }, + "indexpattern": { + "layers": {} + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + } + } + }, + "a88dd08e-9b07-45b0-bc0a-822450d2bfcb": { + "type": "lens", + "gridData": { + "x": 0, + "y": 8, + "w": 9, + "h": 5, + "i": "a88dd08e-9b07-45b0-bc0a-822450d2bfcb" + }, + "explicitInput": { + "id": "a88dd08e-9b07-45b0-bc0a-822450d2bfcb", + "title": "", + "enhancements": { + "dynamicActions": { + "events": [] + } + }, + "syncColors": false, + "syncCursor": true, + "syncTooltips": false, + "searchSessionId": "52b991d3-bfec-474c-a9ee-860b58baffd2", + "filters": [], + "query": { + "query": "", + "language": "kuery" + }, + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-04d32faf-7772-4820-bc25-dc510a13b0bc", + "type": "index-pattern" + } + ], + "state": { + "visualization": { + "layerId": "04d32faf-7772-4820-bc25-dc510a13b0bc", + "layerType": "data", + "metricAccessor": "3016ca70-b4ae-408d-859d-3de50b22b528", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 3, + "name": "custom", + "reverse": false, + "rangeType": "number", + "rangeMin": 0, + "rangeMax": null, + "progression": "fixed", + "stops": [ + { + "color": "#bbdad3", + "stop": 13154.66 + }, + { + "color": "#77b6a8", + "stop": 26309.32 + }, + { + "color": "#209280", + "stop": 39464 + } + ], + "continuity": "above", + "maxSteps": 5, + "colorStops": [ + { + "color": "#bbdad3", + "stop": 0 + }, + { + "color": "#77b6a8", + "stop": 13154.66 + }, + { + "color": "#209280", + "stop": 26309.32 + } + ] + } + } + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "04d32faf-7772-4820-bc25-dc510a13b0bc": { + "columns": { + "3016ca70-b4ae-408d-859d-3de50b22b528": { + "label": "Median Request Size", + "dataType": "number", + "operationType": "median", + "sourceField": "bytes", + "isBucketed": false, + "scale": "ratio", + "params": { + "format": { + "id": "bytes", + "params": { + "decimals": 2 + } + }, + "emptyAsNull": true + }, + "customLabel": true + } + }, + "columnOrder": ["3016ca70-b4ae-408d-859d-3de50b22b528"], + "incompleteColumns": {}, + "sampling": 1 + } + } + }, + "indexpattern": { + "layers": {} + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + } + } + }, + "2daa9fd6-1406-4f3d-8230-3172760c5eb0": { + "type": "lens", + "gridData": { + "x": 9, + "y": 8, + "w": 9, + "h": 5, + "i": "2daa9fd6-1406-4f3d-8230-3172760c5eb0" + }, + "explicitInput": { + "id": "2daa9fd6-1406-4f3d-8230-3172760c5eb0", + "enhancements": { + "dynamicActions": { + "events": [] + } + }, + "syncColors": false, + "syncCursor": true, + "syncTooltips": false, + "searchSessionId": "52b991d3-bfec-474c-a9ee-860b58baffd2", + "filters": [], + "query": { + "query": "", + "language": "kuery" + }, + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-04d32faf-7772-4820-bc25-dc510a13b0bc", + "type": "index-pattern" + } + ], + "state": { + "visualization": { + "layerId": "04d32faf-7772-4820-bc25-dc510a13b0bc", + "layerType": "data", + "metricAccessor": "3016ca70-b4ae-408d-859d-3de50b22b528", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 3, + "name": "custom", + "reverse": false, + "rangeType": "number", + "rangeMin": 0, + "rangeMax": null, + "progression": "fixed", + "stops": [ + { + "color": "#bbdad3", + "stop": 13154.66 + }, + { + "color": "#77b6a8", + "stop": 26309.32 + }, + { + "color": "#209280", + "stop": 39464 + } + ], + "continuity": "above", + "maxSteps": 5, + "colorStops": [ + { + "color": "#bbdad3", + "stop": 0 + }, + { + "color": "#77b6a8", + "stop": 13154.66 + }, + { + "color": "#209280", + "stop": 26309.32 + } + ] + } + } + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "04d32faf-7772-4820-bc25-dc510a13b0bc": { + "columns": { + "3016ca70-b4ae-408d-859d-3de50b22b528": { + "label": "Maximum Request Size", + "dataType": "number", + "operationType": "max", + "sourceField": "bytes", + "isBucketed": false, + "scale": "ratio", + "params": { + "format": { + "id": "bytes", + "params": { + "decimals": 2 + } + }, + "emptyAsNull": true + }, + "customLabel": true + } + }, + "columnOrder": ["3016ca70-b4ae-408d-859d-3de50b22b528"], + "incompleteColumns": {}, + "sampling": 1 + } + } + }, + "indexpattern": { + "layers": {} + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + } + } + }, + "b6a55c16-2cce-40a7-9d15-988bc060af16": { + "type": "lens", + "gridData": { + "x": 0, + "y": 13, + "w": 18, + "h": 11, + "i": "b6a55c16-2cce-40a7-9d15-988bc060af16" + }, + "explicitInput": { + "id": "b6a55c16-2cce-40a7-9d15-988bc060af16", + "title": "Bytes distribution", + "enhancements": { + "dynamicActions": { + "events": [] + } + }, + "hidePanelTitles": false, + "syncColors": false, + "syncCursor": true, + "syncTooltips": false, + "searchSessionId": "51b1c696-a450-48c2-8223-5753e57957c1", + "filters": [], + "query": { + "language": "kuery", + "query": "" + }, + "attributes": { + "title": "[Logs] Bytes distribution - Bucketed", + "description": "", + "visualizationType": "lnsXY", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-40355730-da62-4d81-8b3b-18ff824f7dd2" + } + ], + "state": { + "visualization": { + "legend": { + "isVisible": true, + "legendSize": "auto", + "position": "right" + }, + "valueLabels": "hide", + "fittingFunction": "None", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + }, + "axisTitlesVisibilitySettings": { + "x": false, + "yLeft": false, + "yRight": true + }, + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "labelsOrientation": { + "x": 0, + "yLeft": 0, + "yRight": 0 + }, + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "preferredSeriesType": "bar", + "layers": [ + { + "layerId": "40355730-da62-4d81-8b3b-18ff824f7dd2", + "seriesType": "bar", + "xAccessor": "cfeb3d08-02c2-48d5-92f8-d2fc831f6953", + "accessors": ["569e4272-0188-4023-9b8c-e8611d2c7e0e"], + "layerType": "data", + "colorMapping": { + "assignments": [], + "specialAssignments": [ + { + "rule": { + "type": "other" + }, + "color": { + "type": "loop" + }, + "touched": false + } + ], + "paletteId": "eui_amsterdam_color_blind", + "colorMode": { + "type": "categorical" + } + }, + "yConfig": [ + { + "forAccessor": "569e4272-0188-4023-9b8c-e8611d2c7e0e", + "color": "#54B399" + } + ] + } + ] + }, + "query": { + "language": "kuery", + "query": "" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "40355730-da62-4d81-8b3b-18ff824f7dd2": { + "columns": { + "cfeb3d08-02c2-48d5-92f8-d2fc831f6953": { + "label": "bytes", + "dataType": "string", + "operationType": "range", + "sourceField": "bytes", + "isBucketed": true, + "scale": "ordinal", + "params": { + "type": "range", + "ranges": [ + { + "from": 0, + "to": 1, + "label": "Empty requests" + }, + { + "from": 1, + "to": 2000, + "label": "" + }, + { + "from": 2000, + "to": 4000, + "label": "" + }, + { + "from": 4000, + "to": 6000, + "label": "" + }, + { + "from": 6000, + "to": 8000, + "label": "" + }, + { + "from": 8000, + "to": 10000, + "label": "" + }, + { + "from": 10000, + "to": 12000, + "label": "" + }, + { + "from": 12000, + "to": 14000, + "label": "" + }, + { + "from": 14000, + "to": null, + "label": "" + } + ], + "maxBars": 499.5, + "format": { + "id": "bytes", + "params": { + "decimals": 2 + } + }, + "parentFormat": { + "id": "range", + "params": { + "template": "arrow_right", + "replaceInfinity": true + } + } + } + }, + "569e4272-0188-4023-9b8c-e8611d2c7e0eX0": { + "label": "Part of % of visits", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "569e4272-0188-4023-9b8c-e8611d2c7e0eX1": { + "label": "Part of % of visits", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "569e4272-0188-4023-9b8c-e8611d2c7e0eX2": { + "label": "Part of % of visits", + "dataType": "number", + "operationType": "overall_sum", + "isBucketed": false, + "scale": "ratio", + "references": ["569e4272-0188-4023-9b8c-e8611d2c7e0eX1"], + "customLabel": true + }, + "569e4272-0188-4023-9b8c-e8611d2c7e0eX3": { + "label": "Part of % of visits", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "569e4272-0188-4023-9b8c-e8611d2c7e0eX0", + "569e4272-0188-4023-9b8c-e8611d2c7e0eX2" + ], + "location": { + "min": 0, + "max": 30 + }, + "text": "count() / overall_sum(count())" + } + }, + "references": [ + "569e4272-0188-4023-9b8c-e8611d2c7e0eX0", + "569e4272-0188-4023-9b8c-e8611d2c7e0eX2" + ], + "customLabel": true + }, + "569e4272-0188-4023-9b8c-e8611d2c7e0e": { + "label": "% of visits", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count() / overall_sum(count())", + "isFormulaBroken": false + }, + "references": ["569e4272-0188-4023-9b8c-e8611d2c7e0eX3"], + "customLabel": true + } + }, + "columnOrder": [ + "cfeb3d08-02c2-48d5-92f8-d2fc831f6953", + "569e4272-0188-4023-9b8c-e8611d2c7e0e", + "569e4272-0188-4023-9b8c-e8611d2c7e0eX0", + "569e4272-0188-4023-9b8c-e8611d2c7e0eX1", + "569e4272-0188-4023-9b8c-e8611d2c7e0eX2", + "569e4272-0188-4023-9b8c-e8611d2c7e0eX3" + ], + "incompleteColumns": {}, + "sampling": 1, + "indexPatternId": "90943e30-9a47-11e8-b64d-95841ca0b247" + } + }, + "currentIndexPatternId": "90943e30-9a47-11e8-b64d-95841ca0b247" + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + } + } + }, + "7c02d081-73a7-48c7-8ca7-fff6a9bb3ac3": { + "type": "lens", + "gridData": { + "x": 18, + "y": 13, + "w": 30, + "h": 11, + "i": "7c02d081-73a7-48c7-8ca7-fff6a9bb3ac3" + }, + "explicitInput": { + "id": "7c02d081-73a7-48c7-8ca7-fff6a9bb3ac3", + "enhancements": { + "dynamicActions": { + "events": [] + } + }, + "syncColors": false, + "syncCursor": true, + "syncTooltips": false, + "searchSessionId": "52b991d3-bfec-474c-a9ee-860b58baffd2", + "filters": [], + "query": { + "query": "", + "language": "kuery" + }, + "attributes": { + "title": "", + "visualizationType": "lnsDatatable", + "type": "lens", + "references": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-c840e93e-2949-4723-ad35-6bdb2d724404", + "type": "index-pattern" + } + ], + "state": { + "visualization": { + "columns": [ + { + "columnId": "4e64d6d7-4f92-4d5e-abbb-13796604db30", + "isTransposed": false + }, + { + "columnId": "fb9a848d-76f3-4005-a067-4259a50b5621", + "isTransposed": false + }, + { + "columnId": "a2760bc2-9a6e-46a1-8595-86f61573c7cf", + "isTransposed": false + }, + { + "columnId": "2c8bd8d5-35ff-4386-8d27-3ba882b13e43", + "isTransposed": false, + "colorMode": "text", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 5, + "stops": [ + { + "color": "#d23115", + "stop": 1000 + }, + { + "color": "#fcc400", + "stop": 1500 + }, + { + "color": "#68bc00", + "stop": 1501 + } + ], + "rangeType": "number", + "rangeMin": 0, + "rangeMax": null, + "continuity": "above", + "colorStops": [ + { + "color": "#d23115", + "stop": 0 + }, + { + "color": "#fcc400", + "stop": 1000 + }, + { + "color": "#68bc00", + "stop": 1500 + } + ], + "name": "custom" + } + } + }, + { + "columnId": "defa6f97-b874-4556-8438-056fb437787b", + "isTransposed": false, + "colorMode": "text", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 5, + "stops": [ + { + "color": "#D23115", + "stop": 10 + }, + { + "color": "#FCC400", + "stop": 25 + }, + { + "color": "#68bc00", + "stop": 26 + } + ], + "rangeType": "number", + "rangeMin": 0, + "rangeMax": null, + "continuity": "above", + "colorStops": [ + { + "color": "#D23115", + "stop": 0 + }, + { + "color": "#FCC400", + "stop": 10 + }, + { + "color": "#68bc00", + "stop": 25 + } + ], + "name": "custom" + } + } + } + ], + "layerId": "c840e93e-2949-4723-ad35-6bdb2d724404", + "layerType": "data" + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "c840e93e-2949-4723-ad35-6bdb2d724404": { + "columns": { + "4e64d6d7-4f92-4d5e-abbb-13796604db30": { + "label": "Type", + "dataType": "string", + "operationType": "terms", + "scale": "ordinal", + "sourceField": "extension.keyword", + "isBucketed": true, + "params": { + "size": 10, + "orderBy": { + "type": "column", + "columnId": "fb9a848d-76f3-4005-a067-4259a50b5621" + }, + "orderDirection": "desc", + "otherBucket": true, + "missingBucket": false, + "parentFormat": { + "id": "terms" + }, + "include": [], + "exclude": [], + "includeIsRegex": false, + "excludeIsRegex": false + }, + "customLabel": true + }, + "fb9a848d-76f3-4005-a067-4259a50b5621": { + "label": "Bytes (Total)", + "dataType": "number", + "operationType": "sum", + "sourceField": "bytes", + "isBucketed": false, + "scale": "ratio", + "params": { + "emptyAsNull": true, + "format": { + "id": "bytes", + "params": { + "decimals": 2 + } + } + }, + "customLabel": true + }, + "a2760bc2-9a6e-46a1-8595-86f61573c7cf": { + "label": "Bytes (Last Hour)", + "dataType": "number", + "operationType": "sum", + "sourceField": "bytes", + "isBucketed": false, + "scale": "ratio", + "reducedTimeRange": "1h", + "params": { + "emptyAsNull": true, + "format": { + "id": "bytes", + "params": { + "decimals": 2 + } + } + }, + "customLabel": true + }, + "2c8bd8d5-35ff-4386-8d27-3ba882b13e43": { + "label": "Unique Visits (Total)", + "dataType": "number", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "clientip", + "isBucketed": false, + "params": { + "emptyAsNull": true + }, + "customLabel": true + }, + "defa6f97-b874-4556-8438-056fb437787b": { + "label": "Unique count of clientip", + "dataType": "number", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "clientip", + "isBucketed": false, + "reducedTimeRange": "1h", + "params": { + "emptyAsNull": true + } + } + }, + "columnOrder": [ + "4e64d6d7-4f92-4d5e-abbb-13796604db30", + "fb9a848d-76f3-4005-a067-4259a50b5621", + "a2760bc2-9a6e-46a1-8595-86f61573c7cf", + "2c8bd8d5-35ff-4386-8d27-3ba882b13e43", + "defa6f97-b874-4556-8438-056fb437787b" + ], + "sampling": 1, + "incompleteColumns": {} + } + } + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + } + } + }, + + "4": { + "type": "map", + "gridData": { + "x": 0, + "y": 16, + "w": 48, + "h": 17, + "i": "4", + "row": 1 + }, + "explicitInput": { + "id": "4", + "savedObjectId": "de71f4f0-1902-11e9-919b-ffe5949a18d2", + "isLayerTOCOpen": false, + "hiddenLayers": [], + "mapCenter": { + "lat": 39.6308, + "lon": -100.62555, + "zoom": 3.31 + }, + "openTOCDetails": [], + "enhancements": {}, + "mapBuffer": { + "minLon": -180, + "minLat": 21.94305, + "maxLon": -22.5, + "maxLat": 55.77657 + } + }, + "version": "8.10.0-SNAPSHOT" + }, + "05da0d2b-0145-4068-b21c-00be3184d465": { + "type": "visualization", + "gridData": { + "x": 0, + "y": 0, + "w": 18, + "h": 8, + "i": "05da0d2b-0145-4068-b21c-00be3184d465", + "row": 1 + }, + "explicitInput": { + "id": "05da0d2b-0145-4068-b21c-00be3184d465", + "savedVis": { + "title": "[Logs] Markdown Instructions", + "description": "", + "type": "markdown", + "params": { + "fontSize": 12, + "openLinksInNewTab": true, + "markdown": "#### Sample Logs Data\n### Visitors\nThis dashboard uses the `[Logs]` sample data for you to play with and includes visualizations related to the visitors. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html)." + }, + "uiState": {}, + "data": { + "aggs": [], + "searchSource": { + "query": { + "query": "", + "language": "kuery" + }, + "filter": [] + } + } + }, + "enhancements": {}, + "hidePanelTitles": true + }, + "version": "8.10.0-SNAPSHOT" + }, + "b7da9075-4742-47e3-b4f8-fc9ba82de74c": { + "type": "visualization", + "gridData": { + "x": 18, + "y": 0, + "w": 18, + "h": 16, + "i": "b7da9075-4742-47e3-b4f8-fc9ba82de74c", + "row": 1 + }, + "explicitInput": { + "id": "b7da9075-4742-47e3-b4f8-fc9ba82de74c", + "savedObjectId": "cb099a20-ea66-11eb-9425-113343a037e3", + "title": "Unique Destination Heatmap", + "savedVis": { + "title": "", + "description": "", + "type": "vega", + "params": { + "spec": "{\n $schema: https://vega.github.io/schema/vega-lite/v5.json\n data: {\n url: {\n %context%: true\n %timefield%: @timestamp\n index: kibana_sample_data_logs\n body: {\n aggs: {\n countries: {\n terms: {\n field: geo.src\n size: 25\n }\n aggs: {\n hours: {\n histogram: {\n field: hour_of_day\n interval: 1\n }\n aggs: {\n unique: {\n cardinality: {\n field: clientip\n }\n }\n }\n }\n }\n }\n }\n size: 0\n }\n }\n format: {property: \"aggregations.countries.buckets\"}\n }\n \n transform: [\n {\n flatten: [\"hours.buckets\"],\n as: [\"buckets\"]\n }\n ]\n\n mark: {\n type: rect\n tooltip: true\n }\n\n encoding: {\n x: {\n field: buckets.key\n type: ordinal\n axis: {\n title: false\n labelAngle: 0\n }\n }\n y: {\n field: key\n type: nominal\n sort: {\n field: -buckets.unique.value\n }\n axis: {title: false}\n }\n color: {\n field: buckets.unique.value\n type: quantitative\n axis: {title: false}\n scale: {\n scheme: reds\n }\n }\n }\n}\n" + }, + "uiState": {}, + "data": { + "aggs": [], + "searchSource": { + "query": { + "query": "", + "language": "kuery" + }, + "filter": [] + } + } + }, + "enhancements": {}, + "hidePanelTitles": false + }, + "version": "8.10.0-SNAPSHOT" + }, + "5c409557-644d-4c05-a284-ffe54bb28db0": { + "type": "lens", + "gridData": { + "x": 36, + "y": 0, + "w": 12, + "h": 16, + "i": "5c409557-644d-4c05-a284-ffe54bb28db0", + "row": 1 + }, + "explicitInput": { + "id": "5c409557-644d-4c05-a284-ffe54bb28db0", + "title": "Sum of Request Bytes by Visitor Location", + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsPie", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-edb3a980-148e-4d41-ae90-a77f679a988b" + } + ], + "state": { + "visualization": { + "shape": "treemap", + "layers": [ + { + "layerId": "edb3a980-148e-4d41-ae90-a77f679a988b", + "primaryGroups": ["e5fed3a7-a050-401f-84cc-98f32d97c350"], + "metrics": ["90c36c1d-5653-4d12-b7ab-a8fb82a354fc"], + "numberDisplay": "percent", + "categoryDisplay": "default", + "legendDisplay": "default", + "nestedLegend": false, + "layerType": "data" + } + ], + "palette": { + "type": "palette", + "name": "cool" + } + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "edb3a980-148e-4d41-ae90-a77f679a988b": { + "columns": { + "e5fed3a7-a050-401f-84cc-98f32d97c350": { + "label": "Top 5 values of geo.dest", + "dataType": "string", + "operationType": "terms", + "scale": "ordinal", + "sourceField": "geo.dest", + "isBucketed": true, + "params": { + "size": 5, + "orderBy": { + "type": "column", + "columnId": "90c36c1d-5653-4d12-b7ab-a8fb82a354fc" + }, + "orderDirection": "desc", + "otherBucket": true, + "missingBucket": false, + "parentFormat": { + "id": "terms" + }, + "include": [], + "exclude": [], + "includeIsRegex": false, + "excludeIsRegex": false + } + }, + "90c36c1d-5653-4d12-b7ab-a8fb82a354fc": { + "label": "Sum of bytes", + "dataType": "number", + "operationType": "sum", + "sourceField": "bytes", + "isBucketed": false, + "scale": "ratio", + "params": { + "emptyAsNull": true, + "format": { + "id": "bytes", + "params": { + "decimals": 2 + } + } + } + } + }, + "columnOrder": [ + "e5fed3a7-a050-401f-84cc-98f32d97c350", + "90c36c1d-5653-4d12-b7ab-a8fb82a354fc" + ], + "incompleteColumns": {}, + "sampling": 1 + } + } + }, + "indexpattern": { + "layers": {} + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {}, + "hidePanelTitles": false + }, + "version": "8.10.0-SNAPSHOT" + }, + "af4b5c07-506e-44c2-b2bb-2113d0c5b274": { + "type": "lens", + "gridData": { + "x": 0, + "y": 8, + "w": 6, + "h": 8, + "i": "af4b5c07-506e-44c2-b2bb-2113d0c5b274", + "row": 1 + }, + "explicitInput": { + "id": "af4b5c07-506e-44c2-b2bb-2113d0c5b274", + "title": "", + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-ebfc4825-b7b6-4113-bd22-f1f07a4113e9" + }, + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-717129e0-2d11-4f22-abbb-a2c8a0f7ae0d" + } + ], + "state": { + "visualization": { + "layerId": "ebfc4825-b7b6-4113-bd22-f1f07a4113e9", + "layerType": "data", + "metricAccessor": "0e1418b8-3896-416c-8f78-9e832e5c42c8", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 3, + "name": "custom", + "reverse": false, + "rangeType": "number", + "rangeMin": 0, + "rangeMax": null, + "progression": "fixed", + "stops": [ + { + "color": "#ccd9ea", + "stop": 500 + }, + { + "color": "#98b5d5", + "stop": 1000 + }, + { + "color": "#6092c0", + "stop": 3662 + } + ], + "continuity": "above", + "maxSteps": 5, + "colorStops": [ + { + "color": "#ccd9ea", + "stop": 0 + }, + { + "color": "#98b5d5", + "stop": 500 + }, + { + "color": "#6092c0", + "stop": 1000 + } + ] + } + }, + "showBar": false, + "icon": "empty", + "trendlineLayerId": "717129e0-2d11-4f22-abbb-a2c8a0f7ae0d", + "trendlineLayerType": "metricTrendline", + "trendlineTimeAccessor": "6c3a5c8c-2e1f-4a51-9b15-39d2fac35c58", + "trendlineMetricAccessor": "cf34fce7-0017-472e-91c2-56f30a86d32b" + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "ebfc4825-b7b6-4113-bd22-f1f07a4113e9": { + "columns": { + "0e1418b8-3896-416c-8f78-9e832e5c42c8": { + "label": "Total Visits", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "clientip", + "params": { + "emptyAsNull": true + }, + "customLabel": true + } + }, + "columnOrder": ["0e1418b8-3896-416c-8f78-9e832e5c42c8"], + "incompleteColumns": {}, + "sampling": 1 + }, + "717129e0-2d11-4f22-abbb-a2c8a0f7ae0d": { + "linkToLayers": ["ebfc4825-b7b6-4113-bd22-f1f07a4113e9"], + "columns": { + "6c3a5c8c-2e1f-4a51-9b15-39d2fac35c58": { + "label": "timestamp", + "dataType": "date", + "operationType": "date_histogram", + "sourceField": "timestamp", + "isBucketed": true, + "scale": "interval", + "params": { + "interval": "auto", + "includeEmptyRows": true, + "dropPartials": false + } + }, + "cf34fce7-0017-472e-91c2-56f30a86d32b": { + "label": "Total Visits", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "clientip", + "params": { + "emptyAsNull": true + }, + "customLabel": true + } + }, + "columnOrder": [ + "6c3a5c8c-2e1f-4a51-9b15-39d2fac35c58", + "cf34fce7-0017-472e-91c2-56f30a86d32b" + ], + "sampling": 1, + "ignoreGlobalFilters": false, + "incompleteColumns": {} + } + } + }, + "indexpattern": { + "layers": {} + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.10.0-SNAPSHOT" + }, + "d42c4870-c028-4d8a-abd0-0effbc190ce3": { + "type": "lens", + "gridData": { + "x": 6, + "y": 8, + "w": 6, + "h": 8, + "i": "d42c4870-c028-4d8a-abd0-0effbc190ce3", + "row": 1 + }, + "explicitInput": { + "id": "d42c4870-c028-4d8a-abd0-0effbc190ce3", + "title": "", + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-ebfc4825-b7b6-4113-bd22-f1f07a4113e9" + } + ], + "state": { + "visualization": { + "layerId": "ebfc4825-b7b6-4113-bd22-f1f07a4113e9", + "layerType": "data", + "metricAccessor": "0e1418b8-3896-416c-8f78-9e832e5c42c8", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 3, + "name": "custom", + "reverse": false, + "rangeType": "number", + "rangeMin": 0, + "rangeMax": null, + "progression": "fixed", + "stops": [ + { + "color": "#dee1e6", + "stop": 577.33 + }, + { + "color": "#b3b8c1", + "stop": 1154.66 + }, + { + "color": "#8c909d", + "stop": 1732 + } + ], + "continuity": "above", + "maxSteps": 5, + "colorStops": [ + { + "color": "#dee1e6", + "stop": 0 + }, + { + "color": "#b3b8c1", + "stop": 577.33 + }, + { + "color": "#8c909d", + "stop": 1154.66 + } + ] + } + }, + "showBar": false, + "icon": "empty" + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "ebfc4825-b7b6-4113-bd22-f1f07a4113e9": { + "columns": { + "0e1418b8-3896-416c-8f78-9e832e5c42c8": { + "label": "Unique Visitors", + "dataType": "number", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "clientip", + "isBucketed": false, + "params": { + "emptyAsNull": true + }, + "customLabel": true + } + }, + "columnOrder": ["0e1418b8-3896-416c-8f78-9e832e5c42c8"], + "incompleteColumns": {}, + "sampling": 1 + } + } + }, + "indexpattern": { + "layers": {} + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.10.0-SNAPSHOT" + }, + "4092d42c-f93b-4c71-a6db-8f12abf12791": { + "type": "lens", + "gridData": { + "x": 12, + "y": 8, + "w": 6, + "h": 8, + "i": "4092d42c-f93b-4c71-a6db-8f12abf12791", + "row": 1 + }, + "explicitInput": { + "id": "4092d42c-f93b-4c71-a6db-8f12abf12791", + "title": "", + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-ebfc4825-b7b6-4113-bd22-f1f07a4113e9" + } + ], + "state": { + "visualization": { + "layerId": "ebfc4825-b7b6-4113-bd22-f1f07a4113e9", + "layerType": "data", + "metricAccessor": "0e1418b8-3896-416c-8f78-9e832e5c42c8", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 3, + "name": "custom", + "reverse": false, + "rangeType": "number", + "rangeMin": 0, + "rangeMax": null, + "progression": "fixed", + "stops": [ + { + "color": "#dee1e6", + "stop": 88 + }, + { + "color": "#b3b8c1", + "stop": 176 + }, + { + "color": "#8c909d", + "stop": 264 + } + ], + "continuity": "above", + "maxSteps": 5, + "colorStops": [ + { + "color": "#dee1e6", + "stop": 0 + }, + { + "color": "#b3b8c1", + "stop": 88 + }, + { + "color": "#8c909d", + "stop": 176 + } + ] + } + }, + "showBar": false, + "icon": "mapMarker" + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "ebfc4825-b7b6-4113-bd22-f1f07a4113e9": { + "columns": { + "0e1418b8-3896-416c-8f78-9e832e5c42c8": { + "label": "Unique Visitor Locations", + "dataType": "number", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "geo.dest", + "isBucketed": false, + "params": { + "emptyAsNull": true + }, + "customLabel": true + } + }, + "columnOrder": ["0e1418b8-3896-416c-8f78-9e832e5c42c8"], + "incompleteColumns": {}, + "sampling": 1 + } + } + }, + "indexpattern": { + "layers": {} + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.10.0-SNAPSHOT" + }, + + "15": { + "type": "lens", + "gridData": { + "x": 18, + "y": 0, + "w": 30, + "h": 15, + "i": "15", + "row": 2 + }, + "explicitInput": { + "id": "15", + "title": "[Logs] Response Codes Over Time + Annotations", + "enhancements": { + "dynamicActions": { + "events": [] + } + }, + "hidePanelTitles": false, + "syncColors": false, + "syncCursor": true, + "syncTooltips": false, + "searchSessionId": "c35ac8a2-8ced-403c-8d3d-81e07e633c44", + "filters": [], + "query": { + "query": "", + "language": "kuery" + }, + "attributes": { + "title": "[Logs] Response Codes Over Time + Annotations (converted)", + "visualizationType": "lnsXY", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-b38fe501-4b47-4de8-a423-6656d1162174" + }, + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "xy-visualization-layer-f265e722-ae38-495c-903c-48aa7931fa82" + } + ], + "state": { + "visualization": { + "legend": { + "isVisible": true, + "showSingleSeries": true, + "position": "bottom", + "shouldTruncate": true, + "maxLines": 1 + }, + "valueLabels": "hide", + "fittingFunction": "None", + "fillOpacity": 0.5, + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + }, + "yLeftScale": "linear", + "yRightScale": "linear", + "axisTitlesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "labelsOrientation": { + "x": 0, + "yLeft": 0, + "yRight": 0 + }, + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "preferredSeriesType": "bar_stacked", + "layers": [ + { + "seriesType": "area_percentage_stacked", + "layerType": "data", + "layerId": "b38fe501-4b47-4de8-a423-6656d1162174", + "accessors": ["896c5eb2-81c5-44f1-a4a1-57344161ea62"], + "yConfig": [ + { + "forAccessor": "896c5eb2-81c5-44f1-a4a1-57344161ea62", + "color": "rgba(115,216,255,1)", + "axisMode": "left" + } + ], + "xAccessor": "8986e393-d24f-49b0-96ca-118fd66d75e5", + "splitAccessor": "43f5bb0f-c6da-43a0-8a0a-50e9838ed34b", + "palette": { + "name": "default", + "type": "palette" + }, + "colorMapping": { + "assignments": [], + "specialAssignments": [ + { + "rule": { + "type": "other" + }, + "color": { + "type": "loop" + }, + "touched": false + } + ], + "paletteId": "eui_amsterdam_color_blind", + "colorMode": { + "type": "categorical" + } + } + }, + { + "layerId": "f265e722-ae38-495c-903c-48aa7931fa82", + "layerType": "annotations", + "ignoreGlobalFilters": true, + "annotations": [ + { + "type": "query", + "id": "bd7548a0-2223-11e8-832f-d5027f3c8a47", + "label": "Event", + "key": { + "type": "point_in_time" + }, + "color": "#D33115", + "timeField": "timestamp", + "icon": "asterisk", + "filter": { + "type": "kibana_query", + "query": "tags:error AND tags:security", + "language": "lucene" + }, + "extraFields": ["geo.src"] + } + ], + "indexPatternId": "90943e30-9a47-11e8-b64d-95841ca0b247" + } + ] + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "b38fe501-4b47-4de8-a423-6656d1162174": { + "columns": { + "8986e393-d24f-49b0-96ca-118fd66d75e5": { + "label": "timestamp", + "dataType": "date", + "operationType": "date_histogram", + "sourceField": "timestamp", + "isBucketed": true, + "scale": "interval", + "params": { + "interval": "auto", + "includeEmptyRows": true, + "dropPartials": false + } + }, + "43f5bb0f-c6da-43a0-8a0a-50e9838ed34b": { + "label": "Filters", + "dataType": "string", + "operationType": "filters", + "scale": "ordinal", + "isBucketed": true, + "params": { + "filters": [ + { + "input": { + "query": "response.keyword >= 200 and response.keyword < 400", + "language": "kuery" + }, + "label": "HTTP 2xx and 3xx" + }, + { + "input": { + "query": "response.keyword >= 400 and response.keyword < 500", + "language": "kuery" + }, + "label": "HTTP 4xx" + }, + { + "input": { + "query": "response.keyword >= 500", + "language": "kuery" + }, + "label": "HTTP 5xx" + } + ] + } + }, + "896c5eb2-81c5-44f1-a4a1-57344161ea62": { + "label": "Response Code Count", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "params": { + "emptyAsNull": true + }, + "customLabel": true + } + }, + "columnOrder": [ + "8986e393-d24f-49b0-96ca-118fd66d75e5", + "43f5bb0f-c6da-43a0-8a0a-50e9838ed34b", + "896c5eb2-81c5-44f1-a4a1-57344161ea62" + ], + "incompleteColumns": {}, + "indexPatternId": "90943e30-9a47-11e8-b64d-95841ca0b247" + } + }, + "currentIndexPatternId": "90943e30-9a47-11e8-b64d-95841ca0b247" + }, + "textBased": { + "layers": {}, + "indexPatternRefs": [ + { + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "title": "kibana_sample_data_logs", + "timeField": "timestamp" + } + ] + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + } + } + }, + "4e64d6d7-4f92-4d5e-abbb-13796604db30": { + "type": "visualization", + "gridData": { + "x": 0, + "y": 0, + "w": 18, + "h": 8, + "i": "4e64d6d7-4f92-4d5e-abbb-13796604db30", + "row": 2 + }, + "explicitInput": { + "id": "4e64d6d7-4f92-4d5e-abbb-13796604db30v", + "savedVis": { + "title": "[Logs] Markdown Instructions", + "description": "", + "type": "markdown", + "params": { + "fontSize": 12, + "openLinksInNewTab": true, + "markdown": "#### Sample Logs Data\n### Response Codes\nThis dashboard uses the `[Logs]` sample data for you to play with and includes visualizations related to the response codes of the sample requests. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html)." + }, + "uiState": {}, + "data": { + "aggs": [], + "searchSource": { + "query": { + "query": "", + "language": "kuery" + }, + "filter": [] + } + } + }, + "enhancements": {}, + "hidePanelTitles": true + }, + "version": "8.10.0-SNAPSHOT" + }, + "ddce4ad8-6a82-44f0-9995-57f46f153f50": { + "type": "lens", + "gridData": { + "x": 0, + "y": 8, + "w": 6, + "h": 7, + "i": "ddce4ad8-6a82-44f0-9995-57f46f153f50", + "row": 2 + }, + "explicitInput": { + "id": "ddce4ad8-6a82-44f0-9995-57f46f153f50", + "title": "", + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a" + } + ], + "state": { + "visualization": { + "layerId": "f3793bb7-3971-4753-866d-4008e77a9f9a", + "layerType": "data", + "metricAccessor": "71c076a6-e782-4866-b8df-5fd85a41f08b", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 3, + "name": "custom", + "reverse": false, + "rangeType": "number", + "rangeMin": 0, + "rangeMax": null, + "progression": "fixed", + "stops": [ + { + "color": "#dfdfdf", + "stop": 0.25 + }, + { + "color": "#c9f0e5", + "stop": 0.5 + }, + { + "color": "#99dcca", + "stop": 0.75 + }, + { + "color": "#54B399", + "stop": 1 + }, + { + "color": "#36967c", + "stop": 1.8423645320197044 + } + ], + "colorStops": [ + { + "color": "#dfdfdf", + "stop": 0 + }, + { + "color": "#c9f0e5", + "stop": 0.25 + }, + { + "color": "#99dcca", + "stop": 0.5 + }, + { + "color": "#54B399", + "stop": 0.75 + }, + { + "color": "#36967c", + "stop": 1 + } + ], + "continuity": "above", + "maxSteps": 5 + } + } + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "f3793bb7-3971-4753-866d-4008e77a9f9a": { + "columns": { + "71c076a6-e782-4866-b8df-5fd85a41f08bX0": { + "label": "Part of HTTP 2xx", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "response.keyword >= 200 and response.keyword < 300", + "language": "kuery" + }, + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "71c076a6-e782-4866-b8df-5fd85a41f08bX1": { + "label": "Part of HTTP 2xx", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "71c076a6-e782-4866-b8df-5fd85a41f08bX2": { + "label": "Part of HTTP 2xx", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "71c076a6-e782-4866-b8df-5fd85a41f08bX0", + "71c076a6-e782-4866-b8df-5fd85a41f08bX1" + ], + "location": { + "min": 0, + "max": 73 + }, + "text": "count(kql='response.keyword >= 200 and response.keyword < 300') / count()" + } + }, + "references": [ + "71c076a6-e782-4866-b8df-5fd85a41f08bX0", + "71c076a6-e782-4866-b8df-5fd85a41f08bX1" + ], + "customLabel": true + }, + "71c076a6-e782-4866-b8df-5fd85a41f08b": { + "label": "HTTP 2xx", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count(kql='response.keyword >= 200 and response.keyword < 300') / count()", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 2, + "compact": true + } + } + }, + "references": ["71c076a6-e782-4866-b8df-5fd85a41f08bX2"], + "customLabel": true + } + }, + "columnOrder": [ + "71c076a6-e782-4866-b8df-5fd85a41f08b", + "71c076a6-e782-4866-b8df-5fd85a41f08bX0", + "71c076a6-e782-4866-b8df-5fd85a41f08bX1", + "71c076a6-e782-4866-b8df-5fd85a41f08bX2" + ], + "incompleteColumns": {} + } + } + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.10.0-SNAPSHOT" + }, + "a2884704-db3b-4b92-a19a-cdfe668dec39": { + "type": "lens", + "gridData": { + "x": 6, + "y": 8, + "w": 6, + "h": 7, + "i": "a2884704-db3b-4b92-a19a-cdfe668dec39", + "row": 2 + }, + "explicitInput": { + "id": "a2884704-db3b-4b92-a19a-cdfe668dec39", + "title": "", + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a" + } + ], + "state": { + "visualization": { + "layerId": "f3793bb7-3971-4753-866d-4008e77a9f9a", + "layerType": "data", + "metricAccessor": "71c076a6-e782-4866-b8df-5fd85a41f08b", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 3, + "name": "custom", + "reverse": false, + "rangeType": "number", + "rangeMin": 0, + "rangeMax": null, + "progression": "fixed", + "stops": [ + { + "color": "#ffe4ed", + "stop": 0.05 + }, + { + "color": "#f0b1c6", + "stop": 0.1 + }, + { + "color": "#e187a5", + "stop": 0.5 + }, + { + "color": "#D36086", + "stop": 0.9 + }, + { + "color": "#b1315b", + "stop": 1.9 + } + ], + "colorStops": [ + { + "color": "#ffe4ed", + "stop": 0 + }, + { + "color": "#f0b1c6", + "stop": 0.05 + }, + { + "color": "#e187a5", + "stop": 0.1 + }, + { + "color": "#D36086", + "stop": 0.5 + }, + { + "color": "#b1315b", + "stop": 0.9 + } + ], + "continuity": "above", + "maxSteps": 5 + } + } + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "f3793bb7-3971-4753-866d-4008e77a9f9a": { + "columns": { + "71c076a6-e782-4866-b8df-5fd85a41f08bX0": { + "label": "Part of HTTP 4xx", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "response.keyword >= 400 and response.keyword < 500", + "language": "kuery" + }, + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "71c076a6-e782-4866-b8df-5fd85a41f08bX1": { + "label": "Part of HTTP 4xx", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "71c076a6-e782-4866-b8df-5fd85a41f08bX2": { + "label": "Part of HTTP 4xx", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "71c076a6-e782-4866-b8df-5fd85a41f08bX0", + "71c076a6-e782-4866-b8df-5fd85a41f08bX1" + ], + "location": { + "min": 0, + "max": 73 + }, + "text": "count(kql='response.keyword >= 400 and response.keyword < 500') / count()" + } + }, + "references": [ + "71c076a6-e782-4866-b8df-5fd85a41f08bX0", + "71c076a6-e782-4866-b8df-5fd85a41f08bX1" + ], + "customLabel": true + }, + "71c076a6-e782-4866-b8df-5fd85a41f08b": { + "label": "HTTP 4xx", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count(kql='response.keyword >= 400 and response.keyword < 500') / count()", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 2, + "compact": true + } + } + }, + "references": ["71c076a6-e782-4866-b8df-5fd85a41f08bX2"], + "customLabel": true + } + }, + "columnOrder": [ + "71c076a6-e782-4866-b8df-5fd85a41f08b", + "71c076a6-e782-4866-b8df-5fd85a41f08bX0", + "71c076a6-e782-4866-b8df-5fd85a41f08bX1", + "71c076a6-e782-4866-b8df-5fd85a41f08bX2" + ], + "incompleteColumns": {} + } + } + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.10.0-SNAPSHOT" + }, + "529eec49-10e2-4a40-9c77-5c81f4eb3943": { + "type": "lens", + "gridData": { + "x": 12, + "y": 8, + "w": 6, + "h": 7, + "i": "529eec49-10e2-4a40-9c77-5c81f4eb3943", + "row": 2 + }, + "explicitInput": { + "id": "529eec49-10e2-4a40-9c77-5c81f4eb3943", + "title": "", + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-f3793bb7-3971-4753-866d-4008e77a9f9a" + } + ], + "state": { + "visualization": { + "layerId": "f3793bb7-3971-4753-866d-4008e77a9f9a", + "layerType": "data", + "metricAccessor": "71c076a6-e782-4866-b8df-5fd85a41f08b", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 3, + "name": "custom", + "reverse": false, + "rangeType": "number", + "rangeMin": 0, + "rangeMax": null, + "progression": "fixed", + "stops": [ + { + "color": "#ffe4ed", + "stop": 0.05 + }, + { + "color": "#f0b1c6", + "stop": 0.1 + }, + { + "color": "#e187a5", + "stop": 0.5 + }, + { + "color": "#D36086", + "stop": 0.9 + }, + { + "color": "#b1315b", + "stop": 1.9 + } + ], + "colorStops": [ + { + "color": "#ffe4ed", + "stop": 0 + }, + { + "color": "#f0b1c6", + "stop": 0.05 + }, + { + "color": "#e187a5", + "stop": 0.1 + }, + { + "color": "#D36086", + "stop": 0.5 + }, + { + "color": "#b1315b", + "stop": 0.9 + } + ], + "continuity": "above", + "maxSteps": 5 + } + } + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "f3793bb7-3971-4753-866d-4008e77a9f9a": { + "columns": { + "71c076a6-e782-4866-b8df-5fd85a41f08bX0": { + "label": "Part of HTTP 5xx", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "response.keyword >= 500 and response.keyword < 600", + "language": "kuery" + }, + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "71c076a6-e782-4866-b8df-5fd85a41f08bX1": { + "label": "Part of HTTP 5xx", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "71c076a6-e782-4866-b8df-5fd85a41f08bX2": { + "label": "Part of HTTP 5xx", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "71c076a6-e782-4866-b8df-5fd85a41f08bX0", + "71c076a6-e782-4866-b8df-5fd85a41f08bX1" + ], + "location": { + "min": 0, + "max": 73 + }, + "text": "count(kql='response.keyword >= 500 and response.keyword < 600') / count()" + } + }, + "references": [ + "71c076a6-e782-4866-b8df-5fd85a41f08bX0", + "71c076a6-e782-4866-b8df-5fd85a41f08bX1" + ], + "customLabel": true + }, + "71c076a6-e782-4866-b8df-5fd85a41f08b": { + "label": "HTTP 5xx", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count(kql='response.keyword >= 500 and response.keyword < 600') / count()", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 2, + "compact": true + } + } + }, + "references": ["71c076a6-e782-4866-b8df-5fd85a41f08bX2"], + "customLabel": true + } + }, + "columnOrder": [ + "71c076a6-e782-4866-b8df-5fd85a41f08b", + "71c076a6-e782-4866-b8df-5fd85a41f08bX0", + "71c076a6-e782-4866-b8df-5fd85a41f08bX1", + "71c076a6-e782-4866-b8df-5fd85a41f08bX2" + ], + "incompleteColumns": {} + } + } + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.10.0-SNAPSHOT" + }, + "1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b": { + "type": "lens", + "gridData": { + "x": 0, + "y": 15, + "w": 48, + "h": 12, + "i": "1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b", + "row": 2 + }, + "explicitInput": { + "id": "1d5f0b3f-d9d2-4b26-997b-83bc5ca3090b", + "title": "[Logs] Errors by host", + "attributes": { + "title": "", + "description": "", + "visualizationType": "lnsDatatable", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "90943e30-9a47-11e8-b64d-95841ca0b247", + "name": "indexpattern-datasource-layer-c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0" + } + ], + "state": { + "visualization": { + "layerId": "c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0", + "columns": [ + { + "columnId": "42783ad7-dbcf-4310-bc06-f21f4eaaac96", + "width": 650.6666666666666 + }, + { + "columnId": "f7835375-4d5b-4839-95ea-41928192a319" + }, + { + "columnId": "491285fd-0196-402c-9b7f-4660fdc1c22a", + "isTransposed": false, + "width": 81.66666666666669, + "colorMode": "cell", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 5, + "stops": [ + { + "color": "#FFDAE6", + "stop": 0.1 + }, + { + "color": "#D36086", + "stop": 1 + } + ], + "rangeType": "number", + "name": "custom", + "colorStops": [ + { + "color": "#FFDAE6", + "stop": 0.05 + }, + { + "color": "#D36086", + "stop": 0.1 + } + ], + "rangeMin": 0.05, + "rangeMax": null, + "continuity": "above" + } + } + }, + { + "columnId": "07fc84ca-4147-4ba9-879e-d1b4e086e1da", + "isTransposed": false, + "colorMode": "cell", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 5, + "stops": [ + { + "color": "#ffdae6", + "stop": 0.1 + }, + { + "color": "#D36086", + "stop": 1 + } + ], + "name": "custom", + "colorStops": [ + { + "color": "#ffdae6", + "stop": 0.05 + }, + { + "color": "#D36086", + "stop": 0.1 + } + ], + "rangeType": "number", + "rangeMin": 0.05, + "rangeMax": null, + "continuity": "above" + } + } + }, + { + "columnId": "791d5a5b-a7ba-4e9e-b533-51b33c7d7747", + "isTransposed": false + }, + { + "columnId": "611e3509-e834-4fdd-b573-44e959e95d27", + "isTransposed": false + }, + { + "columnId": "9f79ecca-123f-4098-a658-6b0e998da003", + "isTransposed": false + } + ], + "sorting": { + "columnId": "491285fd-0196-402c-9b7f-4660fdc1c22a", + "direction": "desc" + }, + "layerType": "data", + "rowHeight": "single", + "rowHeightLines": 1 + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "c35dc8ee-50d1-4ef7-8b4b-9c21a7e7d3b0": { + "columns": { + "42783ad7-dbcf-4310-bc06-f21f4eaaac96": { + "label": "URL", + "dataType": "string", + "operationType": "terms", + "scale": "ordinal", + "sourceField": "url.keyword", + "isBucketed": true, + "params": { + "size": 1000, + "orderBy": { + "type": "column", + "columnId": "f7835375-4d5b-4839-95ea-41928192a319" + }, + "orderDirection": "desc", + "otherBucket": true, + "missingBucket": false + }, + "customLabel": true + }, + "f7835375-4d5b-4839-95ea-41928192a319": { + "label": "Visits", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "customLabel": true + }, + "07fc84ca-4147-4ba9-879e-d1b4e086e1daX0": { + "label": "Part of HTTP 4xx", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "response.keyword >= 400 and response.keyword < 500", + "language": "kuery" + }, + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "07fc84ca-4147-4ba9-879e-d1b4e086e1daX1": { + "label": "Part of HTTP 4xx", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "07fc84ca-4147-4ba9-879e-d1b4e086e1daX2": { + "label": "Part of HTTP 4xx", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "07fc84ca-4147-4ba9-879e-d1b4e086e1daX0", + "07fc84ca-4147-4ba9-879e-d1b4e086e1daX1" + ], + "location": { + "min": 0, + "max": 73 + }, + "text": "count(kql='response.keyword >= 400 and response.keyword < 500') / count()" + } + }, + "references": [ + "07fc84ca-4147-4ba9-879e-d1b4e086e1daX0", + "07fc84ca-4147-4ba9-879e-d1b4e086e1daX1" + ], + "customLabel": true + }, + "07fc84ca-4147-4ba9-879e-d1b4e086e1da": { + "label": "HTTP 4xx", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count(kql='response.keyword >= 400 and response.keyword < 500') / count()", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 1 + } + } + }, + "references": ["07fc84ca-4147-4ba9-879e-d1b4e086e1daX2"], + "customLabel": true + }, + "791d5a5b-a7ba-4e9e-b533-51b33c7d7747": { + "label": "Unique", + "dataType": "number", + "operationType": "unique_count", + "scale": "ratio", + "sourceField": "clientip", + "isBucketed": false, + "customLabel": true + }, + "611e3509-e834-4fdd-b573-44e959e95d27": { + "label": "95th percentile of bytes", + "dataType": "number", + "operationType": "percentile", + "sourceField": "bytes", + "isBucketed": false, + "scale": "ratio", + "params": { + "percentile": 95, + "format": { + "id": "bytes", + "params": { + "decimals": 0 + } + } + } + }, + "9f79ecca-123f-4098-a658-6b0e998da003": { + "label": "Median of bytes", + "dataType": "number", + "operationType": "median", + "sourceField": "bytes", + "isBucketed": false, + "scale": "ratio", + "params": { + "format": { + "id": "bytes", + "params": { + "decimals": 0 + } + } + } + }, + "491285fd-0196-402c-9b7f-4660fdc1c22aX0": { + "label": "Part of HTTP 5xx", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "response.keyword >= 500", + "language": "kuery" + }, + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "491285fd-0196-402c-9b7f-4660fdc1c22aX1": { + "label": "Part of HTTP 5xx", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "491285fd-0196-402c-9b7f-4660fdc1c22aX2": { + "label": "Part of HTTP 5xx", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "491285fd-0196-402c-9b7f-4660fdc1c22aX0", + "491285fd-0196-402c-9b7f-4660fdc1c22aX1" + ], + "location": { + "min": 0, + "max": 46 + }, + "text": "count(kql='response.keyword >= 500') / count()" + } + }, + "references": [ + "491285fd-0196-402c-9b7f-4660fdc1c22aX0", + "491285fd-0196-402c-9b7f-4660fdc1c22aX1" + ], + "customLabel": true + }, + "491285fd-0196-402c-9b7f-4660fdc1c22a": { + "label": "HTTP 5xx", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count(kql='response.keyword >= 500') / count()", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 1 + } + } + }, + "references": ["491285fd-0196-402c-9b7f-4660fdc1c22aX2"], + "customLabel": true + } + }, + "columnOrder": [ + "42783ad7-dbcf-4310-bc06-f21f4eaaac96", + "f7835375-4d5b-4839-95ea-41928192a319", + "791d5a5b-a7ba-4e9e-b533-51b33c7d7747", + "07fc84ca-4147-4ba9-879e-d1b4e086e1da", + "491285fd-0196-402c-9b7f-4660fdc1c22a", + "491285fd-0196-402c-9b7f-4660fdc1c22aX0", + "491285fd-0196-402c-9b7f-4660fdc1c22aX1", + "491285fd-0196-402c-9b7f-4660fdc1c22aX2", + "07fc84ca-4147-4ba9-879e-d1b4e086e1daX0", + "07fc84ca-4147-4ba9-879e-d1b4e086e1daX1", + "07fc84ca-4147-4ba9-879e-d1b4e086e1daX2", + "611e3509-e834-4fdd-b573-44e959e95d27", + "9f79ecca-123f-4098-a658-6b0e998da003" + ], + "incompleteColumns": {} + } + } + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": { + "dynamicActions": { + "events": [] + } + }, + "hidePanelTitles": false + }, + "version": "8.10.0-SNAPSHOT" + }, + + "9f79ecca-123f-4098-a658-6b0e998da003": { + "type": "search", + "gridData": { + "x": 0, + "y": 69, + "w": 48, + "h": 15, + "i": "9f79ecca-123f-4098-a658-6b0e998da003", + "row": 3 + }, + "explicitInput": { + "id": "9f79ecca-123f-4098-a658-6b0e998da003", + "savedObjectId": "571aaf70-4c88-11e8-b3d7-01146121b73d", + "enhancements": {} + }, + "version": "8.8.0" + }, + "7": { + "type": "lens", + "gridData": { + "x": 0, + "y": 16, + "w": 24, + "h": 9, + "i": "7", + "row": 3 + }, + "explicitInput": { + "id": "7", + "title": "[Flights] Delays & Cancellations", + "attributes": { + "title": "[Flights] Delays & Cancellations (converted)", + "visualizationType": "lnsXY", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-dc8cf715-b56b-4dd7-a624-7c3ef9e2f2ce" + }, + { + "type": "index-pattern", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "xy-visualization-layer-10fed425-accd-411b-a773-ee825bc3945b" + } + ], + "state": { + "visualization": { + "legend": { + "isVisible": false, + "showSingleSeries": false, + "position": "bottom", + "shouldTruncate": true, + "maxLines": 1 + }, + "valueLabels": "hide", + "fittingFunction": "None", + "fillOpacity": 0.5, + "yLeftExtent": { + "upperBound": 1, + "mode": "custom" + }, + "yRightExtent": { + "mode": "full" + }, + "yLeftScale": "linear", + "yRightScale": "linear", + "axisTitlesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "labelsOrientation": { + "x": 0, + "yLeft": 0, + "yRight": 0 + }, + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "preferredSeriesType": "bar_stacked", + "layers": [ + { + "seriesType": "area", + "layerType": "data", + "layerId": "dc8cf715-b56b-4dd7-a624-7c3ef9e2f2ce", + "accessors": ["b9d6187b-8a7e-4c49-bffd-7a60cc075a4a"], + "yConfig": [ + { + "forAccessor": "b9d6187b-8a7e-4c49-bffd-7a60cc075a4a", + "color": "rgba(0,156,224,1)", + "axisMode": "left" + } + ], + "xAccessor": "6944431b-f90a-4dab-a282-0961cb97edd1", + "palette": { + "name": "default", + "type": "palette" + } + }, + { + "layerId": "10fed425-accd-411b-a773-ee825bc3945b", + "layerType": "annotations", + "ignoreGlobalFilters": true, + "annotations": [ + { + "type": "query", + "id": "53b7dff0-4c89-11e8-a66a-6989ad5a0a39", + "label": "Event", + "key": { + "type": "point_in_time" + }, + "timeField": "timestamp", + "color": "#0062B1", + "icon": "alert", + "filter": { + "type": "kibana_query", + "query": "FlightDelay:true AND Cancelled:true", + "language": "lucene" + }, + "extraFields": ["FlightDelay", "Cancelled", "Carrier"] + } + ] + } + ] + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "dc8cf715-b56b-4dd7-a624-7c3ef9e2f2ce": { + "columns": { + "6944431b-f90a-4dab-a282-0961cb97edd1": { + "label": "timestamp", + "dataType": "date", + "operationType": "date_histogram", + "sourceField": "timestamp", + "isBucketed": true, + "scale": "interval", + "params": { + "interval": "auto", + "includeEmptyRows": true, + "dropPartials": false + } + }, + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX0": { + "label": "Part of count(lucene='FlightDelay:true') / count(kql='*')", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "FlightDelay:true", + "language": "lucene" + }, + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX1": { + "label": "Part of count(lucene='FlightDelay:true') / count(kql='*')", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "*", + "language": "kuery" + }, + "params": { + "emptyAsNull": false + }, + "customLabel": true + }, + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX2": { + "label": "Part of count(lucene='FlightDelay:true') / count(kql='*')", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX0", + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX1" + ], + "location": { + "min": 0, + "max": 49 + }, + "text": "count(lucene='FlightDelay:true') / count(kql='*')" + } + }, + "references": [ + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX0", + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX1" + ], + "customLabel": true + }, + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4a": { + "label": "Percent Delays", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "format": { + "id": "percent" + }, + "formula": "count(lucene='FlightDelay:true') / count(kql='*')", + "isFormulaBroken": false + }, + "references": ["b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX2"], + "customLabel": true + } + }, + "columnOrder": [ + "6944431b-f90a-4dab-a282-0961cb97edd1", + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX0", + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX1", + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4aX2", + "b9d6187b-8a7e-4c49-bffd-7a60cc075a4a" + ], + "incompleteColumns": {} + } + } + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {}, + "hidePanelTitles": false + }, + "version": "8.9.0" + }, + "10": { + "type": "lens", + "gridData": { + "x": 0, + "y": 58, + "w": 24, + "h": 11, + "i": "10", + "row": 3 + }, + "explicitInput": { + "id": "10", + "title": "[Flights] Delay Buckets", + "attributes": { + "title": "[Flights] Delay Buckets (converted)", + "visualizationType": "lnsXY", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-247b176d-e81d-47dc-bbd1-4de85d098961" + }, + { + "type": "index-pattern", + "name": "c61f29fc-1ff3-4d46-a6c2-bde8a5b97f4a", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d" + } + ], + "state": { + "visualization": { + "legend": { + "isVisible": false, + "position": "right", + "legendSize": "auto", + "shouldTruncate": true, + "maxLines": 1, + "showSingleSeries": true + }, + "valueLabels": "hide", + "curveType": "LINEAR", + "yTitle": "Count", + "yLeftExtent": { + "mode": "full", + "enforce": true + }, + "yLeftScale": "linear", + "yRightScale": "linear", + "axisTitlesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "labelsOrientation": { + "x": 0, + "yLeft": 0, + "yRight": -90 + }, + "gridlinesVisibilitySettings": { + "x": false, + "yLeft": false, + "yRight": true + }, + "preferredSeriesType": "bar_stacked", + "layers": [ + { + "layerId": "247b176d-e81d-47dc-bbd1-4de85d098961", + "accessors": ["c84f9fa9-e2a1-4845-8fcf-f5a34f5f92da"], + "layerType": "data", + "seriesType": "bar_stacked", + "xAccessor": "c7936be7-e59b-424e-a912-69ba820d8e24", + "simpleView": false, + "palette": { + "type": "palette", + "name": "default" + }, + "yConfig": [ + { + "forAccessor": "c84f9fa9-e2a1-4845-8fcf-f5a34f5f92da", + "axisMode": "left", + "color": "#1F78C1" + } + ], + "xScaleType": "linear", + "isHistogram": true + } + ] + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [ + { + "meta": { + "negate": true, + "disabled": false, + "alias": null, + "type": "phrase", + "key": "FlightDelayMin", + "value": "0", + "params": { + "query": 0, + "type": "phrase" + }, + "index": "c61f29fc-1ff3-4d46-a6c2-bde8a5b97f4a" + }, + "query": { + "match": { + "FlightDelayMin": { + "query": 0, + "type": "phrase" + } + } + }, + "$state": { + "store": "appState" + } + } + ], + "datasourceStates": { + "formBased": { + "layers": { + "247b176d-e81d-47dc-bbd1-4de85d098961": { + "columns": { + "c7936be7-e59b-424e-a912-69ba820d8e24": { + "label": "Flight Delay Minutes", + "dataType": "number", + "operationType": "range", + "sourceField": "FlightDelayMin", + "isBucketed": true, + "scale": "interval", + "params": { + "includeEmptyRows": false, + "type": "histogram", + "ranges": [ + { + "from": 0, + "to": 1000, + "label": "" + } + ], + "maxBars": "auto" + }, + "customLabel": true + }, + "c84f9fa9-e2a1-4845-8fcf-f5a34f5f92da": { + "label": "Count", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "params": { + "emptyAsNull": true + }, + "customLabel": true + } + }, + "columnOrder": [ + "c7936be7-e59b-424e-a912-69ba820d8e24", + "c84f9fa9-e2a1-4845-8fcf-f5a34f5f92da" + ], + "incompleteColumns": {} + } + } + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {}, + "hidePanelTitles": false + }, + "version": "8.9.0" + }, + "23": { + "type": "map", + "gridData": { + "x": 0, + "y": 36, + "w": 24, + "h": 22, + "i": "23", + "row": 3 + }, + "explicitInput": { + "id": "23", + "savedObjectId": "5dd88580-1906-11e9-919b-ffe5949a18d2", + "isLayerTOCOpen": true, + "hiddenLayers": [], + "mapCenter": { + "lat": 48.72307, + "lon": -115.18171, + "zoom": 4.28 + }, + "openTOCDetails": [], + "enhancements": {} + }, + "version": "8.8.0" + }, + "31": { + "type": "visualization", + "gridData": { + "x": 24, + "y": 36, + "w": 24, + "h": 22, + "i": "31", + "row": 3 + }, + "explicitInput": { + "id": "31", + "savedObjectId": "ed78a660-53a0-11e8-acbd-0be0ad9d822b", + "enhancements": {} + }, + "version": "8.8.0" + }, + "6afc61f7-e2d5-45a3-9e7a-281160ad3eb9": { + "type": "visualization", + "gridData": { + "x": 0, + "y": 0, + "w": 24, + "h": 8, + "i": "6afc61f7-e2d5-45a3-9e7a-281160ad3eb9", + "row": 3 + }, + "explicitInput": { + "id": "6afc61f7-e2d5-45a3-9e7a-281160ad3eb9", + "savedVis": { + "title": "[Flights] Markdown Instructions", + "description": "", + "type": "markdown", + "params": { + "fontSize": 10, + "openLinksInNewTab": true, + "markdown": "## Sample Flight data\nThis dashboard contains sample data for you to play with. You can view it, search it, and interact with the visualizations. For more information about Kibana, check our [docs](https://www.elastic.co/guide/en/kibana/current/index.html)." + }, + "uiState": {}, + "data": { + "aggs": [], + "searchSource": {} + } + }, + "hidePanelTitles": true, + "enhancements": {} + }, + "version": "8.9.0" + }, + "392b4936-f753-47bc-a98d-a4e41a0a4cd4": { + "type": "lens", + "gridData": { + "x": 24, + "y": 0, + "w": 8, + "h": 8, + "i": "392b4936-f753-47bc-a98d-a4e41a0a4cd4", + "row": 3 + }, + "explicitInput": { + "id": "392b4936-f753-47bc-a98d-a4e41a0a4cd4", + "enhancements": {}, + "attributes": { + "title": "[Flights] Total Flights", + "description": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-8fa993db-c147-4954-adf7-4ff264d42576" + } + ], + "state": { + "visualization": { + "layerId": "8fa993db-c147-4954-adf7-4ff264d42576", + "layerType": "data", + "metricAccessor": "81124c45-6ab6-42f4-8859-495d55eb8065" + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "8fa993db-c147-4954-adf7-4ff264d42576": { + "columns": { + "81124c45-6ab6-42f4-8859-495d55eb8065": { + "label": "Total flights", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "customLabel": true + } + }, + "columnOrder": ["81124c45-6ab6-42f4-8859-495d55eb8065"], + "incompleteColumns": {} + } + } + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "hidePanelTitles": true + }, + "version": "8.9.0" + }, + "9271deff-5a61-4665-83fc-f9fdc6bf0c0b": { + "type": "lens", + "gridData": { + "x": 32, + "y": 0, + "w": 8, + "h": 4, + "i": "9271deff-5a61-4665-83fc-f9fdc6bf0c0b", + "row": 3 + }, + "explicitInput": { + "id": "9271deff-5a61-4665-83fc-f9fdc6bf0c0b", + "attributes": { + "title": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317" + } + ], + "state": { + "visualization": { + "layerId": "b4712d43-1e84-4f5b-878d-8e38ba748317", + "layerType": "data", + "metricAccessor": "7e8fe9b1-f45c-4f3d-9561-30febcd357ec" + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "b4712d43-1e84-4f5b-878d-8e38ba748317": { + "columns": { + "7e8fe9b1-f45c-4f3d-9561-30febcd357ec": { + "label": "Delayed", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count(kql='FlightDelay : true') / count()", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 1, + "compact": true + } + } + }, + "references": ["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2"], + "customLabel": true + }, + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0": { + "label": "Part of count(kql='FlightDelay : true') / count()", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "FlightDelay : true", + "language": "kuery" + }, + "customLabel": true + }, + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1": { + "label": "Part of count(kql='FlightDelay : true') / count()", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "customLabel": true + }, + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2": { + "label": "Part of count(kql='FlightDelay : true') / count()", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0", + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1" + ], + "location": { + "min": 0, + "max": 41 + }, + "text": "count(kql='FlightDelay : true') / count()" + } + }, + "references": [ + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0", + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1" + ], + "customLabel": true + } + }, + "columnOrder": [ + "7e8fe9b1-f45c-4f3d-9561-30febcd357ec", + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0", + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1", + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2" + ], + "incompleteColumns": {} + } + } + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.9.0" + }, + "aa591c29-1a31-4ee1-a71d-b829c06fd162": { + "type": "lens", + "gridData": { + "x": 40, + "y": 0, + "w": 8, + "h": 4, + "i": "aa591c29-1a31-4ee1-a71d-b829c06fd162", + "row": 3 + }, + "explicitInput": { + "id": "aa591c29-1a31-4ee1-a71d-b829c06fd162", + "attributes": { + "title": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317" + }, + { + "type": "index-pattern", + "name": "c804c161-375f-4d52-a1cc-2e98b966957d", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d" + } + ], + "state": { + "visualization": { + "layerId": "b4712d43-1e84-4f5b-878d-8e38ba748317", + "layerType": "data", + "metricAccessor": "c7851241-5526-499a-960b-357af8c2ce5b" + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [ + { + "meta": { + "alias": null, + "negate": false, + "disabled": false, + "type": "phrase", + "key": "FlightDelay", + "params": { + "query": true + }, + "index": "c804c161-375f-4d52-a1cc-2e98b966957d" + }, + "query": { + "match_phrase": { + "FlightDelay": true + } + }, + "$state": { + "store": "appState" + } + } + ], + "datasourceStates": { + "formBased": { + "layers": { + "b4712d43-1e84-4f5b-878d-8e38ba748317": { + "columns": { + "c7851241-5526-499a-960b-357af8c2ce5b": { + "label": "Delayed vs 1 week earlier", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count() / count(shift='1w') - 1", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 1, + "compact": true + } + } + }, + "references": ["c7851241-5526-499a-960b-357af8c2ce5bX2"], + "customLabel": true + }, + "c7851241-5526-499a-960b-357af8c2ce5bX2": { + "label": "Part of Delayed", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "subtract", + "args": [ + { + "type": "function", + "name": "divide", + "args": [ + "c7851241-5526-499a-960b-357af8c2ce5bX0", + "c7851241-5526-499a-960b-357af8c2ce5bX1" + ], + "location": { + "min": 0, + "max": 28 + }, + "text": "count() / count(shift='1w') " + }, + 1 + ], + "location": { + "min": 0, + "max": 31 + }, + "text": "count() / count(shift='1w') - 1" + } + }, + "references": [ + "c7851241-5526-499a-960b-357af8c2ce5bX0", + "c7851241-5526-499a-960b-357af8c2ce5bX1" + ], + "customLabel": true + }, + "c7851241-5526-499a-960b-357af8c2ce5bX0": { + "label": "Part of Delayed", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "customLabel": true + }, + "c7851241-5526-499a-960b-357af8c2ce5bX1": { + "label": "Part of Delayed", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "timeShift": "1w", + "customLabel": true + } + }, + "columnOrder": [ + "c7851241-5526-499a-960b-357af8c2ce5b", + "c7851241-5526-499a-960b-357af8c2ce5bX2", + "c7851241-5526-499a-960b-357af8c2ce5bX0", + "c7851241-5526-499a-960b-357af8c2ce5bX1" + ], + "incompleteColumns": {} + } + } + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.9.0" + }, + "b766e3b8-4544-46ed-99e6-9ecc4847e2a2": { + "type": "lens", + "gridData": { + "x": 32, + "y": 4, + "w": 8, + "h": 4, + "i": "b766e3b8-4544-46ed-99e6-9ecc4847e2a2", + "row": 3 + }, + "explicitInput": { + "id": "b766e3b8-4544-46ed-99e6-9ecc4847e2a2", + "attributes": { + "title": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317" + } + ], + "state": { + "visualization": { + "layerId": "b4712d43-1e84-4f5b-878d-8e38ba748317", + "layerType": "data", + "metricAccessor": "7e8fe9b1-f45c-4f3d-9561-30febcd357ec" + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "b4712d43-1e84-4f5b-878d-8e38ba748317": { + "columns": { + "7e8fe9b1-f45c-4f3d-9561-30febcd357ec": { + "label": "Cancelled", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count(kql='Cancelled : true') / count()", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 1, + "compact": true + } + } + }, + "references": ["7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2"], + "customLabel": true + }, + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0": { + "label": "Part of Cancelled", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "Cancelled : true", + "language": "kuery" + }, + "customLabel": true + }, + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1": { + "label": "Part of Cancelled", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "customLabel": true + }, + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2": { + "label": "Part of Cancelled", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0", + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1" + ], + "location": { + "min": 0, + "max": 39 + }, + "text": "count(kql='Cancelled : true') / count()" + } + }, + "references": [ + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0", + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1" + ], + "customLabel": true + } + }, + "columnOrder": [ + "7e8fe9b1-f45c-4f3d-9561-30febcd357ec", + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX0", + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX1", + "7e8fe9b1-f45c-4f3d-9561-30febcd357ecX2" + ], + "incompleteColumns": {} + } + } + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.9.0" + }, + "2e33ade5-96e5-40b4-b460-493e5d4fa834": { + "type": "lens", + "gridData": { + "x": 40, + "y": 4, + "w": 8, + "h": 4, + "i": "2e33ade5-96e5-40b4-b460-493e5d4fa834", + "row": 3 + }, + "explicitInput": { + "id": "2e33ade5-96e5-40b4-b460-493e5d4fa834", + "attributes": { + "title": "", + "visualizationType": "lnsMetric", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-b4712d43-1e84-4f5b-878d-8e38ba748317" + }, + { + "type": "index-pattern", + "name": "14cea722-a629-4c69-a06d-94a4a4c9a718", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d" + } + ], + "state": { + "visualization": { + "layerId": "b4712d43-1e84-4f5b-878d-8e38ba748317", + "layerType": "data", + "metricAccessor": "c7851241-5526-499a-960b-357af8c2ce5b" + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [ + { + "meta": { + "alias": null, + "negate": false, + "disabled": false, + "type": "phrase", + "key": "Cancelled", + "params": { + "query": true + }, + "index": "14cea722-a629-4c69-a06d-94a4a4c9a718" + }, + "query": { + "match_phrase": { + "Cancelled": true + } + }, + "$state": { + "store": "appState" + } + } + ], + "datasourceStates": { + "formBased": { + "layers": { + "b4712d43-1e84-4f5b-878d-8e38ba748317": { + "columns": { + "c7851241-5526-499a-960b-357af8c2ce5b": { + "label": "Cancelled vs 1 week earlier", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count() / count(shift='1w') - 1", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 1, + "compact": true + } + } + }, + "references": ["c7851241-5526-499a-960b-357af8c2ce5bX2"], + "customLabel": true + }, + "c7851241-5526-499a-960b-357af8c2ce5bX2": { + "label": "Part of Delayed vs 1 week earlier", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "subtract", + "args": [ + { + "type": "function", + "name": "divide", + "args": [ + "c7851241-5526-499a-960b-357af8c2ce5bX0", + "c7851241-5526-499a-960b-357af8c2ce5bX1" + ], + "location": { + "min": 0, + "max": 28 + }, + "text": "count() / count(shift='1w') " + }, + 1 + ], + "location": { + "min": 0, + "max": 31 + }, + "text": "count() / count(shift='1w') - 1" + } + }, + "references": [ + "c7851241-5526-499a-960b-357af8c2ce5bX0", + "c7851241-5526-499a-960b-357af8c2ce5bX1" + ], + "customLabel": true + }, + "c7851241-5526-499a-960b-357af8c2ce5bX0": { + "label": "Part of Delayed vs 1 week earlier", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "customLabel": true + }, + "c7851241-5526-499a-960b-357af8c2ce5bX1": { + "label": "Part of Delayed vs 1 week earlier", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "timeShift": "1w", + "customLabel": true + } + }, + "columnOrder": [ + "c7851241-5526-499a-960b-357af8c2ce5b", + "c7851241-5526-499a-960b-357af8c2ce5bX2", + "c7851241-5526-499a-960b-357af8c2ce5bX0", + "c7851241-5526-499a-960b-357af8c2ce5bX1" + ], + "incompleteColumns": {} + } + } + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.9.0" + }, + "086ac2e9-dd16-4b45-92b8-1e43ff7e3f65": { + "type": "lens", + "gridData": { + "x": 0, + "y": 8, + "w": 24, + "h": 8, + "i": "086ac2e9-dd16-4b45-92b8-1e43ff7e3f65", + "row": 3 + }, + "explicitInput": { + "id": "086ac2e9-dd16-4b45-92b8-1e43ff7e3f65", + "title": "[Flights] Flight count", + "attributes": { + "title": "", + "type": "lens", + "visualizationType": "lnsXY", + "state": { + "datasourceStates": { + "formBased": { + "layers": { + "03c34665-471c-49c7-acf1-5a11f517421c": { + "columns": { + "a5b94e30-4e77-4b0a-9187-1d8b13de1456": { + "label": "timestamp", + "dataType": "date", + "operationType": "date_histogram", + "sourceField": "timestamp", + "isBucketed": true, + "scale": "interval", + "params": { + "interval": "auto", + "includeEmptyRows": true + } + }, + "3e267327-7317-4310-aee3-320e0f7c1e70": { + "label": "Count of records", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___" + } + }, + "columnOrder": [ + "a5b94e30-4e77-4b0a-9187-1d8b13de1456", + "3e267327-7317-4310-aee3-320e0f7c1e70" + ], + "incompleteColumns": {} + } + } + } + }, + "visualization": { + "legend": { + "isVisible": true, + "position": "right", + "legendSize": "auto" + }, + "valueLabels": "hide", + "fittingFunction": "None", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "custom", + "lowerBound": 0, + "upperBound": 1 + }, + "axisTitlesVisibilitySettings": { + "x": false, + "yLeft": false, + "yRight": true + }, + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "preferredSeriesType": "bar_stacked", + "layers": [ + { + "layerId": "03c34665-471c-49c7-acf1-5a11f517421c", + "accessors": ["3e267327-7317-4310-aee3-320e0f7c1e70"], + "position": "top", + "seriesType": "bar_stacked", + "showGridlines": false, + "xAccessor": "a5b94e30-4e77-4b0a-9187-1d8b13de1456", + "layerType": "data" + } + ] + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [] + }, + "references": [ + { + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-03c34665-471c-49c7-acf1-5a11f517421c", + "type": "index-pattern" + } + ] + }, + "hidePanelTitles": false, + "enhancements": {} + }, + "version": "8.9.0" + }, + "fb86b32f-fb7a-45cf-9511-f366fef51bbd": { + "type": "lens", + "gridData": { + "x": 24, + "y": 8, + "w": 24, + "h": 28, + "i": "fb86b32f-fb7a-45cf-9511-f366fef51bbd", + "row": 3 + }, + "explicitInput": { + "id": "fb86b32f-fb7a-45cf-9511-f366fef51bbd", + "title": "[Flights] Most delayed cities", + "attributes": { + "title": "Cities by delay, cancellation", + "type": "lens", + "visualizationType": "lnsDatatable", + "state": { + "datasourceStates": { + "formBased": { + "layers": { + "f26e8f7a-4118-4227-bea0-5c02d8b270f7": { + "columns": { + "3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0": { + "label": "Top values of OriginCityName", + "dataType": "string", + "operationType": "terms", + "scale": "ordinal", + "sourceField": "OriginCityName", + "isBucketed": true, + "params": { + "size": 1000, + "orderBy": { + "type": "alphabetical", + "fallback": true + }, + "orderDirection": "asc", + "otherBucket": true, + "missingBucket": false + } + }, + "52f6f2e9-6242-4c44-be63-b799150e7e60X0": { + "label": "Part of Delay %", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "FlightDelay : true ", + "language": "kuery" + }, + "customLabel": true + }, + "52f6f2e9-6242-4c44-be63-b799150e7e60X1": { + "label": "Part of Delay %", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "customLabel": true + }, + "52f6f2e9-6242-4c44-be63-b799150e7e60X2": { + "label": "Part of Delay %", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "52f6f2e9-6242-4c44-be63-b799150e7e60X0", + "52f6f2e9-6242-4c44-be63-b799150e7e60X1" + ], + "location": { + "min": 0, + "max": 42 + }, + "text": "count(kql='FlightDelay : true ') / count()" + } + }, + "references": [ + "52f6f2e9-6242-4c44-be63-b799150e7e60X0", + "52f6f2e9-6242-4c44-be63-b799150e7e60X1" + ], + "customLabel": true + }, + "52f6f2e9-6242-4c44-be63-b799150e7e60": { + "label": "Delay %", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count(kql='FlightDelay : true ') / count()", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 0 + } + } + }, + "references": ["52f6f2e9-6242-4c44-be63-b799150e7e60X2"], + "customLabel": true + }, + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0": { + "label": "Part of Cancel %", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "filter": { + "query": "Cancelled: true", + "language": "kuery" + }, + "customLabel": true + }, + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1": { + "label": "Part of Cancel %", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "customLabel": true + }, + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2": { + "label": "Part of Cancel %", + "dataType": "number", + "operationType": "math", + "isBucketed": false, + "scale": "ratio", + "params": { + "tinymathAst": { + "type": "function", + "name": "divide", + "args": [ + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0", + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1" + ], + "location": { + "min": 0, + "max": 38 + }, + "text": "count(kql='Cancelled: true') / count()" + } + }, + "references": [ + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0", + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1" + ], + "customLabel": true + }, + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6": { + "label": "Cancel %", + "dataType": "number", + "operationType": "formula", + "isBucketed": false, + "scale": "ratio", + "params": { + "formula": "count(kql='Cancelled: true') / count()", + "isFormulaBroken": false, + "format": { + "id": "percent", + "params": { + "decimals": 0 + } + } + }, + "references": ["7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2"], + "customLabel": true + } + }, + "columnOrder": [ + "3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0", + "52f6f2e9-6242-4c44-be63-b799150e7e60", + "52f6f2e9-6242-4c44-be63-b799150e7e60X0", + "52f6f2e9-6242-4c44-be63-b799150e7e60X1", + "52f6f2e9-6242-4c44-be63-b799150e7e60X2", + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X0", + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X1", + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6X2", + "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6" + ], + "incompleteColumns": {} + } + } + } + }, + "visualization": { + "columns": [ + { + "isTransposed": false, + "columnId": "3dd24cb4-45ef-4dd8-b22a-d7b802cb6da0", + "width": 262.75 + }, + { + "columnId": "52f6f2e9-6242-4c44-be63-b799150e7e60", + "isTransposed": false, + "width": 302.5, + "colorMode": "cell", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 5, + "stops": [ + { + "color": "#f7e0b8", + "stop": 0.6 + }, + { + "color": "#e7664c", + "stop": 1 + } + ], + "name": "custom", + "colorStops": [ + { + "color": "#f7e0b8", + "stop": 0.2 + }, + { + "color": "#e7664c", + "stop": 0.6 + } + ], + "rangeType": "number", + "rangeMin": 0.2, + "rangeMax": 0.6 + } + }, + "alignment": "center" + }, + { + "columnId": "7b9f3ece-9da3-4c27-b582-d3f8e8cc31d6", + "isTransposed": false, + "alignment": "center", + "colorMode": "cell", + "palette": { + "name": "custom", + "type": "palette", + "params": { + "steps": 5, + "stops": [ + { + "color": "#f7e0b8", + "stop": 0.6 + }, + { + "color": "#e7664c", + "stop": 0.6666666666666666 + } + ], + "rangeType": "number", + "name": "custom", + "colorStops": [ + { + "color": "#f7e0b8", + "stop": 0.2 + }, + { + "color": "#e7664c", + "stop": 0.6 + } + ], + "rangeMin": 0.2, + "rangeMax": 0.6 + } + } + } + ], + "layerId": "f26e8f7a-4118-4227-bea0-5c02d8b270f7", + "sorting": { + "columnId": "52f6f2e9-6242-4c44-be63-b799150e7e60", + "direction": "desc" + }, + "layerType": "data", + "rowHeight": "single", + "rowHeightLines": 1 + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [] + }, + "references": [ + { + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-f26e8f7a-4118-4227-bea0-5c02d8b270f7", + "type": "index-pattern" + } + ] + }, + "enhancements": {}, + "hidePanelTitles": false + }, + "version": "8.9.0" + }, + "0cc42484-16f7-42ec-b38c-9bf8be69cde7": { + "type": "lens", + "gridData": { + "x": 0, + "y": 25, + "w": 24, + "h": 11, + "i": "0cc42484-16f7-42ec-b38c-9bf8be69cde7", + "row": 3 + }, + "explicitInput": { + "id": "0cc42484-16f7-42ec-b38c-9bf8be69cde7", + "title": "[Flights] Delay Type", + "attributes": { + "title": "", + "type": "lens", + "visualizationType": "lnsXY", + "state": { + "datasourceStates": { + "formBased": { + "layers": { + "e80cc05e-c52a-4e5f-ac71-4b37274867f5": { + "columns": { + "caf7421e-93a3-439e-ab0a-fbdead93c21c": { + "label": "Top values of FlightDelayType", + "dataType": "string", + "operationType": "terms", + "scale": "ordinal", + "sourceField": "FlightDelayType", + "isBucketed": true, + "params": { + "size": 10, + "orderBy": { + "type": "column", + "columnId": "0233d302-ec81-4fbe-96cb-7fac84cf035c" + }, + "orderDirection": "desc", + "otherBucket": true, + "missingBucket": false + } + }, + "13ec79e3-9d73-4536-9056-3d92802bb30a": { + "label": "timestamp", + "dataType": "date", + "operationType": "date_histogram", + "sourceField": "timestamp", + "isBucketed": true, + "scale": "interval", + "params": { + "interval": "auto", + "includeEmptyRows": true + } + }, + "0233d302-ec81-4fbe-96cb-7fac84cf035c": { + "label": "Count of records", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___" + } + }, + "columnOrder": [ + "caf7421e-93a3-439e-ab0a-fbdead93c21c", + "13ec79e3-9d73-4536-9056-3d92802bb30a", + "0233d302-ec81-4fbe-96cb-7fac84cf035c" + ], + "incompleteColumns": {} + } + } + } + }, + "visualization": { + "legend": { + "isVisible": true, + "position": "bottom", + "legendSize": "auto" + }, + "valueLabels": "hide", + "fittingFunction": "None", + "yLeftExtent": { + "mode": "full" + }, + "yRightExtent": { + "mode": "full" + }, + "axisTitlesVisibilitySettings": { + "x": true, + "yLeft": false, + "yRight": true + }, + "tickLabelsVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "gridlinesVisibilitySettings": { + "x": true, + "yLeft": true, + "yRight": true + }, + "preferredSeriesType": "bar_percentage_stacked", + "layers": [ + { + "layerId": "e80cc05e-c52a-4e5f-ac71-4b37274867f5", + "accessors": ["0233d302-ec81-4fbe-96cb-7fac84cf035c"], + "position": "top", + "seriesType": "bar_percentage_stacked", + "showGridlines": false, + "palette": { + "type": "palette", + "name": "cool" + }, + "xAccessor": "13ec79e3-9d73-4536-9056-3d92802bb30a", + "splitAccessor": "caf7421e-93a3-439e-ab0a-fbdead93c21c", + "layerType": "data" + } + ] + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [] + }, + "references": [ + { + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-e80cc05e-c52a-4e5f-ac71-4b37274867f5", + "type": "index-pattern" + } + ] + }, + "hidePanelTitles": false, + "enhancements": {} + }, + "version": "8.9.0" + }, + "5d53db36-2d5a-4adc-af7b-cec4c1a294e0": { + "type": "lens", + "gridData": { + "x": 24, + "y": 58, + "w": 12, + "h": 11, + "i": "5d53db36-2d5a-4adc-af7b-cec4c1a294e0", + "row": 3 + }, + "explicitInput": { + "id": "5d53db36-2d5a-4adc-af7b-cec4c1a294e0", + "title": "[Flights] Delay Type", + "attributes": { + "title": "", + "type": "lens", + "visualizationType": "lnsPie", + "state": { + "datasourceStates": { + "formBased": { + "layers": { + "0c8e136b-a822-4fb3-836d-e06cbea4eea4": { + "columns": { + "d1cee8bf-34cf-4141-99d7-ff043ee77b56": { + "label": "Top values of FlightDelayType", + "dataType": "string", + "operationType": "terms", + "scale": "ordinal", + "sourceField": "FlightDelayType", + "isBucketed": true, + "params": { + "size": 10, + "orderBy": { + "type": "column", + "columnId": "aa152ace-ee2d-447b-b86d-459bef4d7880" + }, + "orderDirection": "desc", + "otherBucket": true, + "missingBucket": false + } + }, + "aa152ace-ee2d-447b-b86d-459bef4d7880": { + "label": "Count of records", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___" + } + }, + "columnOrder": [ + "d1cee8bf-34cf-4141-99d7-ff043ee77b56", + "aa152ace-ee2d-447b-b86d-459bef4d7880" + ], + "incompleteColumns": {} + } + } + } + }, + "visualization": { + "shape": "pie", + "palette": { + "type": "palette", + "name": "cool" + }, + "layers": [ + { + "layerId": "0c8e136b-a822-4fb3-836d-e06cbea4eea4", + "numberDisplay": "percent", + "categoryDisplay": "default", + "legendDisplay": "default", + "nestedLegend": false, + "layerType": "data", + "legendSize": "auto", + "primaryGroups": ["d1cee8bf-34cf-4141-99d7-ff043ee77b56"], + "metrics": ["aa152ace-ee2d-447b-b86d-459bef4d7880"] + } + ] + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [ + { + "meta": { + "type": "phrase", + "key": "FlightDelayType", + "params": { + "query": "No Delay" + }, + "disabled": false, + "negate": true, + "alias": null, + "index": "filter-index-pattern-0" + }, + "query": { + "match_phrase": { + "FlightDelayType": "No Delay" + } + }, + "$state": { + "store": "appState" + } + } + ] + }, + "references": [ + { + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-current-indexpattern", + "type": "index-pattern" + }, + { + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-0c8e136b-a822-4fb3-836d-e06cbea4eea4", + "type": "index-pattern" + }, + { + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "filter-index-pattern-0", + "type": "index-pattern" + } + ] + }, + "enhancements": {}, + "hidePanelTitles": false + }, + "version": "8.9.0" + }, + "ecd89a7c-9124-4472-bdc6-9bdbd70d45d5": { + "type": "lens", + "gridData": { + "x": 36, + "y": 58, + "w": 12, + "h": 11, + "i": "ecd89a7c-9124-4472-bdc6-9bdbd70d45d5", + "row": 3 + }, + "explicitInput": { + "id": "ecd89a7c-9124-4472-bdc6-9bdbd70d45d5", + "attributes": { + "title": "", + "visualizationType": "lnsXY", + "type": "lens", + "references": [ + { + "type": "index-pattern", + "id": "d3d7af60-4c81-11e8-b3d7-01146121b73d", + "name": "indexpattern-datasource-layer-dffd86e7-01a9-4990-b974-3706608b5532" + } + ], + "state": { + "visualization": { + "title": "Empty XY chart", + "legend": { + "isVisible": true, + "position": "right" + }, + "valueLabels": "hide", + "preferredSeriesType": "bar_horizontal", + "layers": [ + { + "layerId": "dffd86e7-01a9-4990-b974-3706608b5532", + "accessors": ["9d212159-afac-41f5-9303-5fb62ff04ba3"], + "position": "top", + "seriesType": "bar_horizontal", + "showGridlines": false, + "layerType": "data", + "splitAccessor": "d99ad4d7-26ff-4d65-a8ce-34656fdafa0a", + "palette": { + "type": "palette", + "name": "temperature" + } + } + ] + }, + "query": { + "query": "", + "language": "kuery" + }, + "filters": [], + "datasourceStates": { + "formBased": { + "layers": { + "dffd86e7-01a9-4990-b974-3706608b5532": { + "columns": { + "9d212159-afac-41f5-9303-5fb62ff04ba3": { + "label": "Count of records", + "dataType": "number", + "operationType": "count", + "isBucketed": false, + "scale": "ratio", + "sourceField": "___records___", + "params": { + "emptyAsNull": true + } + }, + "d99ad4d7-26ff-4d65-a8ce-34656fdafa0a": { + "label": "Top 10 values of DestWeather", + "dataType": "string", + "operationType": "terms", + "scale": "ordinal", + "sourceField": "DestWeather", + "isBucketed": true, + "params": { + "size": 10, + "orderBy": { + "type": "column", + "columnId": "9d212159-afac-41f5-9303-5fb62ff04ba3" + }, + "orderDirection": "desc", + "otherBucket": true, + "missingBucket": false, + "parentFormat": { + "id": "terms" + }, + "include": [], + "exclude": [], + "includeIsRegex": false, + "excludeIsRegex": false + } + } + }, + "columnOrder": [ + "d99ad4d7-26ff-4d65-a8ce-34656fdafa0a", + "9d212159-afac-41f5-9303-5fb62ff04ba3" + ], + "sampling": 1, + "incompleteColumns": {} + } + } + }, + "textBased": { + "layers": {} + } + }, + "internalReferences": [], + "adHocDataViews": {} + } + }, + "enhancements": {} + }, + "version": "8.9.0" + } +} diff --git a/examples/grid_example/public/plugin.ts b/examples/grid_example/public/plugin.ts index d57b06ac96017..b56b339273dd7 100644 --- a/examples/grid_example/public/plugin.ts +++ b/examples/grid_example/public/plugin.ts @@ -9,6 +9,7 @@ import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public'; +import { UiActionsStart } from '@kbn/ui-actions-plugin/public'; export const GRID_EXAMPLE_APP_ID = 'gridExample'; const gridExampleTitle = 'Grid Example'; @@ -17,20 +18,28 @@ interface GridExamplePluginSetupDependencies { developerExamples: DeveloperExamplesSetup; } +export interface GridExamplePluginStartDependencies { + uiActions: UiActionsStart; +} + export class GridExamplePlugin - implements Plugin + implements + Plugin { - public setup(core: CoreSetup<{}>, { developerExamples }: GridExamplePluginSetupDependencies) { + public setup( + core: CoreSetup, + { developerExamples }: GridExamplePluginSetupDependencies + ) { core.application.register({ id: GRID_EXAMPLE_APP_ID, title: gridExampleTitle, visibleIn: [], async mount(params: AppMountParameters) { - const [{ renderGridExampleApp }, [coreStart]] = await Promise.all([ + const [{ renderGridExampleApp }, [coreStart, deps]] = await Promise.all([ import('./app'), core.getStartServices(), ]); - return renderGridExampleApp(params.element, coreStart); + return renderGridExampleApp(params.element, { coreStart, uiActions: deps.uiActions }); }, }); developerExamples.register({ diff --git a/examples/grid_example/public/serialized_grid_layout.ts b/examples/grid_example/public/serialized_grid_layout.ts index 3e40380d91ac3..e5664bd1f22e5 100644 --- a/examples/grid_example/public/serialized_grid_layout.ts +++ b/examples/grid_example/public/serialized_grid_layout.ts @@ -9,6 +9,8 @@ import { MockSerializedDashboardState } from './types'; +import logsPanels from './logs_dashboard_panels.json'; + const STATE_SESSION_STORAGE_KEY = 'kibana.examples.gridExample.state'; export function clearSerializedDashboardState() { @@ -25,21 +27,11 @@ export function setSerializedGridLayout(state: MockSerializedDashboardState) { } const initialState: MockSerializedDashboardState = { - panels: { - panel1: { id: 'panel1', gridData: { i: 'panel1', x: 0, y: 0, w: 12, h: 6, row: 0 } }, - panel2: { id: 'panel2', gridData: { i: 'panel2', x: 0, y: 6, w: 8, h: 4, row: 0 } }, - panel3: { id: 'panel3', gridData: { i: 'panel3', x: 8, y: 6, w: 12, h: 4, row: 0 } }, - panel4: { id: 'panel4', gridData: { i: 'panel4', x: 0, y: 10, w: 48, h: 4, row: 0 } }, - panel5: { id: 'panel5', gridData: { i: 'panel5', x: 12, y: 0, w: 36, h: 6, row: 0 } }, - panel6: { id: 'panel6', gridData: { i: 'panel6', x: 24, y: 6, w: 24, h: 4, row: 0 } }, - panel7: { id: 'panel7', gridData: { i: 'panel7', x: 20, y: 6, w: 4, h: 2, row: 0 } }, - panel8: { id: 'panel8', gridData: { i: 'panel8', x: 20, y: 8, w: 4, h: 2, row: 0 } }, - panel9: { id: 'panel9', gridData: { i: 'panel9', x: 0, y: 0, w: 12, h: 16, row: 1 } }, - panel10: { id: 'panel10', gridData: { i: 'panel10', x: 24, y: 0, w: 12, h: 6, row: 2 } }, - }, + panels: logsPanels, rows: [ - { title: 'Large section', collapsed: false }, - { title: 'Small section', collapsed: false }, - { title: 'Another small section', collapsed: false }, + { title: 'Request Sizes', collapsed: false }, + { title: 'Visitors', collapsed: false }, + { title: 'Response Codes', collapsed: false }, + { title: 'Entire Flights Dashboard', collapsed: true }, ], }; diff --git a/examples/grid_example/public/types.ts b/examples/grid_example/public/types.ts index 39885e25e7153..141ba96ed2a75 100644 --- a/examples/grid_example/public/types.ts +++ b/examples/grid_example/public/types.ts @@ -15,8 +15,15 @@ export interface DashboardGridData { i: string; } +interface DashboardPanelState { + type: string; + gridData: DashboardGridData & { row?: number }; + explicitInput: Partial & { id: string }; + version?: string; +} + export interface MockedDashboardPanelMap { - [key: string]: { id: string; gridData: DashboardGridData & { row: number } }; + [key: string]: DashboardPanelState; } export type MockedDashboardRowMap = Array<{ title: string; collapsed: boolean }>; diff --git a/examples/grid_example/public/use_mock_dashboard_api.tsx b/examples/grid_example/public/use_mock_dashboard_api.tsx index 8388bd83f2645..51933f3a038e4 100644 --- a/examples/grid_example/public/use_mock_dashboard_api.tsx +++ b/examples/grid_example/public/use_mock_dashboard_api.tsx @@ -10,6 +10,10 @@ import { cloneDeep } from 'lodash'; import { useMemo } from 'react'; import { BehaviorSubject } from 'rxjs'; +import { v4 } from 'uuid'; + +import { TimeRange } from '@kbn/es-query'; +import { PanelPackage } from '@kbn/presentation-containers'; import { MockSerializedDashboardState, @@ -27,24 +31,50 @@ export const useMockDashboardApi = ({ savedState: MockSerializedDashboardState; }) => { const mockDashboardApi = useMemo(() => { + const panels$ = new BehaviorSubject(savedState.panels); + const expandedPanelId$ = new BehaviorSubject(undefined); + return { + getSerializedStateForChild: (id: string) => { + return { + rawState: panels$.getValue()[id].explicitInput, + references: [], + }; + }, + children$: new BehaviorSubject({}), + timeRange$: new BehaviorSubject({ + from: 'now-24h', + to: 'now', + }), viewMode: new BehaviorSubject('edit'), - panels$: new BehaviorSubject(savedState.panels), + panels$, rows$: new BehaviorSubject(savedState.rows), + expandedPanelId: expandedPanelId$, + expandPanel: (id: string) => { + if (expandedPanelId$.getValue()) { + expandedPanelId$.next(undefined); + } else { + expandedPanelId$.next(id); + } + }, removePanel: (id: string) => { const panels = { ...mockDashboardApi.panels$.getValue() }; delete panels[id]; // the grid layout component will handle compacting, if necessary mockDashboardApi.panels$.next(panels); }, - replacePanel: (oldId: string, newId: string) => { + replacePanel: (id: string, newPanel: PanelPackage) => { const currentPanels = mockDashboardApi.panels$.getValue(); const otherPanels = { ...currentPanels }; - const oldPanel = currentPanels[oldId]; - delete otherPanels[oldId]; - otherPanels[newId] = { id: newId, gridData: { ...oldPanel.gridData, i: newId } }; + const oldPanel = currentPanels[id]; + delete otherPanels[id]; + const newId = v4(); + otherPanels[newId] = { + ...oldPanel, + explicitInput: { ...newPanel.initialState, id: newId }, + }; mockDashboardApi.panels$.next(otherPanels); }, - addNewPanel: ({ id: newId }: { id: string }) => { + addNewPanel: async (panelPackage: PanelPackage) => { // we are only implementing "place at top" here, for demo purposes const currentPanels = mockDashboardApi.panels$.getValue(); const otherPanels = { ...currentPanels }; @@ -53,17 +83,22 @@ export const useMockDashboardApi = ({ currentPanel.gridData.y = currentPanel.gridData.y + DEFAULT_PANEL_HEIGHT; otherPanels[id] = currentPanel; } + const newId = v4(); mockDashboardApi.panels$.next({ ...otherPanels, [newId]: { - id: newId, + type: panelPackage.panelType, gridData: { - i: newId, row: 0, x: 0, y: 0, w: DEFAULT_PANEL_WIDTH, h: DEFAULT_PANEL_HEIGHT, + i: newId, + }, + explicitInput: { + ...panelPackage.initialState, + id: newId, }, }, }); diff --git a/examples/grid_example/public/utils.ts b/examples/grid_example/public/utils.ts index 5d2dfd0fa3002..8c9db472cffa7 100644 --- a/examples/grid_example/public/utils.ts +++ b/examples/grid_example/public/utils.ts @@ -11,6 +11,7 @@ import { GridLayoutData } from '@kbn/grid-layout'; import { MockedDashboardPanelMap, MockedDashboardRowMap } from './types'; export const gridLayoutToDashboardPanelMap = ( + panelState: MockedDashboardPanelMap, layout: GridLayoutData ): { panels: MockedDashboardPanelMap; rows: MockedDashboardRowMap } => { const panels: MockedDashboardPanelMap = {}; @@ -19,7 +20,7 @@ export const gridLayoutToDashboardPanelMap = ( rows.push({ title: row.title, collapsed: row.isCollapsed }); Object.values(row.panels).forEach((panelGridData) => { panels[panelGridData.id] = { - id: panelGridData.id, + ...panelState[panelGridData.id], gridData: { i: panelGridData.id, y: panelGridData.row, @@ -49,7 +50,7 @@ export const dashboardInputToGridLayout = ({ Object.keys(panels).forEach((panelId) => { const gridData = panels[panelId].gridData; - layout[gridData.row].panels[panelId] = { + layout[gridData.row ?? 0].panels[panelId] = { id: panelId, row: gridData.y, column: gridData.x, diff --git a/examples/grid_example/tsconfig.json b/examples/grid_example/tsconfig.json index ad692e9697b2d..334bc8c809a2c 100644 --- a/examples/grid_example/tsconfig.json +++ b/examples/grid_example/tsconfig.json @@ -3,7 +3,13 @@ "compilerOptions": { "outDir": "target/types" }, - "include": ["index.ts", "public/**/*.ts", "public/**/*.tsx", "../../typings/**/*"], + "include": [ + "index.ts", + "public/**/*.ts", + "public/**/*.tsx", + "../../typings/**/*", + "public/**/*.json" + ], "exclude": ["target/**/*"], "kbn_references": [ "@kbn/grid-layout", @@ -11,7 +17,15 @@ "@kbn/core", "@kbn/developer-examples-plugin", "@kbn/core-lifecycle-browser", + "@kbn/embeddable-plugin", "@kbn/react-kibana-mount", "@kbn/i18n", + "@kbn/embeddable-examples-plugin", + "@kbn/react-kibana-context-render", + "@kbn/presentation-publishing", + "@kbn/es-query", + "@kbn/ui-actions-plugin", + "@kbn/embeddable-examples-plugin", + "@kbn/presentation-containers" ] } diff --git a/packages/kbn-grid-layout/grid/grid_height_smoother.tsx b/packages/kbn-grid-layout/grid/grid_height_smoother.tsx index c880832c90a04..5db0355044622 100644 --- a/packages/kbn-grid-layout/grid/grid_height_smoother.tsx +++ b/packages/kbn-grid-layout/grid/grid_height_smoother.tsx @@ -10,7 +10,6 @@ import { css } from '@emotion/react'; import React, { PropsWithChildren, useEffect, useRef } from 'react'; import { combineLatest } from 'rxjs'; -import { euiThemeVars } from '@kbn/ui-theme'; import { GridLayoutStateManager } from './types'; export const GridHeightSmoother = ({ @@ -30,6 +29,7 @@ export const GridHeightSmoother = ({ } if (!interactionEvent) { smoothHeightRef.current.style.height = `${dimensions.height}px`; + smoothHeightRef.current.style.userSelect = 'auto'; return; } @@ -42,6 +42,7 @@ export const GridHeightSmoother = ({ dimensions.height ?? 0, smoothHeightRef.current.getBoundingClientRect().height )}px`; + smoothHeightRef.current.style.userSelect = 'none'; }); const expandedPanelSubscription = gridLayoutStateManager.expandedPanelId$.subscribe( @@ -49,19 +50,9 @@ export const GridHeightSmoother = ({ if (!smoothHeightRef.current) return; if (expandedPanelId) { - const smoothHeightRefY = - smoothHeightRef.current.getBoundingClientRect().y + document.documentElement.scrollTop; - const gutterSize = parseFloat(euiThemeVars.euiSizeL); - - // When panel is expanded, ensure the page occupies the full viewport height - // If the parent element is a flex container (preferred approach): - smoothHeightRef.current.style.flexBasis = `100%`; - - // fallback in case parent is not a flex container (less reliable if shifts happen after the time we calculate smoothHeightRefY) - smoothHeightRef.current.style.height = `calc(100vh - ${smoothHeightRefY + gutterSize}px`; + smoothHeightRef.current.style.height = `100%`; smoothHeightRef.current.style.transition = 'none'; } else { - smoothHeightRef.current.style.flexBasis = ''; smoothHeightRef.current.style.height = ''; smoothHeightRef.current.style.transition = ''; } @@ -78,6 +69,8 @@ export const GridHeightSmoother = ({
React.ReactNode; + renderPanelContents: ( + panelId: string, + setDragHandles?: (refs: Array) => void + ) => React.ReactNode; onLayoutChange: (newLayout: GridLayoutData) => void; expandedPanelId?: string; accessMode?: GridAccessMode; diff --git a/packages/kbn-grid-layout/grid/grid_panel/drag_handle.tsx b/packages/kbn-grid-layout/grid/grid_panel/drag_handle.tsx index 90305812ff8d5..f175cf227a7e5 100644 --- a/packages/kbn-grid-layout/grid/grid_panel/drag_handle.tsx +++ b/packages/kbn-grid-layout/grid/grid_panel/drag_handle.tsx @@ -7,24 +7,88 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React from 'react'; +import React, { useCallback, useEffect, useImperativeHandle, useRef, useState } from 'react'; import { EuiIcon, useEuiTheme } from '@elastic/eui'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; -import { PanelInteractionEvent } from '../types'; +import { GridLayoutStateManager, PanelInteractionEvent } from '../types'; -export const DragHandle = ({ - interactionStart, -}: { - interactionStart: ( - type: PanelInteractionEvent['type'] | 'drop', - e: React.MouseEvent - ) => void; -}) => { +export interface DragHandleApi { + setDragHandles: (refs: Array) => void; +} + +export const DragHandle = React.forwardRef< + DragHandleApi, + { + gridLayoutStateManager: GridLayoutStateManager; + interactionStart: ( + type: PanelInteractionEvent['type'] | 'drop', + e: MouseEvent | React.MouseEvent + ) => void; + } +>(({ gridLayoutStateManager, interactionStart }, ref) => { const { euiTheme } = useEuiTheme(); - return ( + + const removeEventListenersRef = useRef<(() => void) | null>(null); + const [dragHandleCount, setDragHandleCount] = useState(0); + const dragHandleRefs = useRef>([]); + + /** + * We need to memoize the `onMouseDown` callback so that we don't assign a new `onMouseDown` event handler + * every time `setDragHandles` is called + */ + const onMouseDown = useCallback( + (e: MouseEvent | React.MouseEvent) => { + if (gridLayoutStateManager.accessMode$.getValue() !== 'EDIT' || e.button !== 0) { + // ignore anything but left clicks, and ignore clicks when not in edit mode + return; + } + e.stopPropagation(); + interactionStart('drag', e); + }, + [interactionStart, gridLayoutStateManager.accessMode$] + ); + + const setDragHandles = useCallback( + (dragHandles: Array) => { + setDragHandleCount(dragHandles.length); + dragHandleRefs.current = dragHandles; + + for (const handle of dragHandles) { + if (handle === null) return; + handle.addEventListener('mousedown', onMouseDown, { passive: true }); + } + + removeEventListenersRef.current = () => { + for (const handle of dragHandles) { + if (handle === null) return; + handle.removeEventListener('mousedown', onMouseDown); + } + }; + }, + [onMouseDown] + ); + + useEffect(() => { + return () => { + // on unmount, remove all drag handle event listeners + if (removeEventListenersRef.current) { + removeEventListenersRef.current(); + } + }; + }, []); + + useImperativeHandle( + ref, + () => { + return { setDragHandles }; + }, + [setDragHandles] + ); + + return Boolean(dragHandleCount) ? null : ( ); -}; +}); diff --git a/packages/kbn-grid-layout/grid/grid_panel/grid_panel.tsx b/packages/kbn-grid-layout/grid/grid_panel/grid_panel.tsx index e817f5fc3871b..c30e6ecc996eb 100644 --- a/packages/kbn-grid-layout/grid/grid_panel/grid_panel.tsx +++ b/packages/kbn-grid-layout/grid/grid_panel/grid_panel.tsx @@ -7,24 +7,27 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { forwardRef, useEffect, useMemo } from 'react'; +import React, { forwardRef, useEffect, useMemo, useState } from 'react'; import { combineLatest, skip } from 'rxjs'; -import { EuiPanel, euiFullHeight, useEuiOverflowScroll } from '@elastic/eui'; import { css } from '@emotion/react'; import { euiThemeVars } from '@kbn/ui-theme'; + import { GridLayoutStateManager, PanelInteractionEvent } from '../types'; import { getKeysInOrder } from '../utils/resolve_grid_row'; -import { DragHandle } from './drag_handle'; +import { DragHandle, DragHandleApi } from './drag_handle'; import { ResizeHandle } from './resize_handle'; export interface GridPanelProps { panelId: string; rowIndex: number; - renderPanelContents: (panelId: string) => React.ReactNode; + renderPanelContents: ( + panelId: string, + setDragHandles?: (refs: Array) => void + ) => React.ReactNode; interactionStart: ( type: PanelInteractionEvent['type'] | 'drop', - e: React.MouseEvent + e: MouseEvent | React.MouseEvent ) => void; gridLayoutStateManager: GridLayoutStateManager; } @@ -34,6 +37,37 @@ export const GridPanel = forwardRef( { panelId, rowIndex, renderPanelContents, interactionStart, gridLayoutStateManager }, panelRef ) => { + const [dragHandleApi, setDragHandleApi] = useState(null); + + useEffect(() => { + const onDropEventHandler = (dropEvent: MouseEvent) => interactionStart('drop', dropEvent); + /** + * Subscription to add a singular "drop" event handler whenever an interaction starts - + * this is handled in a subscription so that it is not lost when the component gets remounted + * (which happens when a panel gets dragged from one grid row to another) + */ + const dropEventSubscription = gridLayoutStateManager.interactionEvent$.subscribe((event) => { + if (!event || event.id !== panelId) return; + + /** + * By adding the "drop" event listener to the document rather than the drag/resize event handler, + * we prevent the element from getting "stuck" in an interaction; however, we only attach this event + * listener **when the drag/resize event starts**, and it only executes once (i.e. it removes itself + * once it executes, so we don't have to manually remove it outside of the unmount condition) + */ + document.addEventListener('mouseup', onDropEventHandler, { + once: true, + passive: true, + }); + }); + + return () => { + dropEventSubscription.unsubscribe(); + document.removeEventListener('mouseup', onDropEventHandler); // removes the event listener on row change + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + /** Set initial styles based on state at mount to prevent styles from "blipping" */ const initialStyles = useMemo(() => { const initialPanel = gridLayoutStateManager.gridLayout$.getValue()[rowIndex].panels[panelId]; @@ -88,24 +122,22 @@ export const GridPanel = forwardRef( // undo any "lock to grid" styles **except** for the top left corner, which stays locked ref.style.gridColumnStart = `${panel.column + 1}`; ref.style.gridRowStart = `${panel.row + 1}`; - ref.style.gridColumnEnd = ``; - ref.style.gridRowEnd = ``; + ref.style.gridColumnEnd = `auto`; + ref.style.gridRowEnd = `auto`; } else { // if the current panel is being dragged, render it with a fixed position + size - ref.style.position = `fixed`; + ref.style.position = 'fixed'; + ref.style.left = `${draggingPosition.left}px`; ref.style.top = `${draggingPosition.top}px`; ref.style.width = `${draggingPosition.right - draggingPosition.left}px`; ref.style.height = `${draggingPosition.bottom - draggingPosition.top}px`; // undo any "lock to grid" styles - ref.style.gridColumnStart = ``; - ref.style.gridRowStart = ``; - ref.style.gridColumnEnd = ``; - ref.style.gridRowEnd = ``; + ref.style.gridArea = `auto`; // shortcut to set all grid styles to `auto` } } else { - ref.style.zIndex = '0'; + ref.style.zIndex = `auto`; // if the panel is not being dragged and/or resized, undo any fixed position styles ref.style.position = ''; @@ -175,32 +207,27 @@ export const GridPanel = forwardRef( * Memoize panel contents to prevent unnecessary re-renders */ const panelContents = useMemo(() => { - return renderPanelContents(panelId); - }, [panelId, renderPanelContents]); + if (!dragHandleApi) return <>; // delays the rendering of the panel until after dragHandleApi is defined + return renderPanelContents(panelId, dragHandleApi.setDragHandles); + }, [panelId, renderPanelContents, dragHandleApi]); return (
- - -
- {panelContents} -
+ + {panelContents} -
+
); } diff --git a/packages/kbn-grid-layout/grid/grid_panel/resize_handle.tsx b/packages/kbn-grid-layout/grid/grid_panel/resize_handle.tsx index 4c4a2d60ee5cb..ffee2f2764ed0 100644 --- a/packages/kbn-grid-layout/grid/grid_panel/resize_handle.tsx +++ b/packages/kbn-grid-layout/grid/grid_panel/resize_handle.tsx @@ -7,12 +7,11 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React from 'react'; - import { transparentize } from '@elastic/eui'; import { css } from '@emotion/react'; -import { euiThemeVars } from '@kbn/ui-theme'; import { i18n } from '@kbn/i18n'; +import { euiThemeVars } from '@kbn/ui-theme'; +import React from 'react'; import { PanelInteractionEvent } from '../types'; export const ResizeHandle = ({ @@ -20,7 +19,7 @@ export const ResizeHandle = ({ }: { interactionStart: ( type: PanelInteractionEvent['type'] | 'drop', - e: React.MouseEvent + e: MouseEvent | React.MouseEvent ) => void; }) => { return ( @@ -42,7 +41,9 @@ export const ResizeHandle = ({ margin: -2px; position: absolute; width: ${euiThemeVars.euiSizeL}; + max-width: 100%; height: ${euiThemeVars.euiSizeL}; + z-index: ${euiThemeVars.euiZLevel9}; transition: opacity 0.2s, border 0.2s; border-radius: 7px 0 7px 0; border-bottom: 2px solid ${euiThemeVars.euiColorSuccess}; diff --git a/packages/kbn-grid-layout/grid/grid_row/grid_row.tsx b/packages/kbn-grid-layout/grid/grid_row/grid_row.tsx index 85d051700e90d..d2b046919a62b 100644 --- a/packages/kbn-grid-layout/grid/grid_row/grid_row.tsx +++ b/packages/kbn-grid-layout/grid/grid_row/grid_row.tsx @@ -23,7 +23,10 @@ import { GridRowHeader } from './grid_row_header'; export interface GridRowProps { rowIndex: number; - renderPanelContents: (panelId: string) => React.ReactNode; + renderPanelContents: ( + panelId: string, + setDragHandles?: (refs: Array) => void + ) => React.ReactNode; setInteractionEvent: (interactionData?: PanelInteractionEvent) => void; gridLayoutStateManager: GridLayoutStateManager; } @@ -32,19 +35,13 @@ export const GridRow = forwardRef( ({ rowIndex, renderPanelContents, setInteractionEvent, gridLayoutStateManager }, gridRef) => { const currentRow = gridLayoutStateManager.gridLayout$.value[rowIndex]; - const [panelIds, setPanelIds] = useState(() => getKeysInOrder(currentRow.panels)); + const [panelIds, setPanelIds] = useState(Object.keys(currentRow.panels)); + const [panelIdsInOrder, setPanelIdsInOrder] = useState(() => + getKeysInOrder(currentRow.panels) + ); const [rowTitle, setRowTitle] = useState(currentRow.title); const [isCollapsed, setIsCollapsed] = useState(currentRow.isCollapsed); - /** Syncs panel IDs in order after a change in the grid layout, such as adding, removing, or reordering panels. */ - const syncPanelIds = useCallback(() => { - const newPanelIds = getKeysInOrder(gridLayoutStateManager.gridLayout$.value[rowIndex].panels); - const hasOrderChanged = JSON.stringify(panelIds) !== JSON.stringify(newPanelIds); - if (hasOrderChanged) { - setPanelIds(newPanelIds); - } - }, [setPanelIds, gridLayoutStateManager.gridLayout$, rowIndex, panelIds]); - const getRowCount = useCallback( (row: GridRowData) => { const maxRow = Object.values(row.panels).reduce((acc, panel) => { @@ -152,10 +149,6 @@ export const GridRow = forwardRef( * - Title * - Collapsed state * - Panel IDs (adding/removing/replacing, but not reordering) - * - * Note: During dragging or resizing actions, the row should not re-render because panel positions are controlled via CSS styles for performance reasons. - * However, once the user finishes the interaction, the order of rendered panels need to be aligned with how they are displayed in the grid for accessibility reasons (screen readers and focus management). - * This is handled in the syncPanelIds callback. */ const rowStateSubscription = gridLayoutStateManager.gridLayout$ .pipe( @@ -163,7 +156,7 @@ export const GridRow = forwardRef( return { title: gridLayout[rowIndex].title, isCollapsed: gridLayout[rowIndex].isCollapsed, - panelIds: getKeysInOrder(gridLayout[rowIndex].panels), + panelIds: Object.keys(gridLayout[rowIndex].panels), }; }), pairwise() @@ -180,6 +173,9 @@ export const GridRow = forwardRef( ) ) { setPanelIds(newRowData.panelIds); + setPanelIdsInOrder( + getKeysInOrder(gridLayoutStateManager.gridLayout$.getValue()[rowIndex].panels) + ); } }); @@ -194,64 +190,67 @@ export const GridRow = forwardRef( ); /** - * Memoize panel children components to prevent unnecessary re-renders + * Memoize panel children components (independent of their order) to prevent unnecessary re-renders */ - const children = useMemo(() => { - return panelIds.map((panelId) => ( - { - e.preventDefault(); - e.stopPropagation(); + const children: { [panelId: string]: React.ReactNode } = useMemo(() => { + return panelIds.reduce( + (prev, panelId) => ({ + ...prev, + [panelId]: ( + { + e.stopPropagation(); - // Disable interactions when a panel is expanded - const isInteractive = gridLayoutStateManager.expandedPanelId$.value === undefined; - if (!isInteractive) return; + // Disable interactions when a panel is expanded + const isInteractive = gridLayoutStateManager.expandedPanelId$.value === undefined; + if (!isInteractive) return; - const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][panelId]; - if (!panelRef) return; + const panelRef = gridLayoutStateManager.panelRefs.current[rowIndex][panelId]; + if (!panelRef) return; - const panelRect = panelRef.getBoundingClientRect(); - if (type === 'drop') { - setInteractionEvent(undefined); - // Ensure the row re-renders to reflect the new panel order after a drag-and-drop interaction. - // the order of rendered panels need to be aligned with how they are displayed in the grid for accessibility reasons (screen readers and focus management). - syncPanelIds(); - } else { - setInteractionEvent({ - type, - id: panelId, - panelDiv: panelRef, - targetRowIndex: rowIndex, - mouseOffsets: { - top: e.clientY - panelRect.top, - left: e.clientX - panelRect.left, - right: e.clientX - panelRect.right, - bottom: e.clientY - panelRect.bottom, - }, - }); - } - }} - ref={(element) => { - if (!gridLayoutStateManager.panelRefs.current[rowIndex]) { - gridLayoutStateManager.panelRefs.current[rowIndex] = {}; - } - gridLayoutStateManager.panelRefs.current[rowIndex][panelId] = element; - }} - /> - )); - }, [ - panelIds, - rowIndex, - gridLayoutStateManager, - renderPanelContents, - setInteractionEvent, - syncPanelIds, - ]); + const panelRect = panelRef.getBoundingClientRect(); + if (type === 'drop') { + setInteractionEvent(undefined); + /** + * Ensure the row re-renders to reflect the new panel order after a drag-and-drop interaction, since + * the order of rendered panels need to be aligned with how they are displayed in the grid for accessibility + * reasons (screen readers and focus management). + */ + setPanelIdsInOrder( + getKeysInOrder(gridLayoutStateManager.gridLayout$.getValue()[rowIndex].panels) + ); + } else { + setInteractionEvent({ + type, + id: panelId, + panelDiv: panelRef, + targetRowIndex: rowIndex, + mouseOffsets: { + top: e.clientY - panelRect.top, + left: e.clientX - panelRect.left, + right: e.clientX - panelRect.right, + bottom: e.clientY - panelRect.bottom, + }, + }); + } + }} + ref={(element) => { + if (!gridLayoutStateManager.panelRefs.current[rowIndex]) { + gridLayoutStateManager.panelRefs.current[rowIndex] = {}; + } + gridLayoutStateManager.panelRefs.current[rowIndex][panelId] = element; + }} + /> + ), + }), + {} + ); + }, [panelIds, gridLayoutStateManager, renderPanelContents, rowIndex, setInteractionEvent]); return (
@@ -276,7 +275,8 @@ export const GridRow = forwardRef( ${initialStyles}; `} > - {children} + {/* render the panels **in order** for accessibility, using the memoized panel components */} + {panelIdsInOrder.map((panelId) => children[panelId])}
)} diff --git a/packages/kbn-grid-layout/grid/test_utils/mocks.tsx b/packages/kbn-grid-layout/grid/test_utils/mocks.tsx index 09ffa05a39aa6..52b27ef7c8232 100644 --- a/packages/kbn-grid-layout/grid/test_utils/mocks.tsx +++ b/packages/kbn-grid-layout/grid/test_utils/mocks.tsx @@ -12,6 +12,7 @@ import { BehaviorSubject } from 'rxjs'; import { ObservedSize } from 'use-resize-observer/polyfilled'; import { ActivePanel, + GridAccessMode, GridLayoutData, GridLayoutStateManager, PanelInteractionEvent, @@ -46,6 +47,7 @@ export const gridLayoutStateManagerMock: GridLayoutStateManager = { runtimeSettings$, panelRefs: { current: [] }, rowRefs: { current: [] }, + accessMode$: new BehaviorSubject('EDIT'), interactionEvent$: new BehaviorSubject(undefined), activePanel$: new BehaviorSubject(undefined), gridDimensions$: new BehaviorSubject({ width: 600, height: 900 }), diff --git a/packages/kbn-grid-layout/grid/types.ts b/packages/kbn-grid-layout/grid/types.ts index cd24855d07646..1e0d541ce343a 100644 --- a/packages/kbn-grid-layout/grid/types.ts +++ b/packages/kbn-grid-layout/grid/types.ts @@ -60,6 +60,7 @@ export interface GridLayoutStateManager { gridLayout$: BehaviorSubject; expandedPanelId$: BehaviorSubject; isMobileView$: BehaviorSubject; + accessMode$: BehaviorSubject; gridDimensions$: BehaviorSubject; runtimeSettings$: BehaviorSubject; diff --git a/packages/kbn-grid-layout/grid/use_grid_layout_events.ts b/packages/kbn-grid-layout/grid/use_grid_layout_events.ts index 64cc8f482838e..09f12d13d93d8 100644 --- a/packages/kbn-grid-layout/grid/use_grid_layout_events.ts +++ b/packages/kbn-grid-layout/grid/use_grid_layout_events.ts @@ -13,18 +13,42 @@ import { resolveGridRow } from './utils/resolve_grid_row'; import { GridPanelData, GridLayoutStateManager } from './types'; import { isGridDataEqual } from './utils/equality_checks'; +const MIN_SPEED = 50; +const MAX_SPEED = 150; + const scrollOnInterval = (direction: 'up' | 'down') => { let count = 0; + let currentSpeed = MIN_SPEED; + let maxSpeed = MIN_SPEED; + let turnAroundPoint: number | undefined; + const interval = setInterval(() => { - // calculate the speed based on how long the interval has been going to create an ease effect - // via the parabola formula `y = a(x - h)^2 + k` - // - the starting speed is k = 50 - // - the maximum speed is 250 - // - the rate at which the speed increases is controlled by a = 0.75 - const speed = Math.min(0.75 * count ** 2 + 50, 250); - window.scrollBy({ top: direction === 'down' ? speed : -speed, behavior: 'smooth' }); - count++; - }, 100); + /** + * Since "smooth" scrolling on an interval is jittery on Chrome, we are manually creating + * an "ease" effect via the parabola formula `y = a(x - h)^2 + k` + * + * Scrolling slowly speeds up as the user drags, and it slows down again as they approach the + * top and/or bottom of the screen. + */ + const nearTop = direction === 'up' && scrollY < window.innerHeight; + const nearBottom = + direction === 'down' && + window.innerHeight + window.scrollY > document.body.scrollHeight - window.innerHeight; + if (!turnAroundPoint && (nearTop || nearBottom)) { + // reverse the direction of the parabola + maxSpeed = currentSpeed; + turnAroundPoint = count; + } + + currentSpeed = turnAroundPoint + ? Math.max(-3 * (count - turnAroundPoint) ** 2 + maxSpeed, MIN_SPEED) // slow down fast + : Math.min(0.1 * count ** 2 + MIN_SPEED, MAX_SPEED); // speed up slowly + window.scrollBy({ + top: direction === 'down' ? currentSpeed : -currentSpeed, + }); + + count++; // increase the counter to increase the time interval used in the parabola formula + }, 60); return interval; }; @@ -56,7 +80,6 @@ export const useGridLayoutEvents = ({ stopAutoScrollIfNecessary(); return; } - e.preventDefault(); e.stopPropagation(); const gridRowElements = gridLayoutStateManager.rowRefs.current; @@ -154,8 +177,11 @@ export const useGridLayoutEvents = ({ // auto scroll when an event is happening close to the top or bottom of the screen const heightPercentage = 100 - ((window.innerHeight - mouseTargetPixel.y) / window.innerHeight) * 100; - const startScrollingUp = !isResize && heightPercentage < 5; // don't scroll up when resizing - const startScrollingDown = heightPercentage > 95; + const atTheTop = window.scrollY <= 0; + const atTheBottom = window.innerHeight + window.scrollY >= document.body.scrollHeight; + + const startScrollingUp = !isResize && heightPercentage < 5 && !atTheTop; // don't scroll up when resizing + const startScrollingDown = heightPercentage > 95 && !atTheBottom; if (startScrollingUp || startScrollingDown) { if (!scrollInterval.current) { // only start scrolling if it's not already happening @@ -202,8 +228,9 @@ export const useGridLayoutEvents = ({ calculateUserEvent(e); }; - document.addEventListener('mousemove', onMouseMove); - document.addEventListener('scroll', calculateUserEvent); + document.addEventListener('mousemove', onMouseMove, { passive: true }); + document.addEventListener('scroll', calculateUserEvent, { passive: true }); + return () => { document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('scroll', calculateUserEvent); diff --git a/packages/kbn-grid-layout/grid/use_grid_layout_state.ts b/packages/kbn-grid-layout/grid/use_grid_layout_state.ts index 8617262829c48..3e76687436bcb 100644 --- a/packages/kbn-grid-layout/grid/use_grid_layout_state.ts +++ b/packages/kbn-grid-layout/grid/use_grid_layout_state.ts @@ -10,6 +10,7 @@ import { useEffect, useMemo, useRef } from 'react'; import { BehaviorSubject, combineLatest, debounceTime } from 'rxjs'; import useResizeObserver, { type ObservedSize } from 'use-resize-observer/polyfilled'; +import { cloneDeep } from 'lodash'; import { ActivePanel, @@ -21,6 +22,7 @@ import { RuntimeGridSettings, } from './types'; import { shouldShowMobileView } from './utils/mobile_view'; +import { resolveGridRow } from './utils/resolve_grid_row'; export const useGridLayoutState = ({ layout, @@ -59,7 +61,12 @@ export const useGridLayoutState = ({ }, [accessMode, accessMode$]); const gridLayoutStateManager = useMemo(() => { - const gridLayout$ = new BehaviorSubject(layout); + const resolvedLayout = cloneDeep(layout); + resolvedLayout.forEach((row, rowIndex) => { + resolvedLayout[rowIndex] = resolveGridRow(row); + }); + + const gridLayout$ = new BehaviorSubject(resolvedLayout); const gridDimensions$ = new BehaviorSubject({ width: 0, height: 0 }); const interactionEvent$ = new BehaviorSubject(undefined); const activePanel$ = new BehaviorSubject(undefined); @@ -77,6 +84,7 @@ export const useGridLayoutState = ({ panelIds$, gridLayout$, activePanel$, + accessMode$, gridDimensions$, runtimeSettings$, interactionEvent$, diff --git a/src/plugins/embeddable/kibana.jsonc b/src/plugins/embeddable/kibana.jsonc index b617114f9fa59..ea198de6386a3 100644 --- a/src/plugins/embeddable/kibana.jsonc +++ b/src/plugins/embeddable/kibana.jsonc @@ -1,9 +1,7 @@ { "type": "plugin", "id": "@kbn/embeddable-plugin", - "owner": [ - "@elastic/kibana-presentation" - ], + "owner": ["@elastic/kibana-presentation"], "group": "platform", "visibility": "shared", "description": "Adds embeddables service to Kibana", @@ -19,17 +17,8 @@ "savedObjectsManagement", "contentManagement" ], - "optionalPlugins": [ - "savedObjectsTaggingOss", - "usageCollection" - ], - "requiredBundles": [ - "savedObjects", - "kibanaUtils", - "presentationPanel" - ], - "extraPublicDirs": [ - "common" - ] + "optionalPlugins": ["savedObjectsTaggingOss", "usageCollection"], + "requiredBundles": ["savedObjects", "kibanaUtils", "presentationPanel"], + "extraPublicDirs": ["common"] } -} \ No newline at end of file +} diff --git a/src/plugins/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx b/src/plugins/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx index edf52244c2d4d..a9c4821d71a53 100644 --- a/src/plugins/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx +++ b/src/plugins/embeddable/public/react_embeddable_system/react_embeddable_renderer.tsx @@ -65,6 +65,7 @@ export const ReactEmbeddableRenderer = < | 'hideLoader' | 'hideHeader' | 'hideInspector' + | 'setDragHandles' | 'getActions' >; hidePanelChrome?: boolean; diff --git a/src/plugins/presentation_panel/public/panel_component/panel_header/presentation_panel_header.tsx b/src/plugins/presentation_panel/public/panel_component/panel_header/presentation_panel_header.tsx index 0747e4a4f8229..74b88cd369cf1 100644 --- a/src/plugins/presentation_panel/public/panel_component/panel_header/presentation_panel_header.tsx +++ b/src/plugins/presentation_panel/public/panel_component/panel_header/presentation_panel_header.tsx @@ -10,7 +10,7 @@ import { EuiScreenReaderOnly } from '@elastic/eui'; import { ViewMode } from '@kbn/presentation-publishing'; import classNames from 'classnames'; -import React from 'react'; +import React, { useCallback } from 'react'; import { getAriaLabelForTitle } from '../presentation_panel_strings'; import { DefaultPresentationPanelApi, PresentationPanelInternalProps } from '../types'; import { PresentationPanelTitle } from './presentation_panel_title'; @@ -23,6 +23,7 @@ export type PresentationPanelHeaderProps void; } & Pick; export const PresentationPanelHeader = < @@ -35,6 +36,7 @@ export const PresentationPanelHeader = < hideTitle, panelTitle, panelDescription, + setDragHandle, showBadges = true, showNotifications = true, }: PresentationPanelHeaderProps) => { @@ -45,6 +47,14 @@ export const PresentationPanelHeader = < getActions ); + const memoizedSetDragHandle = useCallback( + // memoize the ref callback so that we don't call `setDragHandle` on every render + (ref: HTMLHeadingElement | null) => { + setDragHandle('panelHeader', ref); + }, + [setDragHandle] + ); + const showPanelBar = (!hideTitle && panelTitle) || badgeElements.length > 0 || notificationElements.length > 0; @@ -71,7 +81,7 @@ export const PresentationPanelHeader = < className={headerClasses} data-test-subj={`embeddablePanelHeading-${(panelTitle || '').replace(/\s/g, '')}`} > -

+

{ariaLabelElement} void; actionPredicate?: (actionId: string) => boolean; children: ReactElement; className?: string; @@ -124,9 +126,10 @@ export const PresentationPanelHoverActions = ({ const [isContextMenuOpen, setIsContextMenuOpen] = useState(false); const [notifications, setNotifications] = useState([]); const hoverActionsRef = useRef(null); + const dragHandleRef = useRef(null); const anchorRef = useRef(null); - const leftHoverActionsRef = useRef(null); const rightHoverActionsRef = useRef(null); + const [combineHoverActions, setCombineHoverActions] = useState(false); const [borderStyles, setBorderStyles] = useState(TOP_ROUNDED_CORNERS); @@ -138,14 +141,14 @@ export const PresentationPanelHoverActions = ({ const anchorWidth = anchorRef.current.offsetWidth; const hoverActionsWidth = (rightHoverActionsRef.current?.offsetWidth ?? 0) + - (leftHoverActionsRef.current?.offsetWidth ?? 0) + + (dragHandleRef.current?.offsetWidth ?? 0) + parseInt(euiThemeVars.euiSize, 10) * 2; const hoverActionsHeight = rightHoverActionsRef.current?.offsetHeight ?? 0; // Left align hover actions when they would get cut off by the right edge of the window if (anchorLeft - (hoverActionsWidth - anchorWidth) <= parseInt(euiThemeVars.euiSize, 10)) { - hoverActionsRef.current.style.removeProperty('right'); - hoverActionsRef.current.style.setProperty('left', '0'); + dragHandleRef.current?.style.removeProperty('right'); + dragHandleRef.current?.style.setProperty('left', '0'); } else { hoverActionsRef.current.style.removeProperty('left'); hoverActionsRef.current.style.setProperty('right', '0'); @@ -442,19 +445,30 @@ export const PresentationPanelHoverActions = ({ /> ); - const dragHandle = ( - + const dragHandle = useMemo( + // memoize the drag handle to avoid calling `setDragHandle` unnecessarily + () => ( + + ), + [setDragHandle] ); const hasHoverActions = quickActionElements.length || contextMenuPanels.lastIndexOf.length; @@ -535,7 +549,6 @@ export const PresentationPanelHoverActions = ({ > {viewMode === 'edit' && !combineHoverActions ? (
) => { const [api, setApi] = useState(null); const headerId = useMemo(() => htmlIdGenerator()(), []); + const dragHandles = useRef<{ [dragHandleKey: string]: HTMLElement | null }>({}); + const viewModeSubject = (() => { if (apiPublishesViewMode(api)) return api.viewMode; if (apiHasParentApi(api) && apiPublishesViewMode(api.parentApi)) return api.parentApi.viewMode; @@ -90,9 +94,26 @@ export const PresentationPanelInternal = < return attrs; }, [dataLoading, blockingError]); + const setDragHandle = useCallback( + (id: string, ref: HTMLElement | null) => { + dragHandles.current[id] = ref; + setDragHandles?.(Object.values(dragHandles.current)); + }, + [setDragHandles] + ); + return ( ) => void; } /**