Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Dashboard] New layout engine #174132

Merged
merged 33 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9bbe618
super simple grid implementation using only html drag drop and css grid
ThomThomson Jan 2, 2024
4b93780
Merge remote-tracking branch 'upstream/main' into spaceTime/htmlDragDrop
ThomThomson May 10, 2024
bdf52b1
Merge remote-tracking branch 'upstream/main' into spaceTime/htmlDragDrop
ThomThomson May 29, 2024
44080d1
Merge remote-tracking branch 'upstream/main' into spaceTime/htmlDragDrop
ThomThomson Jun 4, 2024
140bc32
Simple collision resolution algorithm.
ThomThomson Jun 7, 2024
9b368d9
separate into files
ThomThomson Jun 19, 2024
b6110df
Noop ghosts
ThomThomson Jun 19, 2024
227e722
hide hover if any panel is being dragged
ThomThomson Jun 19, 2024
1c6e194
first pass at multi grid
ThomThomson Jul 3, 2024
107d06f
Merge remote-tracking branch 'upstream/main' into spaceTime/htmlDragDrop
ThomThomson Jul 3, 2024
e7895a5
move out of storybook
ThomThomson Jul 5, 2024
5f56371
use window event listeners
ThomThomson Jul 8, 2024
81e23a5
Merge remote-tracking branch 'upstream/main' into spaceTime/htmlDragDrop
ThomThomson Jul 15, 2024
69453e6
drag preview and better calculation for grid rows
ThomThomson Jul 17, 2024
2a8a444
start simplicity refactor
ThomThomson Jul 31, 2024
8fb43d4
New code style and organization. All features working as before.
ThomThomson Aug 1, 2024
4cf12c5
finish refactor and clean up.
ThomThomson Aug 1, 2024
00319ee
Merge remote-tracking branch 'upstream/main' into spaceTime/htmlDragDrop
ThomThomson Aug 1, 2024
a59c605
simple collapsing implementation
ThomThomson Aug 9, 2024
327e0f5
switch to scrollX and scrollY
ThomThomson Aug 9, 2024
cf67c34
move to package. Create separate example app
ThomThomson Aug 9, 2024
3d71fad
use top row to determine section overlap, add scroll bars and panel c…
ThomThomson Aug 12, 2024
4e2d80d
simple readme
ThomThomson Aug 12, 2024
2f162b5
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Aug 12, 2024
1bccdb3
Merge remote-tracking branch 'refs/remotes/origin/spaceTime/htmlDragD…
ThomThomson Aug 12, 2024
5e182eb
Stop drag preview from contributing to scroll
ThomThomson Aug 12, 2024
f8b4de5
[CI] Auto-commit changed files from 'node scripts/generate codeowners'
kibanamachine Aug 12, 2024
a88dbfe
chore(NA): update tsconfig.json for kbn/grid-layout
mistic Aug 13, 2024
2158737
Merge remote-tracking branch 'upstream/main' into spaceTime/htmlDragDrop
ThomThomson Aug 13, 2024
eed959d
revert unneeded changes
ThomThomson Aug 13, 2024
e3d1d0e
save without formatting
ThomThomson Aug 13, 2024
75a4e51
review feedback
ThomThomson Aug 15, 2024
48fcdc4
Merge branch 'main' into spaceTime/htmlDragDrop
elasticmachine Aug 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,8 @@ x-pack/plugins/global_search @elastic/appex-sharedux
x-pack/plugins/global_search_providers @elastic/appex-sharedux
x-pack/test/plugin_functional/plugins/global_search_test @elastic/kibana-core
x-pack/plugins/graph @elastic/kibana-visualizations
examples/grid_example @elastic/kibana-presentation
packages/kbn-grid-layout @elastic/kibana-presentation
x-pack/plugins/grokdebugger @elastic/kibana-management
packages/kbn-grouping @elastic/response-ops
packages/kbn-guided-onboarding @elastic/appex-sharedux
Expand Down
3 changes: 3 additions & 0 deletions examples/grid_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Grid Example

This temporary example plugin will be used to build out the new layout engine for Dashboards.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets not make this temporary. It would be nice to have a playground for the layout engine outside of dashboard. Plus, its a good learning resource without all the noise of dashboard implementation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 75a4e51

