Table: Update e2e tests to support tableNextGen (#108184)

* Table: Force tableNextGen to be true for Playwright and false for Cypress

* RDG query for body text contains the headers too

* add some simple tests for row height

* dial in the row height test a little more

* more updates

* filters, pagination

* try this on CI

* more updates to the tests

* more tests

* wait for some sort stuff to flush

* replace class selectors for rdg

* target the click to the anchor in the header
pull/108316/head^2
Paul Marbach 1 day ago committed by GitHub
parent 4e6730fee6
commit 7626508842
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      .betterer.results
  2. 2
      apps/iam/pkg/apis/iam_manifest.go
  3. 465
      devenv/dev-dashboards/panel-table/table_kitchen_sink.json
  4. 1
      devenv/jsonnet/dev-dashboards.libsonnet
  5. 8
      e2e-playwright/dashboards-suite/dashboard-browse-nested.spec.ts
  6. 18
      e2e-playwright/dashboards-suite/dashboard-live-streaming.spec.ts
  7. 21
      e2e-playwright/panels-suite/geomap-spatial-operations-transform.spec.ts
  8. 8
      e2e-playwright/panels-suite/panelEdit_base.spec.ts
  9. 331
      e2e-playwright/panels-suite/table-kitchenSink.spec.ts
  10. 29
      e2e-playwright/panels-suite/table-sparkline.spec.ts
  11. 12
      e2e-playwright/plugin-e2e/plugin-e2e-api-tests/as-admin-user/panelEditPage.spec.ts
  12. 10
      e2e-playwright/various-suite/visualization-suggestions.spec.ts
  13. 2
      e2e/dashboards-suite/dashboard-live-streaming.spec.ts
  14. 2
      e2e/old-arch/dashboards-suite/dashboard-live-streaming.spec.ts
  15. 8
      e2e/old-arch/panels-suite/geomap-spatial-operations-transform.spec.ts
  16. 2
      e2e/old-arch/panels-suite/panelEdit_base.spec.ts
  17. 2
      e2e/old-arch/various-suite/visualization-suggestions.spec.ts
  18. 8
      e2e/panels-suite/geomap-spatial-operations-transform.spec.ts
  19. 2
      e2e/panels-suite/panelEdit_base.spec.ts
  20. 2
      e2e/various-suite/visualization-suggestions.spec.ts
  21. 13
      packages/grafana-e2e-selectors/src/selectors/components.ts
  22. 2
      packages/grafana-ui/src/components/Table/TableNG/Filter/Filter.tsx
  23. 6
      packages/grafana-ui/src/components/Table/TableNG/Filter/FilterList.tsx
  24. 7
      packages/grafana-ui/src/components/Table/TableNG/Filter/FilterPopup.tsx
  25. 9
      public/app/plugins/panel/table/table-new/PaginationEditor.tsx
  26. 12
      public/app/plugins/panel/table/table-new/cells/AutoCellOptionsEditor.tsx
  27. 29
      public/app/plugins/panel/table/table-new/cells/ColorBackgroundCellOptionsEditor.tsx
  28. 1
      public/locales/en-US/grafana.json

@ -3895,7 +3895,7 @@ exports[`better eslint`] = {
"public/app/plugins/panel/table/table-new/cells/ColorBackgroundCellOptionsEditor.tsx:5381": [
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"],
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"],
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"]
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"]
],
"public/app/plugins/panel/table/table-new/cells/ImageCellOptionsEditor.tsx:5381": [
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"],

@ -14,8 +14,6 @@ import (
v0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
)
var ()
var appManifestData = app.ManifestData{
AppName: "iam",
Group: "iam.grafana.app",

@ -0,0 +1,465 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 138,
"links": [],
"panels": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto",
"wrapText": false
},
"filterable": true,
"inspect": false,
"wrapHeaderText": false
},
"fieldMinMax": true,
"links": [],
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "A"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"mode": "lcd",
"type": "gauge"
}
},
{
"id": "min",
"value": 0
},
{
"id": "max",
"value": 100
},
{
"id": "custom.width",
"value": 300
}
]
},
{
"matcher": {
"id": "byName",
"options": "Info"
},
"properties": [
{
"id": "links",
"value": [
{
"targetBlank": true,
"title": "Google this term",
"url": "https://google.com/search?q=${__value:percentencode}"
}
]
}
]
},
{
"matcher": {
"id": "byName",
"options": "Min"
},
"properties": []
},
{
"matcher": {
"id": "byName",
"options": "Max"
},
"properties": []
},
{
"matcher": {
"id": "byName",
"options": "State"
},
"properties": []
},
{
"matcher": {
"id": "byName",
"options": "Time"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"type": "auto"
}
},
{
"id": "unit",
"value": "dateTimeFromNow"
}
]
},
{
"matcher": {
"id": "byName",
"options": "Image"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"alt": "${__value}",
"type": "image"
}
}
]
},
{
"matcher": {
"id": "byRegexp",
"options": "/(Time|Min|Max|Info|State|Image)/"
},
"properties": [
{
"id": "custom.width",
"value": 110
}
]
},
{
"matcher": {
"id": "byName",
"options": "Info"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"type": "color-text"
}
},
{
"id": "mappings",
"value": [
{
"options": {
"pattern": "up",
"result": {
"color": "green",
"index": 0
}
},
"type": "regex"
},
{
"options": {
"pattern": "down",
"result": {
"color": "red",
"index": 1
}
},
"type": "regex"
}
]
}
]
},
{
"matcher": {
"id": "byName",
"options": "Min"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"mode": "basic",
"type": "color-background"
}
},
{
"id": "color",
"value": {
"fixedColor": "blue",
"mode": "continuous-YlRd"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Max"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"mode": "gradient",
"type": "color-background"
}
},
{
"id": "color",
"value": {
"mode": "continuous-purples"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "State"
},
"properties": [
{
"id": "displayName",
"value": "State"
},
{
"id": "custom.hidden",
"value": true
}
]
},
{
"matcher": {
"id": "byName",
"options": "Long Text"
},
"properties": [
{
"id": "custom.cellOptions",
"value": {
"type": "auto",
"wrapText": true
}
}
]
}
]
},
"gridPos": {
"h": 18,
"w": 24,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"cellHeight": "sm",
"footer": {
"countRows": false,
"enablePagination": false,
"fields": "",
"reducer": [
"max"
],
"show": true
},
"frameIndex": 0,
"showHeader": true,
"sortBy": []
},
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"refId": "A",
"scenarioId": "random_walk_table"
},
{
"csvContent": "Info,Image,Long Text\ndown,https://placecats.com/millie/300/300,\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse tempus et augue et lacinia. Interdum et malesuada fames ac ante ipsum primis in faucibus. Donec eu pretium tortor. Cras venenatis sapien sed mauris gravida, ut scelerisque est fringilla. Cras lorem diam, facilisis nec malesuada in, vulputate vel enim. Etiam fringilla nisi quis felis blandit tincidunt. Cras id lacus ornare, ullamcorper nisl eget, bibendum odio. Pellentesque imperdiet, leo a imperdiet venenatis, ligula risus venenatis quam, vel euismod magna nisi sit amet leo.\"\nup,https://placecats.com/neo/300/300,\"Sed imperdiet eget diam sit amet fringilla. Curabitur quis lacus blandit, mollis diam non, accumsan tortor. Aliquam ac tellus eget dui facilisis tempor eu id nulla. Maecenas ultrices turpis eu elementum imperdiet. Fusce eget rhoncus mi, et egestas lectus. Mauris facilisis auctor enim sed malesuada. Maecenas placerat ultricies metus vitae viverra. In hac habitasse platea dictumst. Mauris ipsum nisl, dictum eu aliquam eleifend, rutrum id orci. Nullam eget dui et odio eleifend porttitor.\"\nup fast,https://placecats.com/bella/300/300,\"Proin ac libero vulputate ex vulputate pharetra ut vel lacus. Phasellus quis dolor sed leo finibus scelerisque. Ut vel finibus leo, sed viverra ipsum. Suspendisse vitae rutrum arcu. Donec sed tellus vel lectus bibendum vestibulum. Sed eu felis non velit dictum pulvinar eu et leo. Aenean et dignissim arcu. Nam luctus at neque quis efficitur. Fusce tempus at nibh a imperdiet. Nullam malesuada ac magna at facilisis. Duis pretium aliquam eros. Donec pharetra dignissim dolor non bibendum. Ut gravida mi id urna tempus, at ullamcorper felis vulputate. Duis congue augue ex, sed finibus leo ornare ut. Mauris non quam sodales, dignissim lorem eget, tincidunt mauris. Aliquam ut velit auctor, vestibulum metus sed, mollis massa.\"\ndown fast,https://placecats.com/neo_2/300/300,\"Nullam in pulvinar justo. Nunc dictum arcu ac pellentesque bibendum. Sed in erat turpis. Vestibulum eu orci ac ligula lobortis tempus. Fusce consectetur feugiat magna, eu tempor nibh vestibulum ac. Aliquam erat volutpat. Vivamus sit amet viverra enim. Quisque mollis odio nulla, nec vulputate sem placerat in. Etiam dolor sapien, pulvinar in accumsan at, consequat eget nisi. Nunc condimentum neque magna, congue consectetur dui efficitur interdum. Nam lobortis fringilla maximus. Vestibulum eu dui a velit condimentum eleifend consequat nec lectus.\"",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"refId": "B",
"scenarioId": "csv_content"
}
],
"title": "Table - Kitchen Sink",
"transformations": [
{
"id": "joinByField",
"options": {
"byField": "Info",
"mode": "outerTabular"
}
},
{
"id": "organize",
"options": {
"excludeByName": {
"A": false
},
"includeByName": {},
"indexByName": {
"A": 7,
"Image": 5,
"Info": 1,
"Long Text": 6,
"Max A": 3,
"Min A": 2,
"State A": 4,
"Time A": 0
},
"orderBy": [],
"orderByMode": "manual",
"renameByName": {
"A": "Gauge",
"Info": "",
"Max A": "Max",
"Min A": "Min",
"State A": "State",
"Time": "Some really long title that requires wrapping",
"Time A": "Time",
"img_url": "Cat"
}
}
}
],
"type": "table"
},
{
"id": 2,
"type": "table",
"title": "Empty Table Panel",
"gridPos": {
"x": 0,
"y": 0,
"h": 6,
"w": 24
},
"fieldConfig": {
"defaults": {
"custom": {
"align": "auto",
"cellOptions": {
"type": "auto"
},
"inspect": false,
"wrapHeaderText": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"value": null,
"color": "green"
},
{
"value": 80,
"color": "red"
}
]
},
"color": {
"mode": "thresholds"
}
},
"overrides": []
},
"pluginVersion": "12.1.0-pre",
"targets": [
{
"scenarioId": "csv_content",
"refId": "A",
"csvContent": "a,b,c"
}
],
"datasource": {
"uid": "PD8C576611E62080A",
"type": "grafana-testdata-datasource"
},
"options": {
"showHeader": true,
"cellHeight": "sm",
"footer": {
"show": false,
"reducer": [
"sum"
],
"countRows": false,
"fields": ""
}
}
}
],
"preload": false,
"schemaVersion": 41,
"tags": [],
"templating": {
"list": [
{
"baseFilters": [],
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "PD8C576611E62080A"
},
"filters": [],
"name": "Filters",
"type": "adhoc"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Panel Tests - Table - Kitchen Sink",
"uid": "dcb9f5e9-8066-4397-889e-864b99555dbb",
"version": 1
}

@ -93,6 +93,7 @@
"rows-to-fields": (import '../dev-dashboards/transforms/rows-to-fields.json'),
"shared_queries": (import '../dev-dashboards/panel-common/shared_queries.json'),
"slow_queries_and_annotations": (import '../dev-dashboards/scenarios/slow_queries_and_annotations.json'),
"table_kitchen_sink": (import '../dev-dashboards/panel-table/table_kitchen_sink.json'),
"table_pagination": (import '../dev-dashboards/panel-table/table_pagination.json'),
"table_sparkline_cell": (import '../dev-dashboards/panel-table/table_sparkline_cell.json'),
"table_tests": (import '../dev-dashboards/panel-table/table_tests.json'),

@ -7,6 +7,12 @@ const NUM_ROOT_DASHBOARDS = 60;
const NUM_NESTED_FOLDERS = 60;
const NUM_NESTED_DASHBOARDS = 60;
test.use({
featureToggles: {
tableNextGen: true,
},
});
// TODO change this test so it doesn't conflict with the existing dashboard browse test
// probably needs a separate user
test.describe.skip(
@ -101,7 +107,7 @@ test.describe.skip(
await expect(page.getByText('Nested folder 00')).toBeVisible();
// Get the table body container for scrolling
const tableBody = page.getByTestId(selectors.pages.BrowseDashboards.table.body).locator('> div');
const tableBody = page.getByRole('grid');
// Scroll the page and check visibility of next set of items
await tableBody.evaluate((el) => el.scrollTo(0, 2100));

@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e';
import testDashboard from '../dashboards/DashboardLiveTest.json';
test.use({
featureToggles: {
tableNextGen: true,
},
});
test.describe(
'Dashboard Live streaming support',
{
@ -31,18 +37,10 @@ test.describe(
}
});
test('Should receive streaming data', async ({ gotoDashboardPage, selectors }) => {
test('Should receive streaming data', async ({ gotoDashboardPage, selectors, page }) => {
const dashboardPage = await gotoDashboardPage({ uid: dashboardUID });
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Live'))).toBeVisible();
await expect
.poll(
async () =>
await dashboardPage
.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.body)
.getByRole('row')
.count()
)
.toBeGreaterThan(5);
await expect.poll(async () => await page.getByRole('grid').getByRole('row').count()).toBeGreaterThan(5);
});
}
);

@ -2,13 +2,19 @@ import { test, expect } from '@grafana/plugin-e2e';
const DASHBOARD_ID = 'P2jR04WVk';
test.use({
featureToggles: {
tableNextGen: true,
},
});
test.describe(
'Panels test: Geomap spatial operations',
{
tag: ['@panels'],
},
() => {
test('Tests location auto option', async ({ gotoDashboardPage, selectors }) => {
test('Tests location auto option', async ({ gotoDashboardPage, selectors, page }) => {
const dashboardPage = await gotoDashboardPage({
uid: DASHBOARD_ID,
queryParams: new URLSearchParams({ editPanel: '1' }),
@ -40,12 +46,12 @@ test.describe(
await locationInput.press('Enter');
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
const tableHeader = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header);
const tableHeader = page.getByRole('grid').getByRole('row').first();
await expect(tableHeader).toBeVisible();
await expect(tableHeader.getByText('Point')).toBeVisible();
});
test('Tests location coords option', async ({ gotoDashboardPage, dashboardPage, selectors }) => {
test('Tests location coords option', async ({ gotoDashboardPage, dashboardPage, selectors, page }) => {
await gotoDashboardPage({ uid: DASHBOARD_ID, queryParams: new URLSearchParams({ editPanel: '1' }) });
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Transformations')).click();
@ -90,7 +96,7 @@ test.describe(
await longitudeInput.press('Enter');
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
const tableHeader = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header);
const tableHeader = page.getByRole('grid').getByRole('row').first();
await expect(tableHeader).toBeVisible();
await expect(tableHeader.getByText('Point')).toBeVisible();
});
@ -99,6 +105,7 @@ test.describe(
gotoDashboardPage,
dashboardPage,
selectors,
page,
}) => {
await gotoDashboardPage({ uid: DASHBOARD_ID, queryParams: new URLSearchParams({ editPanel: '1' }) });
@ -136,12 +143,12 @@ test.describe(
await geohashFieldInput.press('Enter');
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
const tableHeader = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header);
const tableHeader = page.getByRole('grid').getByRole('row').first();
await expect(tableHeader).toBeVisible();
await expect(tableHeader.getByText('State 1')).toBeVisible();
});
test('Tests location lookup option', async ({ gotoDashboardPage, dashboardPage, selectors }) => {
test('Tests location lookup option', async ({ gotoDashboardPage, dashboardPage, selectors, page }) => {
await gotoDashboardPage({ uid: DASHBOARD_ID, queryParams: new URLSearchParams({ editPanel: '1' }) });
await dashboardPage.getByGrafanaSelector(selectors.components.Tab.title('Transformations')).click();
@ -186,7 +193,7 @@ test.describe(
await gazetteerFieldInput.press('Enter');
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
const tableHeader = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header);
const tableHeader = page.getByRole('grid').getByRole('row').first();
await expect(tableHeader).toBeVisible();
await expect(tableHeader.getByText('Geometry')).toBeVisible();
});

@ -2,6 +2,12 @@ import { test, expect } from '@grafana/plugin-e2e';
const PANEL_UNDER_TEST = 'Lines 500 data points';
test.use({
featureToggles: {
tableNextGen: true,
},
});
test.describe(
'Panels test: Panel edit base',
{
@ -65,7 +71,7 @@ test.describe(
// Check that table view works
await expect(dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.loadingBar(''))).toHaveCount(0);
await dashboardPage.getByGrafanaSelector(selectors.components.PanelEditor.toggleTableView).click({ force: true });
const tableHeader = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header);
const tableHeader = page.getByRole('grid').getByRole('row').first();
await expect(tableHeader).toBeVisible();
await expect(tableHeader.getByText('A-series')).toBeVisible();

@ -0,0 +1,331 @@
import { Page, Locator } from '@playwright/test';
import { test, expect } from '@grafana/plugin-e2e';
test.use({
viewport: { width: 1600, height: 1080 },
featureToggles: {
tableNextGen: true,
},
});
// helper utils
const getCell = async (loc: Page | Locator, rowIdx: number, colIdx: number) =>
loc
.getByRole('row')
.nth(rowIdx)
.getByRole(rowIdx === 0 ? 'columnheader' : 'gridcell')
.nth(colIdx);
const getCellHeight = async (loc: Page | Locator, rowIdx: number, colIdx: number) => {
const cell = await getCell(loc, rowIdx, colIdx);
return (await cell.boundingBox())?.height ?? 0;
};
test.describe(
'Panels test: Table - Kitchen Sink',
{
tag: ['@panels'],
},
() => {
test('Tests word wrap, hover overflow, and cell inspect', async ({ gotoDashboardPage, selectors, page }) => {
const dashboardPage = await gotoDashboardPage({
uid: 'dcb9f5e9-8066-4397-889e-864b99555dbb',
queryParams: new URLSearchParams({ editPanel: '1' }),
});
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink'))
).toBeVisible();
// text wrapping is enabled by default on this panel.
await expect(getCellHeight(page, 1, 5)).resolves.toBeGreaterThan(100);
// toggle the lorem ipsum column's wrap text toggle and confirm that the height shrinks.
await dashboardPage
.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text'))
.last()
.click();
await expect(getCellHeight(page, 1, 5)).resolves.toBeLessThan(100);
// test that hover overflow works.
const loremIpsumCell = await getCell(page, 1, 5);
await loremIpsumCell.hover();
await expect(getCellHeight(page, 1, 5)).resolves.toBeGreaterThan(100);
await (await getCell(page, 1, 6)).hover();
await expect(getCellHeight(page, 1, 5)).resolves.toBeLessThan(100);
// enable cell inspect, confirm that hover no longer triggers.
await dashboardPage
.getByGrafanaSelector(
selectors.components.PanelEditor.OptionsPane.fieldLabel('Cell options Cell value inspect')
)
.first()
.locator('label[for="custom.inspect"]')
.click();
await loremIpsumCell.hover();
await expect(getCellHeight(page, 1, 5)).resolves.toBeLessThan(100);
// click cell inspect, check that cell inspection pops open in the side as we'd expect.
await loremIpsumCell.getByLabel('Inspect value').click();
const loremIpsumText = await loremIpsumCell.textContent();
expect(loremIpsumText).toBeDefined();
await expect(page.getByRole('dialog').getByText(loremIpsumText!)).toBeVisible();
});
test('Tests visibility and display name via overrides', async ({ gotoDashboardPage, selectors, page }) => {
const dashboardPage = await gotoDashboardPage({
uid: 'dcb9f5e9-8066-4397-889e-864b99555dbb',
queryParams: new URLSearchParams({ editPanel: '1' }),
});
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink'))
).toBeVisible();
// confirm that "State" column is hidden by default.
expect(page.getByRole('row').nth(0)).not.toContainText('State');
// toggle the "State" column visibility and test that it appears before re-hiding it.
// FIXME this selector is utterly godawful, but there's no way to give testIds or aria-labels or anything to
// the panel editor builder. we should fix that to make e2e's easier to write for our team.
const hideStateColumnSwitch = page.locator('[id="Override 12"]').locator('label').last();
await hideStateColumnSwitch.click();
expect(page.getByRole('row').nth(0)).toContainText('State');
// now change the display name of the "State" column.
// FIXME it would be good to have a better selector here too.
const displayNameInput = page.locator('[id="Override 12"]').locator('input[value="State"]').last();
await displayNameInput.fill('State (renamed)');
await displayNameInput.press('Enter');
expect(page.getByRole('row').nth(0)).toContainText('State (renamed)');
});
// we test niche cases for sorting, filtering, pagination, etc. in a unit tests already.
// we mainly want to test the happiest paths for these in e2es as well to check for integration
// issues, but the unit tests can confirm that the internal logic works as expected much more quickly and thoroughly.
// hashtag testing pyramid.
test('Tests sorting by column', async ({ gotoDashboardPage, selectors, page }) => {
const dashboardPage = await gotoDashboardPage({
uid: 'dcb9f5e9-8066-4397-889e-864b99555dbb',
queryParams: new URLSearchParams({ editPanel: '1' }),
});
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink'))
).toBeVisible();
// click the "State" column header to sort it.
const stateColumnHeader = await getCell(page, 0, 1);
await stateColumnHeader.getByText('Info').click();
await expect(stateColumnHeader).toHaveAttribute('aria-sort', 'ascending');
expect(getCell(page, 1, 1)).resolves.toContainText('down'); // down or down fast
await stateColumnHeader.getByText('Info').click();
await expect(stateColumnHeader).toHaveAttribute('aria-sort', 'descending');
expect(getCell(page, 1, 1)).resolves.toContainText('up'); // up or up fast
await stateColumnHeader.getByText('Info').click();
await expect(stateColumnHeader).not.toHaveAttribute('aria-sort');
});
test('Tests filtering within a column', async ({ gotoDashboardPage, selectors, page }) => {
const dashboardPage = await gotoDashboardPage({
uid: 'dcb9f5e9-8066-4397-889e-864b99555dbb',
queryParams: new URLSearchParams({ editPanel: '1' }),
});
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink'))
).toBeVisible();
const stateColumnHeader = page.getByRole('columnheader').filter({ hasText: 'Info' });
// get the first value in the "State" column, filter it out, then check that it went away.
const firstStateValue = (await (await getCell(page, 1, 1)).textContent())!;
await stateColumnHeader
.getByTestId(selectors.components.Panels.Visualization.TableNG.Filters.HeaderButton)
.click();
const filterContainer = dashboardPage.getByGrafanaSelector(
selectors.components.Panels.Visualization.TableNG.Filters.Container
);
await expect(filterContainer).toBeVisible();
// select all, then click the first value to unselect it, filtering it out.
await filterContainer.getByTestId(selectors.components.Panels.Visualization.TableNG.Filters.SelectAll).click();
await filterContainer.getByTitle(firstStateValue, { exact: true }).locator('label').click();
await filterContainer.getByRole('button', { name: 'Ok' }).click();
// make sure the filter container closed when we clicked "Ok".
await expect(filterContainer).not.toBeVisible();
// did it actually filter out our value?
await expect(getCell(page, 1, 1)).resolves.not.toHaveText(firstStateValue);
});
test('Tests pagination, row height adjustment', async ({ gotoDashboardPage, selectors, page }) => {
const rowRe = /([\d]+) - ([\d]+) of ([\d]+) rows/;
const getRowStatus = async (page: Page | Locator) => {
const text = (await page.getByText(rowRe).textContent()) ?? '';
const match = text.match(rowRe);
return {
start: parseInt(match?.[1] ?? '0', 10),
end: parseInt(match?.[2] ?? '0', 10),
total: parseInt(match?.[3] ?? '0', 10),
};
};
const dashboardPage = await gotoDashboardPage({
uid: 'dcb9f5e9-8066-4397-889e-864b99555dbb',
queryParams: new URLSearchParams({ editPanel: '1' }),
});
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink'))
).toBeVisible();
await page
.getByLabel(selectors.components.PanelEditor.OptionsPane.fieldLabel(`Enable pagination`), { exact: true })
.click();
// because of text wrapping, we're guaranteed to only be showing a single row when we enable pagination.
await expect(page.getByText(/([\d]+) - ([\d]+) of ([\d]+) rows/)).toBeVisible();
// disable text wrap and see the number of rows.
await dashboardPage
.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text'))
.last()
.click();
// any number of rows that is not "1" is allowed here, we don't want to police the exact number of rows that
// are rendered since there are tons of factors which could effect this. we do want to grab this number for comparison
// in a second, though.
const smallRowStatus = await getRowStatus(page);
expect(smallRowStatus.end).toBeGreaterThan(1);
expect(page.getByRole('grid').getByRole('row')).toHaveCount(smallRowStatus.end + 1);
// change cell height to Large
await dashboardPage
.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Table Cell height'))
.locator('input')
.last()
.click();
const largeRowStatus = await getRowStatus(page);
expect(largeRowStatus.end).toBeLessThan(smallRowStatus.end);
expect(page.getByRole('grid').getByRole('row')).toHaveCount(largeRowStatus.end + 1);
// click a page over with the directional nav
await page.getByLabel('next page').click();
const nextPageStatus = await getRowStatus(page);
expect(nextPageStatus.start).toBe(largeRowStatus.end + 1);
expect(nextPageStatus.end).toBe(largeRowStatus.end * 2);
expect(nextPageStatus.total).toBe(largeRowStatus.total);
// click a page number
await page
.getByTestId('data-testid panel content')
.getByRole('navigation')
.getByText('4', { exact: true })
.click();
const fourthPageStatus = await getRowStatus(page);
expect(fourthPageStatus.start).toBe(largeRowStatus.end * 3 + 1);
expect(fourthPageStatus.end).toBe(largeRowStatus.end * 4);
expect(fourthPageStatus.total).toBe(largeRowStatus.total);
});
test('Tests DataLinks (single and multi) and actions', async ({ gotoDashboardPage, selectors, page }) => {
const addDataLink = async (title: string, url: string) => {
await dashboardPage
.getByGrafanaSelector(
selectors.components.PanelEditor.OptionsPane.fieldLabel('Data links and actions Data links')
)
.locator('button')
.filter({ hasText: 'Add link' })
.click();
// DataLinks dialog has popped open - fill it in and add a global datalink.
await expect(page.getByRole('dialog')).toBeVisible();
await page.getByRole('dialog').locator('#link-title').fill(title);
await page.getByRole('dialog').locator('#data-link-input [contenteditable="true"]').focus();
await page.getByRole('dialog').locator('#data-link-input [contenteditable="true"]').fill(url);
await page.getByRole('dialog').locator('#data-link-input [contenteditable="true"]').blur();
await page.getByRole('dialog').getByRole('button', { name: 'Save' }).click();
await expect(page.getByRole('dialog')).not.toBeVisible();
};
const dashboardPage = await gotoDashboardPage({
uid: 'dcb9f5e9-8066-4397-889e-864b99555dbb',
queryParams: new URLSearchParams({ editPanel: '1' }),
});
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink'))
).toBeVisible();
// disable text wrapping for this test to make it easier to click the links, the long lorem ipsum
// can push the links off the screen.
await dashboardPage
.getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text'))
.last()
.click();
// Info column has a single DataLink by default.
const infoCell = await getCell(page, 1, 1);
await expect(infoCell.locator('a')).toBeVisible();
expect(infoCell.locator('a')).toHaveAttribute('href');
expect(infoCell.locator('a')).not.toHaveAttribute('aria-haspopup');
// now, add a DataLink to the whole table
await addDataLink('Test link', 'https://grafana.com');
// add a DataLink to the whole table, all cells will now have a single link.
const colCount = await page.getByRole('row').nth(1).getByRole('gridcell').count();
for (let colIdx = 0; colIdx < colCount; colIdx++) {
const cell = await getCell(page, 1, colIdx);
await expect(cell.locator('a')).toBeVisible();
expect(cell.locator('a')).toHaveAttribute('href');
expect(cell.locator('a')).not.toHaveAttribute('aria-haspopup', 'menu');
}
const headerContainer = dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.headerContainer);
// add another data link. now we'll check that the multi-link popups work.
await addDataLink('Another test link', 'https://grafana.com/foo');
// loop thru the columns, click the links, observe that the tooltip appears, and close the tooltip.
for (let colIdx = 0; colIdx < colCount; colIdx++) {
const cell = await getCell(page, 1, colIdx);
if (colIdx === 1) {
// the Info column should still have its single link.
expect(cell.locator('a')).not.toHaveAttribute('aria-haspopup', 'menu');
continue;
}
await cell.locator('a').click({ force: true });
await expect(page.getByTestId(selectors.components.DataLinksActionsTooltip.tooltipWrapper)).toBeVisible();
await headerContainer.click(); // convenient just to click the header to close the tooltip.
await expect(page.getByTestId(selectors.components.DataLinksActionsTooltip.tooltipWrapper)).not.toBeVisible();
}
// add an Action to the whole table and check that the action button is added to the tooltip.
// TODO -- saving for another day.
});
test('Empty Table panel', async ({ gotoDashboardPage, selectors }) => {
const dashboardPage = await gotoDashboardPage({
uid: 'dcb9f5e9-8066-4397-889e-864b99555dbb',
queryParams: new URLSearchParams({ editPanel: '2' }),
});
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.PanelDataErrorMessage)
).toBeVisible();
await expect(
dashboardPage.getByGrafanaSelector(selectors.components.Panels.Panel.title('Table - Kitchen Sink'))
).not.toBeVisible();
});
}
);

@ -0,0 +1,29 @@
import { test, expect } from '@grafana/plugin-e2e';
test.use({
viewport: { width: 1280, height: 1080 },
featureToggles: {
tableNextGen: true,
},
});
test.describe(
'Panels test: Table - Sparkline',
{
tag: ['@panels'],
},
() => {
test('Tests sparkline tables are successfully rendered', async ({ gotoDashboardPage, selectors, page }) => {
await gotoDashboardPage({
uid: 'd6373b49-1957-4f00-9218-ee2120d3ecd9',
queryParams: new URLSearchParams({ editPanel: '2' }),
});
await expect(page.getByRole('grid')).toBeVisible();
const uplotCount = await page.locator('.uplot').count();
const rowCount = await page.getByRole('row').count();
expect(uplotCount).toBe(rowCount - 1);
});
}
);

@ -11,6 +11,12 @@ const STANDARD_OTIONS_CATEGORY = 'Standard options';
const DISPLAY_NAME_LABEL = 'Display name';
const REACT_TABLE_DASHBOARD = { uid: 'U_bZIMRMk' };
test.use({
featureToggles: {
tableNextGen: true,
},
});
test.describe(
'plugin-e2e-api-tests admin',
{
@ -53,7 +59,7 @@ test.describe(
).toHaveText(scenarios.map((s) => s.name));
});
test('mocked query data response', async ({ panelEditPage, selectors }) => {
test('mocked query data response', async ({ panelEditPage, page }) => {
await panelEditPage.mockQueryDataResponse(successfulDataQuery, 200);
await panelEditPage.datasource.set('gdev-testdata');
await panelEditPage.setVisualization(TABLE_VIZ_NAME);
@ -63,9 +69,9 @@ test.describe(
formatExpectError('Did not expect panel error to be displayed after query execution')
).toBeHidden();
await expect(
panelEditPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.body),
page.getByRole('grid'),
formatExpectError('Expected certain select options to be displayed after clicking on the select input')
).toHaveText('val1val2val3val4');
).toHaveText(/val1val2val3val4/);
});
});

