From 2a76fe3ee432d0b6746eae660cfe31fc71d15547 Mon Sep 17 00:00:00 2001 From: Hannah Mudge Date: Tue, 10 Dec 2024 10:14:31 -0700 Subject: [PATCH] [Dashboard] [Collapsable Panels] Add embeddable support (#198413) Closes https://github.com/elastic/kibana/issues/190379 ## Summary This PR switches the example grid layout app to render embeddables as panels rather than the simplified mock panel we were using previously. In doing so, I had to add the ability for custom panels to add a custom drag handle via the `renderPanelContents` callback - this required adding a `setDragHandles` callback to the `ReactEmbeddableRenderer` that could be passed down to the `PresentationPanel` component. https://github.com/user-attachments/assets/9e2c68f9-34af-4360-a978-9113701a5ea2 #### New scroll behaviour In https://github.com/elastic/kibana/pull/201867, I introduced a small "ease" to the auto-scroll effect that happens when you drag a panel to the top or bottom of the window. However, in that PR, I was using the `smooth` scrolling behaviour, which unfortunately became **very** jittery once I switched to embeddables rather than simple panels (specifically in Chrome - it worked fine in Firefox). The only way to prevent this jittery scroll was to switch to the default scroll behaviour, but this lead to a very **abrupt** stop when the scrollbar reached the top and/or bottom of the page - so, to give the same "gentle" stop that the `smooth` scroll had, I decided to recreate this effect by adding a slow down "ease" when close to the top or bottom of the page: https://github.com/user-attachments/assets/cb7bf03f-4a9e-4446-be4f-8f54c0bc88ac This effect is accomplished via the parabola formula `y = a(x-h)2 + k` and can be roughly visualized with the following, which shows that the "speed up" ease happens at a much slower pace than the "slow down" ease: ![image](https://github.com/user-attachments/assets/02b4389c-fe78-448d-9c02-c4ec5e722d5e) #### Notes about parent changes As I investigated improving the efficiency of the grid layout with embeddables, one of the main things I noticed was that the grid panel was **always** remounted when moving a panel from one collapsible section to another. This lead me (and @ThomThomson) down a rabbit hole of React-reparenting, and we explored a few different options to see if we could change the parent of a component **without** having it remount. In summary, after various experiments and a whole bunch of research, we determined that, due to the reconciliation of the React tree, this is unfortunately impossible. So our priorities will instead have to move to making the remount of `ReactEmbeddableRenderer` **as efficient as possible** via caching, since the remount is inevitable. ### Checklist - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Identify risks There are no risks to this PR, since the most significant work is contained in the `examples` plugin. Some changes were made to the presentation panel to allow for custom drag handles, but this isn't actually used in Dashboard - so for now, this code is only called in the example plugin, as well. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../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; } /**