mirror of
https://github.com/grafana/grafana.git
synced 2024-11-22 08:56:43 -06:00
Dashboards: Fix scroll position not being restored when leaving panel edit (#83787)
* Dashboards: Fix scroll position not being restored when leaving panel edit view * remove mock from tests * remove console log * Remove my debugging stuff, and don't render grid if width is 0 * remove old comment (but retain old, probably unneeded css) * rename ref * fix it not actually working anymore!!! * add e2e tests * jsonnet, i guess
This commit is contained in:
parent
6db7eafd7e
commit
183aa09eeb
1791
devenv/dev-dashboards/scenarios/tall_dashboard.json
Normal file
1791
devenv/dev-dashboards/scenarios/tall_dashboard.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -91,6 +91,7 @@
|
|||||||
"table_sparkline_cell": (import '../dev-dashboards/panel-table/table_sparkline_cell.json'),
|
"table_sparkline_cell": (import '../dev-dashboards/panel-table/table_sparkline_cell.json'),
|
||||||
"table_tests": (import '../dev-dashboards/panel-table/table_tests.json'),
|
"table_tests": (import '../dev-dashboards/panel-table/table_tests.json'),
|
||||||
"table_tests_new": (import '../dev-dashboards/panel-table/table_tests_new.json'),
|
"table_tests_new": (import '../dev-dashboards/panel-table/table_tests_new.json'),
|
||||||
|
"tall_dashboard": (import '../dev-dashboards/scenarios/tall_dashboard.json'),
|
||||||
"templating-dashboard-links-and-variables": (import '../dev-dashboards/feature-templating/templating-dashboard-links-and-variables.json'),
|
"templating-dashboard-links-and-variables": (import '../dev-dashboards/feature-templating/templating-dashboard-links-and-variables.json'),
|
||||||
"templating-repeating-panels": (import '../dev-dashboards/feature-templating/templating-repeating-panels.json'),
|
"templating-repeating-panels": (import '../dev-dashboards/feature-templating/templating-repeating-panels.json'),
|
||||||
"templating-repeating-rows": (import '../dev-dashboards/feature-templating/templating-repeating-rows.json'),
|
"templating-repeating-rows": (import '../dev-dashboards/feature-templating/templating-repeating-rows.json'),
|
||||||
|
33
e2e/dashboards-suite/general-dashboards.spec.ts
Normal file
33
e2e/dashboards-suite/general-dashboards.spec.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { e2e } from '../utils';
|
||||||
|
|
||||||
|
const PAGE_UNDER_TEST = 'edediimbjhdz4b/a-tall-dashboard';
|
||||||
|
|
||||||
|
describe('Dashboards', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should restore scroll position', () => {
|
||||||
|
e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST });
|
||||||
|
e2e.components.Panels.Panel.title('Panel #1').should('be.visible');
|
||||||
|
|
||||||
|
// scroll to the bottom
|
||||||
|
e2e.pages.Dashboard.DashNav.navV2()
|
||||||
|
.parent()
|
||||||
|
.parent() // Note, this will probably fail when we change the custom scrollbars
|
||||||
|
.scrollTo('bottom', {
|
||||||
|
timeout: 5 * 1000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// The last panel should be visible...
|
||||||
|
e2e.components.Panels.Panel.title('Panel #50').should('be.visible');
|
||||||
|
|
||||||
|
// Then we open and close the panel editor
|
||||||
|
e2e.components.Panels.Panel.menu('Panel #50').click({ force: true }); // it only shows on hover
|
||||||
|
e2e.components.Panels.Panel.menuItems('Edit').click();
|
||||||
|
e2e.components.PanelEditor.applyButton().click();
|
||||||
|
|
||||||
|
// And the last panel should still be visible!
|
||||||
|
e2e.components.Panels.Panel.title('Panel #50').should('be.visible');
|
||||||
|
});
|
||||||
|
});
|
@ -4,7 +4,6 @@ import React from 'react';
|
|||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { match, Router } from 'react-router-dom';
|
import { match, Router } from 'react-router-dom';
|
||||||
import { useEffectOnce } from 'react-use';
|
import { useEffectOnce } from 'react-use';
|
||||||
import { Props as AutoSizerProps } from 'react-virtualized-auto-sizer';
|
|
||||||
import { mockToolkitActionCreator } from 'test/core/redux/mocks';
|
import { mockToolkitActionCreator } from 'test/core/redux/mocks';
|
||||||
import { TestProvider } from 'test/helpers/TestProvider';
|
import { TestProvider } from 'test/helpers/TestProvider';
|
||||||
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
||||||
@ -71,18 +70,6 @@ jest.mock('@grafana/runtime', () => ({
|
|||||||
getPluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
getPluginLinkExtensions: jest.fn().mockReturnValue({ extensions: [] }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('react-virtualized-auto-sizer', () => {
|
|
||||||
// The size of the children need to be small enough to be outside the view.
|
|
||||||
// So it does not trigger the query to be run by the PanelQueryRunner.
|
|
||||||
return ({ children }: AutoSizerProps) =>
|
|
||||||
children({
|
|
||||||
height: 1,
|
|
||||||
scaledHeight: 1,
|
|
||||||
scaledWidth: 1,
|
|
||||||
width: 1,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function getTestDashboard(overrides?: Partial<Dashboard>, metaOverrides?: Partial<DashboardMeta>): DashboardModel {
|
function getTestDashboard(overrides?: Partial<Dashboard>, metaOverrides?: Partial<DashboardMeta>): DashboardModel {
|
||||||
const data = Object.assign(
|
const data = Object.assign(
|
||||||
{
|
{
|
||||||
|
@ -3,7 +3,6 @@ import React from 'react';
|
|||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { Router } from 'react-router-dom';
|
import { Router } from 'react-router-dom';
|
||||||
import { useEffectOnce } from 'react-use';
|
import { useEffectOnce } from 'react-use';
|
||||||
import { Props as AutoSizerProps } from 'react-virtualized-auto-sizer';
|
|
||||||
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
import { getGrafanaContextMock } from 'test/mocks/getGrafanaContextMock';
|
||||||
|
|
||||||
import { TextBoxVariableModel } from '@grafana/data';
|
import { TextBoxVariableModel } from '@grafana/data';
|
||||||
@ -42,18 +41,6 @@ jest.mock('app/features/dashboard/dashgrid/LazyLoader', () => {
|
|||||||
return { LazyLoader };
|
return { LazyLoader };
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mock('react-virtualized-auto-sizer', () => {
|
|
||||||
// The size of the children need to be small enough to be outside the view.
|
|
||||||
// So it does not trigger the query to be run by the PanelQueryRunner.
|
|
||||||
return ({ children }: AutoSizerProps) =>
|
|
||||||
children({
|
|
||||||
scaledHeight: 1,
|
|
||||||
height: 1,
|
|
||||||
scaledWidth: 1,
|
|
||||||
width: 1,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function setup(props: Props) {
|
function setup(props: Props) {
|
||||||
const context = getGrafanaContextMock();
|
const context = getGrafanaContextMock();
|
||||||
const store = configureStore({});
|
const store = configureStore({});
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import React, { PureComponent, CSSProperties } from 'react';
|
import React, { PureComponent, CSSProperties } from 'react';
|
||||||
import ReactGridLayout, { ItemCallback } from 'react-grid-layout';
|
import ReactGridLayout, { ItemCallback } from 'react-grid-layout';
|
||||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
|
||||||
import { Subscription } from 'rxjs';
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
@ -31,6 +30,7 @@ export interface Props {
|
|||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
panelFilter?: RegExp;
|
panelFilter?: RegExp;
|
||||||
|
width: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DashboardGrid extends PureComponent<Props, State> {
|
export class DashboardGrid extends PureComponent<Props, State> {
|
||||||
@ -47,6 +47,7 @@ export class DashboardGrid extends PureComponent<Props, State> {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
panelFilter: undefined,
|
panelFilter: undefined,
|
||||||
|
width: document.body.clientWidth, // initial very rough estimate
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,22 +292,41 @@ export class DashboardGrid extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private resizeObserver?: ResizeObserver;
|
||||||
|
private rootEl: HTMLDivElement | null = null;
|
||||||
|
onMeasureRef = (rootEl: HTMLDivElement | null) => {
|
||||||
|
if (!rootEl) {
|
||||||
|
if (this.rootEl && this.resizeObserver) {
|
||||||
|
this.resizeObserver.unobserve(this.rootEl);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rootEl = rootEl;
|
||||||
|
this.resizeObserver = new ResizeObserver((entries) => {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
this.setState({ width: entry.contentRect.width });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.resizeObserver.observe(rootEl);
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { isEditable, dashboard } = this.props;
|
const { isEditable, dashboard } = this.props;
|
||||||
|
const { width } = this.state;
|
||||||
|
|
||||||
if (dashboard.panels.length === 0) {
|
if (dashboard.panels.length === 0) {
|
||||||
return <DashboardEmpty dashboard={dashboard} canCreate={isEditable} />;
|
return <DashboardEmpty dashboard={dashboard} canCreate={isEditable} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const draggable = width <= config.theme2.breakpoints.values.md ? false : isEditable;
|
||||||
* We have a parent with "flex: 1 1 0" we need to reset it to "flex: 1 1 auto" to have the AutoSizer
|
|
||||||
* properly working. For more information go here:
|
// pos: rel + z-index is required to create a new stacking context to contain
|
||||||
* https://github.com/bvaughn/react-virtualized/blob/master/docs/usingAutoSizer.md#can-i-use-autosizer-within-a-flex-container
|
// the escalating z-indexes of the panels
|
||||||
*
|
|
||||||
* pos: rel + z-index is required to create a new stacking context to contain the escalating z-indexes of the panels
|
|
||||||
*/
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
ref={this.onMeasureRef}
|
||||||
style={{
|
style={{
|
||||||
flex: '1 1 auto',
|
flex: '1 1 auto',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
@ -314,46 +334,27 @@ export class DashboardGrid extends PureComponent<Props, State> {
|
|||||||
display: this.props.editPanel ? 'none' : undefined,
|
display: this.props.editPanel ? 'none' : undefined,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<AutoSizer disableHeight>
|
<div style={{ width: width, height: '100%' }} ref={this.onGetWrapperDivRef}>
|
||||||
{({ width }) => {
|
<ReactGridLayout
|
||||||
if (width === 0) {
|
width={width}
|
||||||
return null;
|
isDraggable={draggable}
|
||||||
}
|
isResizable={isEditable}
|
||||||
|
containerPadding={[0, 0]}
|
||||||
// Disable draggable if mobile device, solving an issue with unintentionally
|
useCSSTransforms={true}
|
||||||
// moving panels. https://github.com/grafana/grafana/issues/18497
|
margin={[GRID_CELL_VMARGIN, GRID_CELL_VMARGIN]}
|
||||||
const draggable = width <= config.theme2.breakpoints.values.md ? false : isEditable;
|
cols={GRID_COLUMN_COUNT}
|
||||||
|
rowHeight={GRID_CELL_HEIGHT}
|
||||||
return (
|
draggableHandle=".grid-drag-handle"
|
||||||
/**
|
draggableCancel=".grid-drag-cancel"
|
||||||
* The children is using a width of 100% so we need to guarantee that it is wrapped
|
layout={this.buildLayout()}
|
||||||
* in an element that has the calculated size given by the AutoSizer. The AutoSizer
|
onDragStop={this.onDragStop}
|
||||||
* has a width of 0 and will let its content overflow its div.
|
onResize={this.onResize}
|
||||||
*/
|
onResizeStop={this.onResizeStop}
|
||||||
<div style={{ width: width, height: '100%' }} ref={this.onGetWrapperDivRef}>
|
onLayoutChange={this.onLayoutChange}
|
||||||
<ReactGridLayout
|
>
|
||||||
width={width}
|
{this.renderPanels(width, draggable)}
|
||||||
isDraggable={draggable}
|
</ReactGridLayout>
|
||||||
isResizable={isEditable}
|
</div>
|
||||||
containerPadding={[0, 0]}
|
|
||||||
useCSSTransforms={true}
|
|
||||||
margin={[GRID_CELL_VMARGIN, GRID_CELL_VMARGIN]}
|
|
||||||
cols={GRID_COLUMN_COUNT}
|
|
||||||
rowHeight={GRID_CELL_HEIGHT}
|
|
||||||
draggableHandle=".grid-drag-handle"
|
|
||||||
draggableCancel=".grid-drag-cancel"
|
|
||||||
layout={this.buildLayout()}
|
|
||||||
onDragStop={this.onDragStop}
|
|
||||||
onResize={this.onResize}
|
|
||||||
onResizeStop={this.onResizeStop}
|
|
||||||
onLayoutChange={this.onLayoutChange}
|
|
||||||
>
|
|
||||||
{this.renderPanels(width, draggable)}
|
|
||||||
</ReactGridLayout>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</AutoSizer>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user