@ -1,5 +1,11 @@
import { test, expect } from '@grafana/plugin-e2e';
test.use({
featureToggles: {
tableNextGen: true,
},
});
test.describe(
'Visualization suggestions',
{
@ -39,9 +45,7 @@ test.describe(
await panelEditPage.getByGrafanaSelector(selectors.components.VisualizationPreview.card('Table')).click();
// Verify table header is visible
await expect(
panelEditPage.getByGrafanaSelector(selectors.components.Panels.Visualization.Table.header)
).toBeVisible();
await expect(page.getByRole('grid').getByRole('row').first()).toBeVisible();
});
}
);

@ -9,7 +9,7 @@ describe.skip('Dashboard Live streaming support', () => {
});
it('Should receive streaming data', () => {
e2e.flows.openDashboard({ uid: 'live-e2e-test' });
e2e.flows.openDashboard({ uid: 'live-e2e-test', queryParams: { '__feature.tableNextGen': false } });
cy.wait(1000);
e2e.components.Panels.Panel.title('Live').should('exist');
e2e.components.Panels.Visualization.Table.body().find('[role="row"]').should('have.length.at.least', 5);

@ -8,7 +8,7 @@ describe('Dashboard Live streaming support', () => {
});
it('Should receive streaming data', () => {
e2e.flows.openDashboard({ uid: 'live-e2e-test' });
e2e.flows.openDashboard({ uid: 'live-e2e-test', queryParams: { '__feature.tableNextGen': false } });
cy.wait(1000);
e2e.components.Panels.Panel.title('Live').should('exist');
e2e.components.Panels.Visualization.Table.body().find('[role="row"]').should('have.length.at.least', 5);

@ -8,7 +8,7 @@ describe.skip('Geomap spatial operations', () => {
});
it.skip('Tests location auto option', () => {
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } });
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } });
e2e.components.Tab.title('Transform data').should('be.visible').click();
e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click();
@ -26,7 +26,7 @@ describe.skip('Geomap spatial operations', () => {
});
it('Tests location coords option', () => {
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } });
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } });
e2e.components.Tab.title('Transform data').should('be.visible').click();
e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click();
@ -50,7 +50,7 @@ describe.skip('Geomap spatial operations', () => {
});
it('Tests geoshash field column appears in table view', () => {
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } });
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } });
e2e.components.Tab.title('Transform data').should('be.visible').click();
e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click();
@ -73,7 +73,7 @@ describe.skip('Geomap spatial operations', () => {
});
it('Tests location lookup option', () => {
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } });
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } });
e2e.components.Tab.title('Transform data').should('be.visible').click();
e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click();