13 changes: 13 additions & 0 deletions examples/grid_example/kibana.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"type": "plugin",
"id": "@kbn/grid-example-plugin",
"owner": "@elastic/kibana-presentation",
"description": "Temporary example app used to build out the new Dashboard layout system",
"plugin": {
"id": "gridExample",
"server": false,
"browser": true,
"requiredPlugins": ["developerExamples"],
"requiredBundles": []
}
}
69 changes: 69 additions & 0 deletions examples/grid_example/public/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import React from 'react';
import ReactDOM from 'react-dom';
import { GridLayout, type GridLayoutData } from '@kbn/grid-layout';
import { AppMountParameters } from '@kbn/core-application-browser';
import { EuiPageTemplate, EuiProvider } from '@elastic/eui';

export const GridExample = () => {
return (
<EuiProvider>
<EuiPageTemplate offset={0} restrictWidth={false}>
<EuiPageTemplate.Header iconType={'dashboardApp'} pageTitle="Grid Layout Example" />
<EuiPageTemplate.Section>
<GridLayout
ThomThomson marked this conversation as resolved.
Show resolved Hide resolved
renderPanelContents={(id) => {
return <div style={{ padding: 8 }}>{id}</div>;
}}
getCreationOptions={() => {
const initialLayout: GridLayoutData = [
{
title: 'Large section',
isCollapsed: false,
panels: {
panel1: { column: 0, row: 0, width: 12, height: 6, id: 'panel1' },
panel2: { column: 0, row: 6, width: 8, height: 4, id: 'panel2' },
panel3: { column: 8, row: 6, width: 12, height: 4, id: 'panel3' },
panel4: { column: 0, row: 10, width: 48, height: 4, id: 'panel4' },
panel5: { column: 12, row: 0, width: 36, height: 6, id: 'panel5' },
panel6: { column: 24, row: 6, width: 24, height: 4, id: 'panel6' },
panel7: { column: 20, row: 6, width: 4, height: 2, id: 'panel7' },
panel8: { column: 20, row: 8, width: 4, height: 2, id: 'panel8' },
},
},
{
title: 'Small section',
isCollapsed: false,
panels: { panel9: { column: 0, row: 0, width: 12, height: 6, id: 'panel9' } },
},
{
title: 'Another small section',
isCollapsed: false,
panels: { panel10: { column: 24, row: 0, width: 12, height: 6, id: 'panel10' } },
},
];

return {
gridSettings: { gutterSize: 8, rowHeight: 26, columnCount: 48 },
initialLayout,
};
}}
/>
</EuiPageTemplate.Section>
</EuiPageTemplate>
</EuiProvider>
);
};

export const renderGridExampleApp = (element: AppMountParameters['element']) => {
ReactDOM.render(<GridExample />, element);

return () => ReactDOM.unmountComponentAtNode(element);
};
11 changes: 11 additions & 0 deletions examples/grid_example/public/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { GridExamplePlugin } from './plugin';

export const plugin = () => new GridExamplePlugin();
42 changes: 42 additions & 0 deletions examples/grid_example/public/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { AppMountParameters, CoreSetup, CoreStart, Plugin } from '@kbn/core/public';
import { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';

export const GRID_EXAMPLE_APP_ID = 'gridExample';
const gridExampleTitle = 'Grid Example';

interface GridExamplePluginSetupDependencies {
developerExamples: DeveloperExamplesSetup;
}

export class GridExamplePlugin
implements Plugin<void, void, GridExamplePluginSetupDependencies, {}>
{
public setup(core: CoreSetup<{}>, { developerExamples }: GridExamplePluginSetupDependencies) {
core.application.register({
id: GRID_EXAMPLE_APP_ID,
title: gridExampleTitle,
visibleIn: [],
async mount(params: AppMountParameters) {
const { renderGridExampleApp } = await import('./app');
return renderGridExampleApp(params.element);
},
});
developerExamples.register({
appId: GRID_EXAMPLE_APP_ID,
title: gridExampleTitle,
description: `This temporary example plugin will be used to build out the new layout engine for Dashboards.`,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, lets change wording to call it a playground for the layout engine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 75a4e51

});
}

public start(core: CoreStart, deps: {}) {}

public stop() {}
}
14 changes: 14 additions & 0 deletions examples/grid_example/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "target/types"
},
"include": ["index.ts", "public/**/*.ts", "public/**/*.tsx", "../../typings/**/*"],
"exclude": ["target/**/*"],
"kbn_references": [
"@kbn/grid-layout",
"@kbn/core-application-browser",
"@kbn/core",
"@kbn/developer-examples-plugin",
]
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,8 @@
"@kbn/global-search-providers-plugin": "link:x-pack/plugins/global_search_providers",
"@kbn/global-search-test-plugin": "link:x-pack/test/plugin_functional/plugins/global_search_test",
"@kbn/graph-plugin": "link:x-pack/plugins/graph",
"@kbn/grid-example-plugin": "link:examples/grid_example",
"@kbn/grid-layout": "link:packages/kbn-grid-layout",
"@kbn/grokdebugger-plugin": "link:x-pack/plugins/grokdebugger",
"@kbn/grouping": "link:packages/kbn-grouping",
"@kbn/guided-onboarding": "link:packages/kbn-guided-onboarding",
Expand Down
3 changes: 3 additions & 0 deletions packages/kbn-grid-layout/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @kbn/grid-layout

