From c4e40287fd432a75bedf726ae28a4bc628489004 Mon Sep 17 00:00:00 2001 From: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> Date: Tue, 8 Oct 2024 11:07:42 +1100 Subject: [PATCH] [8.x] [Vega] Fix element sizing issues in fullscreen mode (#194330) (#195328) # Backport This will backport the following commits from `main` to `8.x`: - [[Vega] Fix element sizing issues in fullscreen mode (#194330)](https://github.com/elastic/kibana/pull/194330) ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) Co-authored-by: Nick Partridge --- ....snap => vega_visualization.test.tsx.snap} | 4 +- .../public/components/vega_vis_component.tsx | 30 +++-------- .../vega/public/vega_view/vega_base_view.js | 12 ++--- ...on.test.js => vega_visualization.test.tsx} | 53 ++++++++++--------- .../vega/public/vega_visualization.ts | 5 +- 5 files changed, 44 insertions(+), 60 deletions(-) rename src/plugins/vis_types/vega/public/__snapshots__/{vega_visualization.test.js.snap => vega_visualization.test.tsx.snap} (77%) rename src/plugins/vis_types/vega/public/{vega_visualization.test.js => vega_visualization.test.tsx} (73%) diff --git a/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap b/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.tsx.snap similarity index 77% rename from src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap rename to src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.tsx.snap index a81418c79bb0b..dad12b304efdf 100644 --- a/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.js.snap +++ b/src/plugins/vis_types/vega/public/__snapshots__/vega_visualization.test.tsx.snap @@ -2,6 +2,6 @@ exports[`VegaVisualizations VegaVisualization - basics should show vega graph (may fail in dev env) 1`] = `"
"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"
"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 1`] = `"
"`; -exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"
"`; +exports[`VegaVisualizations VegaVisualization - basics should show vegalite graph and update on resize (may fail in dev env) 2`] = `"
"`; diff --git a/src/plugins/vis_types/vega/public/components/vega_vis_component.tsx b/src/plugins/vis_types/vega/public/components/vega_vis_component.tsx index b7a68593ff5dc..7b92bf085179c 100644 --- a/src/plugins/vis_types/vega/public/components/vega_vis_component.tsx +++ b/src/plugins/vis_types/vega/public/components/vega_vis_component.tsx @@ -7,9 +7,8 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import React, { useEffect, useRef, useMemo, useCallback } from 'react'; +import React, { useEffect, useRef, useCallback } from 'react'; import { EuiResizeObserver, EuiResizeObserverProps, useEuiTheme } from '@elastic/eui'; -import { throttle } from 'lodash'; import type { IInterpreterRenderHandlers, RenderMode } from '@kbn/expressions-plugin/common'; import { createVegaVisualization } from '../vega_visualization'; @@ -28,8 +27,6 @@ interface VegaVisComponentProps { type VegaVisController = InstanceType>; -const THROTTLE_INTERVAL = 300; - export const VegaVisComponent = ({ visData, fireEvent, @@ -64,26 +61,11 @@ export const VegaVisComponent = ({ } }, [renderComplete, visData]); - const resizeChart = useMemo( - () => - throttle( - (dimensions) => { - visController.current?.resize(dimensions); - }, - THROTTLE_INTERVAL, - { leading: false, trailing: true } - ), - [] - ); - - const onContainerResize: EuiResizeObserverProps['onResize'] = useCallback( - (dimensions) => { - if (renderCompleted.current) { - resizeChart(dimensions); - } - }, - [resizeChart] - ); + const onContainerResize: EuiResizeObserverProps['onResize'] = useCallback((dimensions) => { + if (renderCompleted.current) { + visController.current?.resize(dimensions); + } + }, []); const euiTheme = useEuiTheme(); diff --git a/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js b/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js index c2efcced1bb78..d517e593e2227 100644 --- a/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js +++ b/src/plugins/vis_types/vega/public/vega_view/vega_base_view.js @@ -282,9 +282,9 @@ export class VegaBaseView { } } - async resize() { + async resize(dimensions) { if (this._parser.useResize && this._view) { - this.updateVegaSize(this._view); + this.updateVegaSize(this._view, dimensions); await this._view.runAsync(); // The derived class should create this method @@ -293,12 +293,8 @@ export class VegaBaseView { } updateVegaSize(view, dimensions) { - const width = Math.floor( - Math.max(0, dimensions?.width ?? this._container.getBoundingClientRect().width) - ); - const height = Math.floor( - Math.max(0, dimensions?.height ?? this._container.getBoundingClientRect().height) - ); + const width = Math.floor(Math.max(0, dimensions?.width ?? this._container.clientWidth - 1)); + const height = Math.floor(Math.max(0, dimensions?.height ?? this._container.clientHeight - 1)); if (view.width() !== width || view.height() !== height) { view.width(width).height(height); diff --git a/src/plugins/vis_types/vega/public/vega_visualization.test.js b/src/plugins/vis_types/vega/public/vega_visualization.test.tsx similarity index 73% rename from src/plugins/vis_types/vega/public/vega_visualization.test.js rename to src/plugins/vis_types/vega/public/vega_visualization.test.tsx index 3d86b3ccdca07..ab51db562f4f2 100644 --- a/src/plugins/vis_types/vega/public/vega_visualization.test.js +++ b/src/plugins/vis_types/vega/public/vega_visualization.test.tsx @@ -8,8 +8,9 @@ */ import 'jest-canvas-mock'; +import { render, screen } from '@testing-library/react'; -import { createVegaVisualization } from './vega_visualization'; +import { VegaVisType, createVegaVisualization } from './vega_visualization'; import vegaliteGraph from './test_utils/vegalite_graph.json'; import vegaGraph from './test_utils/vega_graph.json'; @@ -21,37 +22,41 @@ import { setInjectedVars, setData, setNotifications } from './services'; import { coreMock } from '@kbn/core/public/mocks'; import { dataPluginMock } from '@kbn/data-plugin/public/mocks'; import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks'; +import { VegaVisualizationDependencies } from './plugin'; +import React from 'react'; +import { TimeCache } from './data_model/time_cache'; jest.mock('./default_spec', () => ({ getDefaultSpec: () => jest.requireActual('./test_utils/default.spec.json'), })); describe('VegaVisualizations', () => { - let domNode; - let VegaVisualization; - let vegaVisualizationDependencies; - - let mockGetBoundingClientRect; - let mockedWidthValue; - let mockedHeightValue; + let domNode: HTMLDivElement; + let VegaVisualization: VegaVisType; + let vegaVisualizationDependencies: VegaVisualizationDependencies; + let mockedHeightValue: number; + let mockedWidthValue: number; const coreStart = coreMock.createStart(); const dataPluginStart = dataPluginMock.createStartContract(); const dataViewsPluginStart = dataViewPluginMocks.createStartContract(); const setupDOM = (width = 512, height = 512) => { + render(
); + domNode = screen.getByTestId('vega-vis-text'); + domNode.style.height = `${height}px`; + domNode.style.width = `${width}px`; mockedWidthValue = width; mockedHeightValue = height; - domNode = document.createElement('div'); - mockGetBoundingClientRect = jest - .spyOn(Element.prototype, 'getBoundingClientRect') - .mockImplementation(() => ({ width: mockedWidthValue, height: mockedHeightValue })); + // rtl does not update client dimensions on element, see https://github.com/testing-library/react-testing-library/issues/353 + jest + .spyOn(Element.prototype, 'clientHeight', 'get') + .mockImplementation(() => mockedHeightValue); + jest.spyOn(Element.prototype, 'clientWidth', 'get').mockImplementation(() => mockedWidthValue); }; - const mockGetServiceSettings = () => { - return {}; - }; + const mockGetServiceSettings = jest.fn() as any; beforeEach(() => { setInjectedVars({ @@ -68,7 +73,7 @@ describe('VegaVisualizations', () => { getServiceSettings: mockGetServiceSettings, }; - VegaVisualization = createVegaVisualization(vegaVisualizationDependencies); + VegaVisualization = createVegaVisualization(vegaVisualizationDependencies, 'view'); }); describe('VegaVisualization - basics', () => { @@ -76,15 +81,11 @@ describe('VegaVisualizations', () => { setupDOM(); }); - afterEach(() => { - mockGetBoundingClientRect.mockRestore(); - }); - test('should show vegalite graph and update on resize (may fail in dev env)', async () => { const mockedConsoleLog = jest.spyOn(console, 'log'); // mocked console.log to avoid messages in the console when running tests mockedConsoleLog.mockImplementation(() => {}); // comment this line when console logging for debugging comment this line - let vegaVis; + let vegaVis: InstanceType; try { vegaVis = new VegaVisualization(domNode, jest.fn()); @@ -95,8 +96,8 @@ describe('VegaVisualizations', () => { indexPatterns: dataViewsPluginStart, uiSettings: coreStart.uiSettings, }), - 0, - 0, + new TimeCache(dataPluginStart.query.timefilter.timefilter, 0), + {}, mockGetServiceSettings ); await vegaParser.parseAsync(); @@ -106,12 +107,14 @@ describe('VegaVisualizations', () => { mockedWidthValue = 256; mockedHeightValue = 250; + // @ts-expect-error - accessing private member await vegaVis.vegaView.resize(); expect(domNode.innerHTML).toMatchSnapshot(); } finally { vegaVis.destroy(); } + // eslint-disable-next-line no-console expect(console.log).toBeCalledTimes(2); mockedConsoleLog.mockRestore(); }); @@ -127,8 +130,8 @@ describe('VegaVisualizations', () => { indexPatterns: dataViewsPluginStart, uiSettings: coreStart.uiSettings, }), - 0, - 0, + new TimeCache(dataPluginStart.query.timefilter.timefilter, 0), + {}, mockGetServiceSettings ); await vegaParser.parseAsync(); diff --git a/src/plugins/vis_types/vega/public/vega_visualization.ts b/src/plugins/vis_types/vega/public/vega_visualization.ts index 2a1eb11f5e9c0..0060a1c0b7061 100644 --- a/src/plugins/vis_types/vega/public/vega_visualization.ts +++ b/src/plugins/vis_types/vega/public/vega_visualization.ts @@ -15,7 +15,10 @@ import { getNotifications, getData } from './services'; import type { VegaView } from './vega_view/vega_view'; import { createVegaStateRestorer } from './lib/vega_state_restorer'; -type VegaVisType = new (el: HTMLDivElement, fireEvent: IInterpreterRenderHandlers['event']) => { +export type VegaVisType = new ( + el: HTMLDivElement, + fireEvent: IInterpreterRenderHandlers['event'] +) => { render(visData: VegaParser): Promise; resize(dimensions?: { height: number; width: number }): Promise; destroy(): void;