mirror of
https://github.com/apache/superset.git
synced 2026-07-16 11:46:09 +00:00
Compare commits
7 Commits
chore/i18n
...
dashboard-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d29f2491f8 | ||
|
|
a346867e77 | ||
|
|
17144d1cda | ||
|
|
e9e489b5f2 | ||
|
|
cbe73f0b42 | ||
|
|
a5eb02bb67 | ||
|
|
0f052f3a03 |
@@ -19,6 +19,19 @@
|
||||
|
||||
import { Page, APIResponse } from '@playwright/test';
|
||||
import rison from 'rison';
|
||||
import getEmptyLayout from '../../../src/dashboard/util/getEmptyLayout';
|
||||
import {
|
||||
BACKGROUND_TRANSPARENT,
|
||||
DASHBOARD_GRID_ID,
|
||||
DASHBOARD_ROOT_ID,
|
||||
GRID_COLUMN_COUNT,
|
||||
GRID_DEFAULT_CHART_WIDTH,
|
||||
} from '../../../src/dashboard/util/constants';
|
||||
import {
|
||||
CHART_TYPE,
|
||||
ROW_TYPE,
|
||||
} from '../../../src/dashboard/util/componentTypes';
|
||||
import type { LayoutItem } from '../../../src/dashboard/types';
|
||||
import {
|
||||
apiGet,
|
||||
apiPost,
|
||||
@@ -47,6 +60,78 @@ export interface DashboardCreatePayload {
|
||||
theme_id?: number;
|
||||
}
|
||||
|
||||
export interface DashboardLayoutChart {
|
||||
id: number;
|
||||
sliceName: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
type DashboardPositionItem = Omit<LayoutItem, 'meta'> & {
|
||||
meta?: LayoutItem['meta'];
|
||||
};
|
||||
|
||||
export type DashboardPositionJson = Record<
|
||||
string,
|
||||
DashboardPositionItem | string
|
||||
>;
|
||||
|
||||
const DASHBOARD_ROW_ID = 'ROW-1';
|
||||
const DEFAULT_CHART_HEIGHT = 50;
|
||||
|
||||
/**
|
||||
* Build a v2 dashboard layout with every chart in one row.
|
||||
*/
|
||||
export function buildSingleRowDashboardLayout(
|
||||
charts: readonly DashboardLayoutChart[],
|
||||
): DashboardPositionJson {
|
||||
const totalWidth = charts.reduce(
|
||||
(sum, chart) => sum + (chart.width ?? GRID_DEFAULT_CHART_WIDTH),
|
||||
0,
|
||||
);
|
||||
if (totalWidth > GRID_COLUMN_COUNT) {
|
||||
throw new Error(
|
||||
`Chart widths total ${totalWidth} columns, exceeding the ` +
|
||||
`${GRID_COLUMN_COUNT}-column dashboard grid`,
|
||||
);
|
||||
}
|
||||
|
||||
const emptyLayout = getEmptyLayout();
|
||||
const chartKeys = charts.map(chart => `CHART-${chart.id}`);
|
||||
const positionJson: DashboardPositionJson = {
|
||||
...emptyLayout,
|
||||
[DASHBOARD_GRID_ID]: {
|
||||
...emptyLayout[DASHBOARD_GRID_ID],
|
||||
children: [DASHBOARD_ROW_ID],
|
||||
},
|
||||
[DASHBOARD_ROW_ID]: {
|
||||
type: ROW_TYPE,
|
||||
id: DASHBOARD_ROW_ID,
|
||||
children: chartKeys,
|
||||
parents: [DASHBOARD_ROOT_ID, DASHBOARD_GRID_ID],
|
||||
meta: { background: BACKGROUND_TRANSPARENT },
|
||||
},
|
||||
};
|
||||
|
||||
charts.forEach(chart => {
|
||||
const chartKey = `CHART-${chart.id}`;
|
||||
positionJson[chartKey] = {
|
||||
type: CHART_TYPE,
|
||||
id: chartKey,
|
||||
children: [],
|
||||
parents: [DASHBOARD_ROOT_ID, DASHBOARD_GRID_ID, DASHBOARD_ROW_ID],
|
||||
meta: {
|
||||
chartId: chart.id,
|
||||
width: chart.width ?? GRID_DEFAULT_CHART_WIDTH,
|
||||
height: chart.height ?? DEFAULT_CHART_HEIGHT,
|
||||
sliceName: chart.sliceName,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return positionJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* POST request to create a dashboard
|
||||
* @param page - Playwright page instance (provides authentication context)
|
||||
|
||||
@@ -69,6 +69,15 @@ export class DashboardPage {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a chart grid component by its chart ID.
|
||||
*/
|
||||
getChart(chartId: number): Locator {
|
||||
return this.page.locator(
|
||||
`[data-test="chart-grid-component"][data-test-chart-id="${chartId}"]`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for all charts on the dashboard to finish loading.
|
||||
* Waits until no loading indicators are visible on the page.
|
||||
|
||||
@@ -19,7 +19,10 @@
|
||||
|
||||
import { testWithAssets, expect } from '../../helpers/fixtures';
|
||||
import { apiPost, apiPut } from '../../helpers/api/requests';
|
||||
import { apiPostDashboard } from '../../helpers/api/dashboard';
|
||||
import {
|
||||
apiPostDashboard,
|
||||
buildSingleRowDashboardLayout,
|
||||
} from '../../helpers/api/dashboard';
|
||||
import { getDatasetByName } from '../../helpers/api/dataset';
|
||||
import { DashboardPage } from '../../pages/DashboardPage';
|
||||
import { TIMEOUT } from '../../utils/constants';
|
||||
@@ -61,36 +64,14 @@ testWithAssets(
|
||||
|
||||
// Create dashboard with chart in position_json and a native filter in json_metadata
|
||||
const filterId = `NATIVE_FILTER-${Math.random().toString(36).slice(2, 10)}`;
|
||||
const chartLayoutKey = `CHART-${chartId}`;
|
||||
const positionJson = {
|
||||
DASHBOARD_VERSION_KEY: 'v2',
|
||||
ROOT_ID: { type: 'ROOT', id: 'ROOT_ID', children: ['GRID_ID'] },
|
||||
GRID_ID: {
|
||||
type: 'GRID',
|
||||
id: 'GRID_ID',
|
||||
children: ['ROW-1'],
|
||||
parents: ['ROOT_ID'],
|
||||
const positionJson = buildSingleRowDashboardLayout([
|
||||
{
|
||||
id: chartId,
|
||||
sliceName: 'clear_all_repro',
|
||||
width: 6,
|
||||
height: 50,
|
||||
},
|
||||
'ROW-1': {
|
||||
type: 'ROW',
|
||||
id: 'ROW-1',
|
||||
children: [chartLayoutKey],
|
||||
parents: ['ROOT_ID', 'GRID_ID'],
|
||||
meta: { background: 'BACKGROUND_TRANSPARENT' },
|
||||
},
|
||||
[chartLayoutKey]: {
|
||||
type: 'CHART',
|
||||
id: chartLayoutKey,
|
||||
children: [],
|
||||
parents: ['ROOT_ID', 'GRID_ID', 'ROW-1'],
|
||||
meta: {
|
||||
chartId,
|
||||
width: 6,
|
||||
height: 50,
|
||||
sliceName: 'clear_all_repro',
|
||||
},
|
||||
},
|
||||
};
|
||||
]);
|
||||
|
||||
const jsonMetadata = {
|
||||
native_filter_configuration: [
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* E2E migration of the Cypress "Dashboard load" suite (dashboard/load.test.ts).
|
||||
*
|
||||
* Only the "should load dashboard" case is a genuine end-to-end test: it loads a
|
||||
* multi-chart dashboard and proves every chart renders by issuing real backend
|
||||
* queries. The remaining legacy cases (edit/standalone URL-param rendering,
|
||||
* send-log-data) only assert DOM/URL state with no backend round-trip and belong
|
||||
* in component/RTL coverage instead.
|
||||
*
|
||||
* The dashboard and charts are built via the API and cleaned up by the fixture;
|
||||
* the chart queries use the repository's read-only birth_names example dataset.
|
||||
*
|
||||
* CI green => the dashboard route mounts, every chart POSTs /api/v1/chart/data
|
||||
* successfully, and each chart paints its expected output.
|
||||
* CI red => the dashboard failed to load or a chart never rendered.
|
||||
*/
|
||||
import type { Locator } from '@playwright/test';
|
||||
import { testWithAssets, expect } from '../../helpers/fixtures';
|
||||
import { apiPostChart, apiPutChart } from '../../helpers/api/chart';
|
||||
import {
|
||||
apiPostDashboard,
|
||||
buildSingleRowDashboardLayout,
|
||||
type DashboardLayoutChart,
|
||||
} from '../../helpers/api/dashboard';
|
||||
import { getDatasetByName } from '../../helpers/api/dataset';
|
||||
import { extractIdFromResponse } from '../../helpers/api/assertions';
|
||||
import { TIMEOUT } from '../../utils/constants';
|
||||
import { DashboardPage } from '../../pages/DashboardPage';
|
||||
|
||||
const DATASET_NAME = 'birth_names';
|
||||
|
||||
type ChartOutput = 'big-number' | 'table' | 'echarts';
|
||||
type CreatedChart = DashboardLayoutChart & { output: ChartOutput };
|
||||
|
||||
async function canvasHasPaintedPixels(canvas: Locator): Promise<boolean> {
|
||||
return canvas.evaluate((element: HTMLCanvasElement) => {
|
||||
const context = element.getContext('2d');
|
||||
if (!context || element.width === 0 || element.height === 0) {
|
||||
return false;
|
||||
}
|
||||
const pixels = context.getImageData(0, 0, element.width, element.height);
|
||||
for (let index = 3; index < pixels.data.length; index += 4) {
|
||||
if (pixels.data[index] !== 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
async function expectChartOutput(
|
||||
chart: Locator,
|
||||
output: ChartOutput,
|
||||
): Promise<void> {
|
||||
if (output === 'big-number') {
|
||||
const value = chart.locator(
|
||||
'.superset-legacy-chart-big-number .header-line',
|
||||
);
|
||||
await expect(value).toBeVisible();
|
||||
await expect(value).toHaveText(/\d/);
|
||||
return;
|
||||
}
|
||||
if (output === 'table') {
|
||||
await expect(
|
||||
chart.locator('table tbody tr:not(:has(.dt-no-results))').first(),
|
||||
).toBeVisible();
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = chart.locator('canvas').first();
|
||||
await expect(canvas).toBeVisible();
|
||||
await expect
|
||||
.poll(() => canvasHasPaintedPixels(canvas), {
|
||||
timeout: TIMEOUT.API_RESPONSE * 2,
|
||||
message: 'ECharts canvas should contain painted pixels',
|
||||
})
|
||||
.toBe(true);
|
||||
}
|
||||
|
||||
testWithAssets(
|
||||
'dashboard loads and every chart renders via real queries',
|
||||
async ({ page, testAssets }) => {
|
||||
// Building + loading a multi-chart dashboard chains several slow queries.
|
||||
testWithAssets.setTimeout(TIMEOUT.SLOW_TEST);
|
||||
|
||||
const dataset = await getDatasetByName(page, DATASET_NAME);
|
||||
if (!dataset) {
|
||||
throw new Error(`Dataset ${DATASET_NAME} not found`);
|
||||
}
|
||||
const datasetId = dataset.id;
|
||||
const datasource = `${datasetId}__table`;
|
||||
|
||||
// A spread of viz types that all render cleanly from the birth_names dataset.
|
||||
const chartSpecs: {
|
||||
viz_type: string;
|
||||
output: ChartOutput;
|
||||
params: Record<string, unknown>;
|
||||
}[] = [
|
||||
{
|
||||
viz_type: 'big_number_total',
|
||||
output: 'big-number',
|
||||
params: { datasource, viz_type: 'big_number_total', metric: 'count' },
|
||||
},
|
||||
{
|
||||
viz_type: 'table',
|
||||
output: 'table',
|
||||
params: {
|
||||
datasource,
|
||||
viz_type: 'table',
|
||||
query_mode: 'aggregate',
|
||||
groupby: ['name'],
|
||||
metrics: ['count'],
|
||||
row_limit: 100,
|
||||
},
|
||||
},
|
||||
{
|
||||
viz_type: 'echarts_timeseries_line',
|
||||
output: 'echarts',
|
||||
params: {
|
||||
datasource,
|
||||
viz_type: 'echarts_timeseries_line',
|
||||
x_axis: 'ds',
|
||||
time_grain_sqla: 'P1Y',
|
||||
metrics: ['count'],
|
||||
groupby: [],
|
||||
row_limit: 100,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// Parallel-safe suffix so chart/dashboard names never collide across workers.
|
||||
const uniqueSuffix = `${Date.now()}_${testWithAssets.info().parallelIndex}`;
|
||||
|
||||
// Create each chart via the API.
|
||||
const charts: CreatedChart[] = [];
|
||||
for (const spec of chartSpecs) {
|
||||
const sliceName = `load_smoke_${spec.viz_type}_${uniqueSuffix}`;
|
||||
const resp = await apiPostChart(page, {
|
||||
slice_name: sliceName,
|
||||
viz_type: spec.viz_type,
|
||||
datasource_id: datasetId,
|
||||
datasource_type: 'table',
|
||||
params: JSON.stringify(spec.params),
|
||||
});
|
||||
expect(resp.ok()).toBe(true);
|
||||
const chartId = await extractIdFromResponse(resp);
|
||||
testAssets.trackChart(chartId);
|
||||
charts.push({ id: chartId, sliceName, output: spec.output });
|
||||
}
|
||||
const chartIds = charts.map(chart => chart.id);
|
||||
|
||||
// Lay all charts out in a single row.
|
||||
const positionJson = buildSingleRowDashboardLayout(charts);
|
||||
|
||||
const dashResp = await apiPostDashboard(page, {
|
||||
dashboard_title: `load_smoke_${uniqueSuffix}`,
|
||||
published: true,
|
||||
position_json: JSON.stringify(positionJson),
|
||||
});
|
||||
expect(dashResp.ok()).toBe(true);
|
||||
const dashboardId = await extractIdFromResponse(dashResp);
|
||||
testAssets.trackDashboard(dashboardId);
|
||||
|
||||
// Associate every chart with the dashboard so they actually render.
|
||||
for (const chartId of chartIds) {
|
||||
await apiPutChart(page, chartId, { dashboards: [dashboardId] });
|
||||
}
|
||||
|
||||
// Record the real chart-data round-trips the dashboard makes on load,
|
||||
// keyed by the chart each one queried for. The chart-data POST carries its
|
||||
// slice id in the encoded `form_data={"slice_id":<id>}` query param (see
|
||||
// chartAction.ts), so parsing it lets us prove every chart queried — not
|
||||
// just that some chart did.
|
||||
const chartDataStatusBySliceId = new Map<number, number>();
|
||||
page.on('response', response => {
|
||||
const request = response.request();
|
||||
if (
|
||||
request.method() !== 'POST' ||
|
||||
!response.url().includes('/api/v1/chart/data')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const formData = new URL(response.url()).searchParams.get('form_data');
|
||||
if (!formData) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const sliceId = JSON.parse(formData).slice_id;
|
||||
if (typeof sliceId === 'number') {
|
||||
chartDataStatusBySliceId.set(sliceId, response.status());
|
||||
}
|
||||
} catch {
|
||||
// Not a slice-id form_data payload; ignore.
|
||||
}
|
||||
});
|
||||
|
||||
const dashboard = new DashboardPage(page);
|
||||
await dashboard.gotoById(dashboardId);
|
||||
await dashboard.waitForLoad();
|
||||
|
||||
// Assert the real terminal output for each known visualization rather than
|
||||
// inferring completion from a generic wrapper shared by loading and errors.
|
||||
for (const chart of charts) {
|
||||
await expectChartOutput(dashboard.getChart(chart.id), chart.output);
|
||||
}
|
||||
|
||||
// The render came from real backend queries: every chart issued its own
|
||||
// chart-data POST and each one was accepted. 202 counts as accepted — with
|
||||
// GLOBAL_ASYNC_QUERIES enabled a cold-cache query legitimately returns 202
|
||||
// and delivers its result out of band. The render assertion above is what
|
||||
// proves the data actually arrived, so this only needs to rule out a chart
|
||||
// that never queried or was rejected outright.
|
||||
for (const chartId of chartIds) {
|
||||
const status = chartDataStatusBySliceId.get(chartId);
|
||||
expect(
|
||||
status,
|
||||
`chart ${chartId} should have issued a /api/v1/chart/data POST`,
|
||||
).toBeDefined();
|
||||
expect(
|
||||
[200, 202],
|
||||
`chart ${chartId}'s /api/v1/chart/data response should be 200 or 202, got ${status}`,
|
||||
).toContain(status);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -25,7 +25,10 @@
|
||||
*/
|
||||
import { testWithAssets, expect } from '../../helpers/fixtures';
|
||||
import { apiPost, apiPut } from '../../helpers/api/requests';
|
||||
import { apiPostDashboard } from '../../helpers/api/dashboard';
|
||||
import {
|
||||
apiPostDashboard,
|
||||
buildSingleRowDashboardLayout,
|
||||
} from '../../helpers/api/dashboard';
|
||||
import { getDatasetByName } from '../../helpers/api/dataset';
|
||||
import { DashboardPage } from '../../pages/DashboardPage';
|
||||
|
||||
@@ -73,36 +76,14 @@ testWithAssets(
|
||||
const chartId: number = chart.id ?? chart.result?.id;
|
||||
testAssets.trackChart(chartId);
|
||||
|
||||
const chartLayoutKey = `CHART-${chartId}`;
|
||||
const positionJson = {
|
||||
DASHBOARD_VERSION_KEY: 'v2',
|
||||
ROOT_ID: { type: 'ROOT', id: 'ROOT_ID', children: ['GRID_ID'] },
|
||||
GRID_ID: {
|
||||
type: 'GRID',
|
||||
id: 'GRID_ID',
|
||||
children: ['ROW-1'],
|
||||
parents: ['ROOT_ID'],
|
||||
const positionJson = buildSingleRowDashboardLayout([
|
||||
{
|
||||
id: chartId,
|
||||
sliceName: 'display_control_repro',
|
||||
width: 6,
|
||||
height: 50,
|
||||
},
|
||||
'ROW-1': {
|
||||
type: 'ROW',
|
||||
id: 'ROW-1',
|
||||
children: [chartLayoutKey],
|
||||
parents: ['ROOT_ID', 'GRID_ID'],
|
||||
meta: { background: 'BACKGROUND_TRANSPARENT' },
|
||||
},
|
||||
[chartLayoutKey]: {
|
||||
type: 'CHART',
|
||||
id: chartLayoutKey,
|
||||
children: [],
|
||||
parents: ['ROOT_ID', 'GRID_ID', 'ROW-1'],
|
||||
meta: {
|
||||
chartId,
|
||||
width: 6,
|
||||
height: 50,
|
||||
sliceName: 'display_control_repro',
|
||||
},
|
||||
},
|
||||
};
|
||||
]);
|
||||
|
||||
// 2. json_metadata: one dashboard filter + one Display Control.
|
||||
const filterId = `NATIVE_FILTER-${Math.random().toString(36).slice(2, 10)}`;
|
||||
|
||||
@@ -36,7 +36,10 @@
|
||||
*/
|
||||
import { testWithAssets, expect } from '../../helpers/fixtures';
|
||||
import { apiPostChart, apiPutChart } from '../../helpers/api/chart';
|
||||
import { apiPostDashboard } from '../../helpers/api/dashboard';
|
||||
import {
|
||||
apiPostDashboard,
|
||||
buildSingleRowDashboardLayout,
|
||||
} from '../../helpers/api/dashboard';
|
||||
import { getDatasetByName } from '../../helpers/api/dataset';
|
||||
import { DashboardPage } from '../../pages/DashboardPage';
|
||||
|
||||
@@ -93,36 +96,9 @@ testWithAssets(
|
||||
}
|
||||
testAssets.trackChart(chartId);
|
||||
|
||||
const chartLayoutKey = `CHART-${chartId}`;
|
||||
const positionJson = {
|
||||
DASHBOARD_VERSION_KEY: 'v2',
|
||||
ROOT_ID: { type: 'ROOT', id: 'ROOT_ID', children: ['GRID_ID'] },
|
||||
GRID_ID: {
|
||||
type: 'GRID',
|
||||
id: 'GRID_ID',
|
||||
children: ['ROW-1'],
|
||||
parents: ['ROOT_ID'],
|
||||
},
|
||||
'ROW-1': {
|
||||
type: 'ROW',
|
||||
id: 'ROW-1',
|
||||
children: [chartLayoutKey],
|
||||
parents: ['ROOT_ID', 'GRID_ID'],
|
||||
meta: { background: 'BACKGROUND_TRANSPARENT' },
|
||||
},
|
||||
[chartLayoutKey]: {
|
||||
type: 'CHART',
|
||||
id: chartLayoutKey,
|
||||
children: [],
|
||||
parents: ['ROOT_ID', 'GRID_ID', 'ROW-1'],
|
||||
meta: {
|
||||
chartId,
|
||||
width: 6,
|
||||
height: 60,
|
||||
sliceName,
|
||||
},
|
||||
},
|
||||
};
|
||||
const positionJson = buildSingleRowDashboardLayout([
|
||||
{ id: chartId, sliceName, width: 6, height: 60 },
|
||||
]);
|
||||
const dashResp = await apiPostDashboard(page, {
|
||||
dashboard_title: `gauge_interval_colors_${Date.now()}`,
|
||||
published: true,
|
||||
|
||||
@@ -35,7 +35,10 @@
|
||||
*/
|
||||
import { testWithAssets, expect } from '../../helpers/fixtures';
|
||||
import { apiPost, apiPut } from '../../helpers/api/requests';
|
||||
import { apiPostDashboard } from '../../helpers/api/dashboard';
|
||||
import {
|
||||
apiPostDashboard,
|
||||
buildSingleRowDashboardLayout,
|
||||
} from '../../helpers/api/dashboard';
|
||||
import { DashboardPage } from '../../pages/DashboardPage';
|
||||
|
||||
const DATASET_NAME = 'birth_names';
|
||||
@@ -86,37 +89,15 @@ testWithAssets(
|
||||
const chartId: number = (await chartResp.json()).id;
|
||||
testAssets.trackChart(chartId);
|
||||
|
||||
const chartLayoutKey = `CHART-${chartId}`;
|
||||
const filterId = `NATIVE_FILTER-${Math.random().toString(36).slice(2, 10)}`;
|
||||
const positionJson = {
|
||||
DASHBOARD_VERSION_KEY: 'v2',
|
||||
ROOT_ID: { type: 'ROOT', id: 'ROOT_ID', children: ['GRID_ID'] },
|
||||
GRID_ID: {
|
||||
type: 'GRID',
|
||||
id: 'GRID_ID',
|
||||
children: ['ROW-1'],
|
||||
parents: ['ROOT_ID'],
|
||||
const positionJson = buildSingleRowDashboardLayout([
|
||||
{
|
||||
id: chartId,
|
||||
sliceName: 'mixed_filter_repro',
|
||||
width: 8,
|
||||
height: 60,
|
||||
},
|
||||
'ROW-1': {
|
||||
type: 'ROW',
|
||||
id: 'ROW-1',
|
||||
children: [chartLayoutKey],
|
||||
parents: ['ROOT_ID', 'GRID_ID'],
|
||||
meta: { background: 'BACKGROUND_TRANSPARENT' },
|
||||
},
|
||||
[chartLayoutKey]: {
|
||||
type: 'CHART',
|
||||
id: chartLayoutKey,
|
||||
children: [],
|
||||
parents: ['ROOT_ID', 'GRID_ID', 'ROW-1'],
|
||||
meta: {
|
||||
chartId,
|
||||
width: 8,
|
||||
height: 60,
|
||||
sliceName: 'mixed_filter_repro',
|
||||
},
|
||||
},
|
||||
};
|
||||
]);
|
||||
const jsonMetadata = {
|
||||
native_filter_configuration: [
|
||||
{
|
||||
|
||||
@@ -53,6 +53,7 @@ import { apiPost, apiPut } from '../../helpers/api/requests';
|
||||
import {
|
||||
apiPostDashboard,
|
||||
apiDeleteDashboard,
|
||||
buildSingleRowDashboardLayout,
|
||||
} from '../../helpers/api/dashboard';
|
||||
import { apiDeleteChart } from '../../helpers/api/chart';
|
||||
import { EmbeddedPage } from '../../pages/EmbeddedPage';
|
||||
@@ -190,36 +191,14 @@ test.describe('Embedded Pivot Table collapse state (#33406)', () => {
|
||||
});
|
||||
chartId = (await chartResp.json()).id;
|
||||
|
||||
const chartLayoutKey = `CHART-${chartId}`;
|
||||
const positionJson = {
|
||||
DASHBOARD_VERSION_KEY: 'v2',
|
||||
ROOT_ID: { type: 'ROOT', id: 'ROOT_ID', children: ['GRID_ID'] },
|
||||
GRID_ID: {
|
||||
type: 'GRID',
|
||||
id: 'GRID_ID',
|
||||
children: ['ROW-1'],
|
||||
parents: ['ROOT_ID'],
|
||||
const positionJson = buildSingleRowDashboardLayout([
|
||||
{
|
||||
id: chartId,
|
||||
sliceName: 'pivot_collapse_repro',
|
||||
width: 6,
|
||||
height: 80,
|
||||
},
|
||||
'ROW-1': {
|
||||
type: 'ROW',
|
||||
id: 'ROW-1',
|
||||
children: [chartLayoutKey],
|
||||
parents: ['ROOT_ID', 'GRID_ID'],
|
||||
meta: { background: 'BACKGROUND_TRANSPARENT' },
|
||||
},
|
||||
[chartLayoutKey]: {
|
||||
type: 'CHART',
|
||||
id: chartLayoutKey,
|
||||
children: [],
|
||||
parents: ['ROOT_ID', 'GRID_ID', 'ROW-1'],
|
||||
meta: {
|
||||
chartId,
|
||||
width: 6,
|
||||
height: 80,
|
||||
sliceName: 'pivot_collapse_repro',
|
||||
},
|
||||
},
|
||||
};
|
||||
]);
|
||||
const dashResp = await apiPostDashboard(setupPage, {
|
||||
dashboard_title: `pivot_collapse_repro_${Date.now()}`,
|
||||
published: true,
|
||||
|
||||
Reference in New Issue
Block a user