Contains a simple drag and drop layout engine for Kibana Dashboards.
92 changes: 92 additions & 0 deletions packages/kbn-grid-layout/grid/grid_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import { EuiPortal, transparentize } from '@elastic/eui';
import { css } from '@emotion/react';
import { useBatchedPublishingSubjects } from '@kbn/presentation-publishing';
import { euiThemeVars } from '@kbn/ui-theme';
import React from 'react';
import { GridRow } from './grid_row';
import { GridLayoutData, GridSettings } from './types';
import { useGridLayoutEvents } from './use_grid_layout_events';
import { useGridLayoutState } from './use_grid_layout_state';

export const GridLayout = ({
getCreationOptions,
renderPanelContents,
}: {
getCreationOptions: () => { initialLayout: GridLayoutData; gridSettings: GridSettings };
renderPanelContents: (panelId: string) => React.ReactNode;
}) => {
const { gridLayoutStateManager, gridSizeRef } = useGridLayoutState({
getCreationOptions,
});
useGridLayoutEvents({ gridLayoutStateManager });

const [gridLayout, runtimeSettings, interactionEvent] = useBatchedPublishingSubjects(
gridLayoutStateManager.gridLayout$,
gridLayoutStateManager.runtimeSettings$,
gridLayoutStateManager.interactionEvent$
);

return (
<div ref={gridSizeRef}>
{gridLayout.map((rowData, rowIndex) => {
return (
<GridRow
key={rowIndex}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about using rowData.title as key since index is not a recommended key?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! Addressed in 75a4e51

rowData={rowData}
rowIndex={rowIndex}
runtimeSettings={runtimeSettings}
activePanelId={interactionEvent?.id}
renderPanelContents={renderPanelContents}
targetRowIndex={interactionEvent?.targetRowIndex}
toggleIsCollapsed={() => {
const currentLayout = gridLayoutStateManager.gridLayout$.value;
currentLayout[rowIndex].isCollapsed = !currentLayout[rowIndex].isCollapsed;
gridLayoutStateManager.gridLayout$.next(currentLayout);
}}
setInteractionEvent={(nextInteractionEvent) => {
if (!nextInteractionEvent) {
gridLayoutStateManager.hideDragPreview();
}
gridLayoutStateManager.interactionEvent$.next(nextInteractionEvent);
}}
ref={(element) => (gridLayoutStateManager.rowRefs.current[rowIndex] = element)}
/>
);
})}
<EuiPortal>
<div
css={css`
top: 0;
left: 0;
width: 100vw;
height: 100vh;
position: fixed;
overflow: hidden;
pointer-events: none;
z-index: ${euiThemeVars.euiZModal};
`}
>
<div
ref={gridLayoutStateManager.dragPreviewRef}
css={css`
pointer-events: none;
z-index: ${euiThemeVars.euiZModal};
border-radius: ${euiThemeVars.euiBorderRadius};
background-color: ${transparentize(euiThemeVars.euiColorSuccess, 0.2)};
transition: opacity 100ms linear;
position: absolute;
`}
/>
</div>
</EuiPortal>
</div>
);
};
Loading