Skip to content

Commit

Permalink
[8.x] [Vega] Fix element sizing issues in fullscreen mode (elastic#19…
Browse files Browse the repository at this point in the history
…4330) (elastic#195328)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Vega] Fix element sizing issues in fullscreen mode
(elastic#194330)](elastic#194330)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Nick
Partridge","email":"[email protected]"},"sourceCommit":{"committedDate":"2024-10-07T22:20:27Z","message":"[Vega]
Fix element sizing issues in fullscreen mode (elastic#194330)\n\n- Fixes
elastic#194011 where a Vega visualization does not respect the
enclosing\r\nelement dimensions.\r\n- Fixes elastic#194861 where resizing Vega
visualization panel fails to update to the\r\nlatest panel dimensions
causing scroll
bars.","sha":"55c2fd7fc1ef292961d5d69d20d1711b1fbbd468","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:fix","Feature:Vega","Team:Visualizations","v9.0.0","backport:prev-minor"],"title":"[Vega]
Fix element sizing issues in fullscreen
mode","number":194330,"url":"https://github.com/elastic/kibana/pull/194330","mergeCommit":{"message":"[Vega]
Fix element sizing issues in fullscreen mode (elastic#194330)\n\n- Fixes
elastic#194011 where a Vega visualization does not respect the
enclosing\r\nelement dimensions.\r\n- Fixes elastic#194861 where resizing Vega
visualization panel fails to update to the\r\nlatest panel dimensions
causing scroll
bars.","sha":"55c2fd7fc1ef292961d5d69d20d1711b1fbbd468"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/194330","number":194330,"mergeCommit":{"message":"[Vega]
Fix element sizing issues in fullscreen mode (elastic#194330)\n\n- Fixes
elastic#194011 where a Vega visualization does not respect the
enclosing\r\nelement dimensions.\r\n- Fixes elastic#194861 where resizing Vega
visualization panel fails to update to the\r\nlatest panel dimensions
causing scroll
bars.","sha":"55c2fd7fc1ef292961d5d69d20d1711b1fbbd468"}}]}] BACKPORT-->

Co-authored-by: Nick Partridge <[email protected]>
  • Loading branch information
kibanamachine and nickofthyme authored Oct 8, 2024
1 parent 858e70c commit c4e4028
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 60 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -28,8 +27,6 @@ interface VegaVisComponentProps {

type VegaVisController = InstanceType<ReturnType<typeof createVegaVisualization>>;

const THROTTLE_INTERVAL = 300;

export const VegaVisComponent = ({
visData,
fireEvent,
Expand Down Expand Up @@ -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();

Expand Down
12 changes: 4 additions & 8 deletions src/plugins/vis_types/vega/public/vega_view/vega_base_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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(<div data-test-subj="vega-vis-text" />);
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({
Expand All @@ -68,23 +73,19 @@ describe('VegaVisualizations', () => {
getServiceSettings: mockGetServiceSettings,
};

VegaVisualization = createVegaVisualization(vegaVisualizationDependencies);
VegaVisualization = createVegaVisualization(vegaVisualizationDependencies, 'view');
});

describe('VegaVisualization - basics', () => {
beforeEach(async () => {
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<VegaVisType>;
try {
vegaVis = new VegaVisualization(domNode, jest.fn());

Expand All @@ -95,8 +96,8 @@ describe('VegaVisualizations', () => {
indexPatterns: dataViewsPluginStart,
uiSettings: coreStart.uiSettings,
}),
0,
0,
new TimeCache(dataPluginStart.query.timefilter.timefilter, 0),
{},
mockGetServiceSettings
);
await vegaParser.parseAsync();
Expand All @@ -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();
});
Expand All @@ -127,8 +130,8 @@ describe('VegaVisualizations', () => {
indexPatterns: dataViewsPluginStart,
uiSettings: coreStart.uiSettings,
}),
0,
0,
new TimeCache(dataPluginStart.query.timefilter.timefilter, 0),
{},
mockGetServiceSettings
);
await vegaParser.parseAsync();
Expand Down
5 changes: 4 additions & 1 deletion src/plugins/vis_types/vega/public/vega_visualization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>;
resize(dimensions?: { height: number; width: number }): Promise<void>;
destroy(): void;
Expand Down

0 comments on commit c4e4028

Please sign in to comment.