@ -11,7 +11,7 @@ describe('Panel edit tests', () => {
cy.intercept({
pathname: '/api/ds/query',
}).as('query');
e2e.flows.openDashboard({ uid: 'TkZXxlNG3' });
e2e.flows.openDashboard({ uid: 'TkZXxlNG3', queryParams: { '__feature.tableNextGen': false } });
cy.wait('@query');
e2e.flows.openPanelMenuItem(e2e.flows.PanelMenuItems.Edit, PANEL_UNDER_TEST);

@ -6,7 +6,7 @@ describe('Visualization suggestions', () => {
});
it('Should be shown and clickable', () => {
e2e.flows.openDashboard({ uid: 'aBXrJ0R7z', queryParams: { editPanel: 9 } });
e2e.flows.openDashboard({ uid: 'aBXrJ0R7z', queryParams: { '__feature.tableNextGen': false, editPanel: 9 } });
// Try visualization suggestions
e2e.components.PanelEditor.toggleVizPicker().click();

@ -8,7 +8,7 @@ describe.skip('Geomap spatial operations', () => {
});
it('Tests location auto option', () => {
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } });
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } });
e2e.components.Tab.title('Transformations').should('be.visible').click();
e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click();
@ -26,7 +26,7 @@ describe.skip('Geomap spatial operations', () => {
});
it('Tests location coords option', () => {
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } });
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } });
e2e.components.Tab.title('Transformations').should('be.visible').click();
e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click();
@ -50,7 +50,7 @@ describe.skip('Geomap spatial operations', () => {
});
it('Tests geoshash field column appears in table view', () => {
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } });
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } });
e2e.components.Tab.title('Transformations').should('be.visible').click();
e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click();
@ -73,7 +73,7 @@ describe.skip('Geomap spatial operations', () => {
});
it('Tests location lookup option', () => {
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { editPanel: 1 } });
e2e.flows.openDashboard({ uid: DASHBOARD_ID, queryParams: { '__feature.tableNextGen': false, editPanel: 1 } });
e2e.components.Tab.title('Transformations').should('be.visible').click();
e2e.components.Transforms.addTransformationButton().scrollIntoView().should('be.visible').click();

@ -13,7 +13,7 @@ describe('Panel edit tests', () => {
cy.intercept({
pathname: '/api/ds/query',
}).as('query');
e2e.flows.openDashboard({ uid: 'TkZXxlNG3' });
e2e.flows.openDashboard({ uid: 'TkZXxlNG3', queryParams: { '__feature.tableNextGen': false } });
cy.wait('@query');
e2e.components.Panels.Panel.title('Lines 500 data points')

@ -6,7 +6,7 @@ describe('Visualization suggestions', () => {
});
it('Should be shown and clickable', () => {
e2e.flows.openDashboard({ uid: 'aBXrJ0R7z', queryParams: { editPanel: 9 } });
e2e.flows.openDashboard({ uid: 'aBXrJ0R7z', queryParams: { '__feature.tableNextGen': false, editPanel: 9 } });
// Try visualization suggestions
e2e.components.PanelEditor.toggleVizPicker().click();

@ -481,6 +481,19 @@ export const versionedComponents = {
'10.2.0': 'data-testid table body',
},
},
TableNG: {
Filters: {
HeaderButton: {
'12.1.0': 'data-testid tableng header filter',
},
Container: {
'12.1.0': 'data-testid tablenf filter container',
},
SelectAll: {
'12.1.0': 'data-testid tableng filter select-all',
},
},
},
},
},
VizLegend: {

@ -2,6 +2,7 @@ import { css, cx } from '@emotion/css';
import { useRef, useState } from 'react';
import { Field, GrafanaTheme2, SelectableValue } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { useStyles2 } from '../../../../themes/ThemeContext';
import { Icon } from '../../../Icon/Icon';
@ -61,6 +62,7 @@ export const Filter = ({
className={styles.headerFilter}
ref={ref}
type="button"
data-testid={selectors.components.Panels.Visualization.TableNG.Filters.HeaderButton}
onClick={(ev) => {
setPopoverVisible(true);
ev.stopPropagation();

@ -4,6 +4,7 @@ import * as React from 'react';
import { FixedSizeList as List, ListChildComponentProps } from 'react-window';
import { GrafanaTheme2, formattedValueToString, getValueFormat, SelectableValue } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Trans } from '@grafana/i18n';
import { useStyles2, useTheme2 } from '../../../../themes/ThemeContext';
@ -160,7 +161,10 @@ export const FilterList = ({ options, values, caseSensitive, onChange, searchFil
>
{ItemRenderer}
</List>
<div className={styles.filterListRow}>
<div
className={styles.filterListRow}
data-testid={selectors.components.Panels.Visualization.TableNG.Filters.SelectAll}
>
<Checkbox
value={selectCheckValue}
indeterminate={selectCheckIndeterminate}

@ -2,6 +2,7 @@ import { css } from '@emotion/css';
import React, { useCallback, useMemo, useState } from 'react';
import { Field, GrafanaTheme2, SelectableValue } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { t, Trans } from '@grafana/i18n';
import { useStyles2, useTheme2 } from '../../../../themes/ThemeContext';
@ -108,7 +109,11 @@ export const FilterPopup = ({
<ClickOutsideWrapper onClick={onCancel} useCapture={true}>
{/* This is just blocking click events from bubbeling and should not have a keyboard interaction. */}
{/* eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events */}
<div className={styles.filterContainer} onClick={stopPropagation}>
<div
className={styles.filterContainer}
onClick={stopPropagation}
data-testid={selectors.components.Panels.Visualization.TableNG.Filters.Container}
>
<Stack direction="column">
<Stack alignItems="center">
{field && <Label className={styles.label}>{getDisplayName(field)}</Label>}

@ -1,6 +1,7 @@
import * as React from 'react';
import { StandardEditorProps } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Switch } from '@grafana/ui';
export function PaginationEditor({ onChange, value, context }: StandardEditorProps<boolean>) {
@ -11,5 +12,11 @@ export function PaginationEditor({ onChange, value, context }: StandardEditorPro
onChange(event?.currentTarget.checked);
};
return <Switch value={Boolean(value)} onChange={changeValue} />;
return (
<Switch
label={selectors.components.PanelEditor.OptionsPane.fieldLabel(`Enable pagination`)}
value={Boolean(value)}
onChange={changeValue}
/>
);
}

@ -1,5 +1,6 @@
import { selectors } from '@grafana/e2e-selectors';
import { t } from '@grafana/i18n';
import { TableAutoCellOptions, TableColorTextCellOptions } from '@grafana/schema';
import { TableAutoCellOptions, TableColoredBackgroundCellOptions, TableColorTextCellOptions } from '@grafana/schema';
import { Field, Switch } from '@grafana/ui';
import { TableCellEditorProps } from '../TableCellOptionEditor';
@ -7,9 +8,8 @@ import { TableCellEditorProps } from '../TableCellOptionEditor';
export const AutoCellOptionsEditor = ({
cellOptions,
onChange,
}: TableCellEditorProps<TableAutoCellOptions | TableColorTextCellOptions>) => {
}: TableCellEditorProps<TableAutoCellOptions | TableColorTextCellOptions | TableColoredBackgroundCellOptions>) => {
// Handle row coloring changes
const onWrapTextChange = () => {
cellOptions.wrapText = !cellOptions.wrapText;
onChange(cellOptions);
@ -17,7 +17,11 @@ export const AutoCellOptionsEditor = ({
return (
<Field label={t('table.auto-cell-options-editor.label-wrap-text', 'Wrap text')}>
<Switch value={cellOptions.wrapText} onChange={onWrapTextChange} />
<Switch
label={selectors.components.PanelEditor.OptionsPane.fieldLabel(`Wrap text`)}
value={cellOptions.wrapText}
onChange={onWrapTextChange}
/>
</Field>
);
};

@ -1,10 +1,13 @@
import { SelectableValue } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { t } from '@grafana/i18n';
import { TableCellBackgroundDisplayMode, TableColoredBackgroundCellOptions } from '@grafana/schema';
import { Field, RadioButtonGroup, Switch } from '@grafana/ui';
import { TableCellEditorProps } from '../TableCellOptionEditor';
import { AutoCellOptionsEditor } from './AutoCellOptionsEditor';
const colorBackgroundOpts: Array<SelectableValue<TableCellBackgroundDisplayMode>> = [
{ value: TableCellBackgroundDisplayMode.Basic, label: 'Basic' },
{ value: TableCellBackgroundDisplayMode.Gradient, label: 'Gradient' },
@ -14,7 +17,6 @@ export const ColorBackgroundCellOptionsEditor = ({
onChange,
}: TableCellEditorProps<TableColoredBackgroundCellOptions>) => {
// Set the display mode on change
const onCellOptionsChange = (v: TableCellBackgroundDisplayMode) => {
cellOptions.mode = v;
onChange(cellOptions);
@ -25,23 +27,19 @@ export const ColorBackgroundCellOptionsEditor = ({
onChange(cellOptions);
};
// Handle row coloring changes
const onWrapTextChange = () => {
cellOptions.wrapText = !cellOptions.wrapText;
onChange(cellOptions);
};
return (
<>
<Field
label={t('table.color-background-cell-options-editor.label-background-display-mode', 'Background display mode')}
>
<RadioButtonGroup
aria-label={selectors.components.PanelEditor.OptionsPane.fieldLabel(`Background display mode`)}
value={cellOptions?.mode ?? TableCellBackgroundDisplayMode.Gradient}
onChange={onCellOptionsChange}
options={colorBackgroundOpts}
/>
</Field>
<Field
label={t('table.color-background-cell-options-editor.label-apply-to-entire-row', 'Apply to entire row')}
description={t(
@ -49,11 +47,20 @@ export const ColorBackgroundCellOptionsEditor = ({
'If selected the entire row will be colored as this cell would be.'
)}
>
<Switch value={cellOptions.applyToRow} onChange={onColorRowChange} />
</Field>
<Field label={t('table.color-background-cell-options-editor.label-wrap-text', 'Wrap text')}>
<Switch value={cellOptions.wrapText} onChange={onWrapTextChange} />
<Switch
label={selectors.components.PanelEditor.OptionsPane.fieldLabel(`Apply to entire row`)}
value={cellOptions.applyToRow}
onChange={onColorRowChange}
/>
</Field>
<AutoCellOptionsEditor
cellOptions={cellOptions}
onChange={(updatedCellOptions) => {
cellOptions.wrapText = updatedCellOptions.wrapText;
onChange(cellOptions);
}}
/>
</>
);
};

@ -11953,7 +11953,6 @@
},
"label-apply-to-entire-row": "Apply to entire row",
"label-background-display-mode": "Background display mode",
"label-wrap-text": "Wrap text",
"wrap-text": "Wrap text"
},
"column-alignment-options": {

Loading…
Cancel
Save