kibana/packages/kbn-resizable-layout/src/panels_resizable.test.tsx
Davis McPhee 3e1865513d
[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.


![resize](71b9a0ae-1795-43c8-acb0-e75fe46e2a8a)

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>
2023-09-27 21:52:25 -03:00

246 lines
9.5 KiB
TypeScript

/*
* 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 type { ReactWrapper } from 'enzyme';
import { mount } from 'enzyme';
import { ReactElement, useState } from 'react';
import React from 'react';
import { PanelsResizable } from './panels_resizable';
import { act } from 'react-dom/test-utils';
const containerHeight = 1000;
const containerWidth = 500;
const fixedPanelId = 'fixedPanel';
jest.mock('@elastic/eui', () => ({
...jest.requireActual('@elastic/eui'),
useResizeObserver: jest.fn(),
useGeneratedHtmlId: jest.fn(() => fixedPanelId),
}));
import * as eui from '@elastic/eui';
import { waitFor } from '@testing-library/dom';
import { ResizableLayoutDirection } from '../types';
describe('Panels resizable', () => {
const mountComponent = ({
className = '',
direction = ResizableLayoutDirection.Vertical,
container = null,
initialFixedPanelSize = 0,
minFixedPanelSize = 0,
minFlexPanelSize = 0,
fixedPanel = <></>,
flexPanel = <></>,
attachTo,
onFixedPanelSizeChange = jest.fn(),
}: {
className?: string;
direction?: ResizableLayoutDirection;
container?: HTMLElement | null;
initialFixedPanelSize?: number;
minFixedPanelSize?: number;
minFlexPanelSize?: number;
fixedPanel?: ReactElement;
flexPanel?: ReactElement;
attachTo?: HTMLElement;
onFixedPanelSizeChange?: (fixedPanelSize: number) => void;
}) => {
const PanelsWrapper = ({ fixedPanelSize }: { fixedPanelSize?: number }) => {
const [panelSizes, setPanelSizes] = useState({
fixedPanelSizePct: 50,
flexPanelSizePct: 50,
});
return (
<PanelsResizable
className={className}
direction={direction}
container={container}
fixedPanelSize={fixedPanelSize ?? initialFixedPanelSize}
minFixedPanelSize={minFixedPanelSize}
minFlexPanelSize={minFlexPanelSize}
panelSizes={panelSizes}
fixedPanel={fixedPanel}
flexPanel={flexPanel}
onFixedPanelSizeChange={onFixedPanelSizeChange}
setPanelSizes={setPanelSizes}
/>
);
};
return mount(<PanelsWrapper />, attachTo ? { attachTo } : undefined);
};
const expectCorrectPanelSizes = (
component: ReactWrapper,
currentContainerSize: number,
fixedPanelSize: number
) => {
const fixedPanelSizePct = (fixedPanelSize / currentContainerSize) * 100;
expect(
component.find('[data-test-subj="resizableLayoutResizablePanelFixed"]').at(0).prop('size')
).toBe(fixedPanelSizePct);
expect(
component.find('[data-test-subj="resizableLayoutResizablePanelFlex"]').at(0).prop('size')
).toBe(100 - fixedPanelSizePct);
};
const forceRender = (component: ReactWrapper) => {
component.setProps({}).update();
};
beforeEach(() => {
jest
.spyOn(eui, 'useResizeObserver')
.mockReturnValue({ height: containerHeight, width: containerWidth });
});
it('should render both panels', () => {
const fixedPanel = <div data-test-subj="fixedPanel" />;
const flexPanel = <div data-test-subj="flexPanel" />;
const component = mountComponent({ fixedPanel, flexPanel });
expect(component.contains(fixedPanel)).toBe(true);
expect(component.contains(flexPanel)).toBe(true);
});
it('should set the initial sizes of both panels', () => {
const initialFixedPanelSize = 200;
const component = mountComponent({ initialFixedPanelSize });
expectCorrectPanelSizes(component, containerHeight, initialFixedPanelSize);
});
it('should set the correct sizes of both panels when the panels are resized', () => {
const initialFixedPanelSize = 200;
const onFixedPanelSizeChange = jest.fn((fixedPanelSize) => {
component.setProps({ fixedPanelSize }).update();
});
const component = mountComponent({ initialFixedPanelSize, onFixedPanelSizeChange });
expectCorrectPanelSizes(component, containerHeight, initialFixedPanelSize);
const newFixedPanelSizePct = 30;
const onPanelSizeChange = component
.find('[data-test-subj="resizableLayoutResizableContainer"]')
.at(0)
.prop('onPanelWidthChange') as Function;
act(() => {
onPanelSizeChange({ [fixedPanelId]: newFixedPanelSizePct });
});
forceRender(component);
const newFixedPanelSize = (newFixedPanelSizePct / 100) * containerHeight;
expect(onFixedPanelSizeChange).toHaveBeenCalledWith(newFixedPanelSize);
expectCorrectPanelSizes(component, containerHeight, newFixedPanelSize);
});
it('should maintain the size of the fixed panel and resize the flex panel when the container size changes', () => {
const initialFixedPanelSize = 200;
const component = mountComponent({ initialFixedPanelSize });
expectCorrectPanelSizes(component, containerHeight, initialFixedPanelSize);
const newContainerSize = 2000;
jest.spyOn(eui, 'useResizeObserver').mockReturnValue({ height: newContainerSize, width: 0 });
forceRender(component);
expectCorrectPanelSizes(component, newContainerSize, initialFixedPanelSize);
});
it('should resize the fixed panel once the flex panel is at its minimum size', () => {
const initialFixedPanelSize = 500;
const minFixedPanelSize = 100;
const minFlexPanelSize = 100;
const component = mountComponent({
initialFixedPanelSize,
minFixedPanelSize,
minFlexPanelSize,
});
expectCorrectPanelSizes(component, containerHeight, initialFixedPanelSize);
const newContainerSize = 400;
jest.spyOn(eui, 'useResizeObserver').mockReturnValue({ height: newContainerSize, width: 0 });
forceRender(component);
expectCorrectPanelSizes(component, newContainerSize, newContainerSize - minFlexPanelSize);
jest.spyOn(eui, 'useResizeObserver').mockReturnValue({ height: containerHeight, width: 0 });
forceRender(component);
expectCorrectPanelSizes(component, containerHeight, initialFixedPanelSize);
});
it('should maintain the minimum sizes of both panels when the container is too small to fit them', () => {
const initialFixedPanelSize = 500;
const minFixedPanelSize = 100;
const minFlexPanelSize = 150;
const component = mountComponent({
initialFixedPanelSize,
minFixedPanelSize,
minFlexPanelSize,
});
expectCorrectPanelSizes(component, containerHeight, initialFixedPanelSize);
const newContainerSize = 200;
jest.spyOn(eui, 'useResizeObserver').mockReturnValue({ height: newContainerSize, width: 0 });
forceRender(component);
expect(
component.find('[data-test-subj="resizableLayoutResizablePanelFixed"]').at(0).prop('size')
).toBe((minFixedPanelSize / newContainerSize) * 100);
expect(
component.find('[data-test-subj="resizableLayoutResizablePanelFlex"]').at(0).prop('size')
).toBe((minFlexPanelSize / newContainerSize) * 100);
jest.spyOn(eui, 'useResizeObserver').mockReturnValue({ height: containerHeight, width: 0 });
forceRender(component);
expectCorrectPanelSizes(component, containerHeight, initialFixedPanelSize);
});
it('should blur the resize button after a resize', async () => {
const attachTo = document.createElement('div');
document.body.appendChild(attachTo);
const component = mountComponent({ attachTo });
const getContainer = () =>
component.find('[data-test-subj="resizableLayoutResizableContainer"]').at(0);
const resizeButton = component.find('button[data-test-subj="resizableLayoutResizableButton"]');
act(() => {
const onResizeStart = getContainer().prop('onResizeStart') as Function;
onResizeStart('pointer');
});
(resizeButton.getDOMNode() as HTMLElement).focus();
forceRender(component);
act(() => {
const onResizeEnd = getContainer().prop('onResizeEnd') as Function;
onResizeEnd();
});
expect(resizeButton.getDOMNode()).toHaveFocus();
await waitFor(() => {
expect(resizeButton.getDOMNode()).not.toHaveFocus();
});
});
it('should pass direction "vertical" to EuiResizableContainer when direction is ResizableLayoutDirection.Vertical', () => {
const component = mountComponent({ direction: ResizableLayoutDirection.Vertical });
expect(
component.find('[data-test-subj="resizableLayoutResizableContainer"]').at(0).prop('direction')
).toBe('vertical');
});
it('should pass direction "horizontal" to EuiResizableContainer when direction is ResizableLayoutDirection.Horizontal', () => {
const component = mountComponent({ direction: ResizableLayoutDirection.Horizontal });
expect(
component.find('[data-test-subj="resizableLayoutResizableContainer"]').at(0).prop('direction')
).toBe('horizontal');
});
it('should use containerHeight when direction is ResizableLayoutDirection.Vertical', () => {
const initialFixedPanelSize = 200;
const component = mountComponent({
direction: ResizableLayoutDirection.Vertical,
initialFixedPanelSize,
});
expectCorrectPanelSizes(component, containerHeight, initialFixedPanelSize);
});
it('should use containerWidth when direction is ResizableLayoutDirection.Horizontal', () => {
const initialFixedPanelSize = 200;
const component = mountComponent({
direction: ResizableLayoutDirection.Horizontal,
initialFixedPanelSize,
});
expectCorrectPanelSizes(component, containerWidth, initialFixedPanelSize);
});
});