mirror of
https://github.com/elastic/kibana.git
synced 2025-06-27 10:40:07 -04:00
[Discover] Add resize support to the Discover field list sidebar (#167066)
## Summary
This PR adds resize support to the Discover field list sidebar, which is
persisted to a user's local storage similar to the resizable chart
height.
Additionally it migrates the resizable layout code from Unified
Histogram to a new package called `kbn-resizable-layout` so it can be
shared between Discover and Unified Histogram, as well as act as a new
platform component that other teams can consume to create their own
resizable layouts.

Resolves #9531.
### Checklist
- [ ] ~Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)~
- [ ]
~[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials~
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [x] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [ ] ~Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))~
- [ ] ~If a plugin configuration key changed, check if it needs to be
allowlisted in the cloud and added to the [docker
list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)~
- [x] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [x] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
### For maintainers
- [ ] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process)
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
272219ca49
commit
3e1865513d
51 changed files with 1822 additions and 789 deletions
11
examples/resizable_layout_examples/kibana.jsonc
Normal file
11
examples/resizable_layout_examples/kibana.jsonc
Normal file
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"type": "plugin",
|
||||
"id": "@kbn/resizable-layout-examples-plugin",
|
||||
"owner": "@elastic/kibana-data-discovery",
|
||||
"plugin": {
|
||||
"id": "resizableLayoutExamples",
|
||||
"server": false,
|
||||
"browser": true,
|
||||
"requiredPlugins": ["developerExamples"]
|
||||
}
|
||||
}
|
161
examples/resizable_layout_examples/public/application.tsx
Normal file
161
examples/resizable_layout_examples/public/application.tsx
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* 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 { CoreThemeProvider } from '@kbn/core-theme-browser-internal';
|
||||
import type { AppMountParameters } from '@kbn/core/public';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import React, { ReactNode, useState } from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { useIsWithinBreakpoints } from '@elastic/eui';
|
||||
import { css } from '@emotion/react';
|
||||
import {
|
||||
ResizableLayout,
|
||||
ResizableLayoutDirection,
|
||||
ResizableLayoutMode,
|
||||
} from '@kbn/resizable-layout';
|
||||
import { createHtmlPortalNode, InPortal, OutPortal } from 'react-reverse-portal';
|
||||
|
||||
const ResizableSection = ({
|
||||
direction,
|
||||
initialFixedPanelSize,
|
||||
minFixedPanelSize,
|
||||
minFlexPanelSize,
|
||||
fixedPanelColor,
|
||||
flexPanelColor,
|
||||
fixedPanelContent,
|
||||
flexPanelContent,
|
||||
}: {
|
||||
direction: ResizableLayoutDirection;
|
||||
initialFixedPanelSize: number;
|
||||
minFixedPanelSize: number;
|
||||
minFlexPanelSize: number;
|
||||
fixedPanelColor: string;
|
||||
flexPanelColor: string;
|
||||
fixedPanelContent: ReactNode;
|
||||
flexPanelContent: ReactNode;
|
||||
}) => {
|
||||
const [fixedPanelSize, setFixedPanelSize] = useState(initialFixedPanelSize);
|
||||
const [container, setContainer] = useState<HTMLDivElement | null>(null);
|
||||
const [fixedPanelNode] = useState(() =>
|
||||
createHtmlPortalNode({ attributes: { class: 'eui-fullHeight' } })
|
||||
);
|
||||
const [flexPanelNode] = useState(() =>
|
||||
createHtmlPortalNode({ attributes: { class: 'eui-fullHeight' } })
|
||||
);
|
||||
|
||||
const isMobile = useIsWithinBreakpoints(['xs', 's']);
|
||||
const layoutMode = isMobile ? ResizableLayoutMode.Static : ResizableLayoutMode.Resizable;
|
||||
const layoutDirection = isMobile ? ResizableLayoutDirection.Vertical : direction;
|
||||
|
||||
const fullWidthAndHeightCss = css`
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
`;
|
||||
const panelBaseCss = css`
|
||||
${fullWidthAndHeightCss}
|
||||
padding: 20px;
|
||||
font-size: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
`;
|
||||
const fixedPanelCss = css`
|
||||
${panelBaseCss}
|
||||
background-color: ${fixedPanelColor};
|
||||
`;
|
||||
const flexPanelCss = css`
|
||||
${panelBaseCss}
|
||||
background-color: ${flexPanelColor};
|
||||
`;
|
||||
|
||||
return (
|
||||
<div ref={setContainer} css={fullWidthAndHeightCss}>
|
||||
<InPortal node={fixedPanelNode}>
|
||||
<div css={fixedPanelCss}>{fixedPanelContent}</div>
|
||||
</InPortal>
|
||||
<InPortal node={flexPanelNode}>
|
||||
<div css={flexPanelCss}>{flexPanelContent}</div>
|
||||
</InPortal>
|
||||
<ResizableLayout
|
||||
mode={layoutMode}
|
||||
direction={layoutDirection}
|
||||
container={container}
|
||||
fixedPanelSize={fixedPanelSize}
|
||||
minFixedPanelSize={minFixedPanelSize}
|
||||
minFlexPanelSize={minFlexPanelSize}
|
||||
fixedPanel={<OutPortal node={fixedPanelNode} />}
|
||||
flexPanel={<OutPortal node={flexPanelNode} />}
|
||||
onFixedPanelSizeChange={setFixedPanelSize}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const renderApp = ({ element, theme$ }: AppMountParameters) => {
|
||||
ReactDOM.render(
|
||||
<I18nProvider>
|
||||
<CoreThemeProvider theme$={theme$}>
|
||||
<div
|
||||
css={css`
|
||||
height: calc(100vh - var(--euiFixedHeadersOffset, 0));
|
||||
`}
|
||||
>
|
||||
<ResizableSection
|
||||
direction={ResizableLayoutDirection.Horizontal}
|
||||
initialFixedPanelSize={500}
|
||||
minFixedPanelSize={300}
|
||||
minFlexPanelSize={500}
|
||||
fixedPanelColor="#16E0BD"
|
||||
flexPanelColor="#89A6FB"
|
||||
fixedPanelContent={
|
||||
<ResizableSection
|
||||
direction={ResizableLayoutDirection.Vertical}
|
||||
initialFixedPanelSize={200}
|
||||
minFixedPanelSize={100}
|
||||
minFlexPanelSize={200}
|
||||
fixedPanelColor="#E3655B"
|
||||
flexPanelColor="#FDCA40"
|
||||
fixedPanelContent="Sidebar Header"
|
||||
flexPanelContent="Sidebar Body"
|
||||
/>
|
||||
}
|
||||
flexPanelContent={
|
||||
<ResizableSection
|
||||
direction={ResizableLayoutDirection.Vertical}
|
||||
initialFixedPanelSize={300}
|
||||
minFixedPanelSize={200}
|
||||
minFlexPanelSize={300}
|
||||
fixedPanelColor="#FFA0AC"
|
||||
flexPanelColor="#F6F740"
|
||||
fixedPanelContent="Main Body Header"
|
||||
flexPanelContent={
|
||||
<ResizableSection
|
||||
direction={ResizableLayoutDirection.Horizontal}
|
||||
initialFixedPanelSize={400}
|
||||
minFixedPanelSize={200}
|
||||
minFlexPanelSize={200}
|
||||
fixedPanelColor="#78C3FB"
|
||||
flexPanelColor="#EF709D"
|
||||
fixedPanelContent="Main Body Left"
|
||||
flexPanelContent="Main Body Right"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</CoreThemeProvider>
|
||||
</I18nProvider>,
|
||||
element
|
||||
);
|
||||
|
||||
return () => {
|
||||
ReactDOM.unmountComponentAtNode(element);
|
||||
};
|
||||
};
|
13
examples/resizable_layout_examples/public/index.ts
Normal file
13
examples/resizable_layout_examples/public/index.ts
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 { ResizableLayoutExamplesPlugin } from './plugin';
|
||||
|
||||
export function plugin() {
|
||||
return new ResizableLayoutExamplesPlugin();
|
||||
}
|
44
examples/resizable_layout_examples/public/plugin.tsx
Normal file
44
examples/resizable_layout_examples/public/plugin.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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, AppNavLinkStatus, CoreSetup, Plugin } from '@kbn/core/public';
|
||||
import type { DeveloperExamplesSetup } from '@kbn/developer-examples-plugin/public';
|
||||
import image from './resizable_layout_examples.png';
|
||||
|
||||
export interface ResizableLayoutExamplesSetupPlugins {
|
||||
developerExamples: DeveloperExamplesSetup;
|
||||
}
|
||||
|
||||
const PLUGIN_ID = 'resizableLayoutExamples';
|
||||
const PLUGIN_NAME = 'Resizable Layout Examples';
|
||||
|
||||
export class ResizableLayoutExamplesPlugin implements Plugin {
|
||||
setup(core: CoreSetup, plugins: ResizableLayoutExamplesSetupPlugins) {
|
||||
core.application.register({
|
||||
id: PLUGIN_ID,
|
||||
title: PLUGIN_NAME,
|
||||
navLinkStatus: AppNavLinkStatus.hidden,
|
||||
mount: async (params: AppMountParameters) => {
|
||||
// Load application bundle
|
||||
const { renderApp } = await import('./application');
|
||||
// Render the application
|
||||
return renderApp(params);
|
||||
},
|
||||
});
|
||||
|
||||
plugins.developerExamples.register({
|
||||
appId: PLUGIN_ID,
|
||||
title: PLUGIN_NAME,
|
||||
description:
|
||||
'A component for creating resizable layouts containing a fixed width panel and a flexible panel, with support for horizontal and vertical layouts.',
|
||||
image,
|
||||
});
|
||||
}
|
||||
|
||||
start() {}
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 30 KiB |
15
examples/resizable_layout_examples/tsconfig.json
Normal file
15
examples/resizable_layout_examples/tsconfig.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types"
|
||||
},
|
||||
"include": ["common/**/*", "public/**/*", "server/**/*", "../../typings/**/*"],
|
||||
"kbn_references": [
|
||||
"@kbn/resizable-layout",
|
||||
"@kbn/core-theme-browser-internal",
|
||||
"@kbn/core",
|
||||
"@kbn/i18n-react",
|
||||
"@kbn/developer-examples-plugin",
|
||||
],
|
||||
"exclude": ["target/**/*"]
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue