Table: Migrate to field-level wrapText toggle, use standard hideFrom.viz for hidden fields (#109297)

* Added wrap text to table options and removed from multiple cell options

* Removed wrap text from cell options shared file

* Edited

* Edited

* Table: Migrate to field-level wrapText toggle

* migrate hidden -> hideFrom.viz as well

* more cleanup from rebase

* restore options that got killed in the rebase

* when wrap text is enabled for the whole table, exclude columns that do not make sense

* fix TableNG unit tests

* fix i18n

* unit tests for the migrations

* sort out e2e issue

* fix migration unit test

* fix backend migration test as well

* add a dashboard for the v12.2 table migrations

* update dev-dashboards.lisonnet

* make gen-jsonnet

* one of the panel versions didnt get updated

---------

Co-authored-by: Isabel Matwawana <isabel.matwawana@grafana.com>
This commit is contained in:
Paul Marbach
2025-08-27 16:37:02 -04:00
committed by GitHub
co-authored by Isabel Matwawana
parent 699bffc9e4
commit 76b1e5e389
26 changed files with 1350 additions and 173 deletions
-3
View File
@@ -3988,9 +3988,6 @@ exports[`better eslint`] = {
"public/app/plugins/panel/table/cells/SparklineCellOptionsEditor.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"]
],
"public/app/plugins/panel/table/cells/TextWrapOptionsEditor.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"]
],
"public/app/plugins/panel/table/migrations.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
@@ -165,7 +165,7 @@ import (
// {
// "matcher": { "id": "byName", "options": "Hidden" },
// "properties": [
// { "id": "custom.hidden", "value": true }
// { "id": "custom.hideFrom.viz", "value": true }
// ]
// }
// ]
@@ -436,7 +436,7 @@ func migrateTableStyleToOverride(style map[string]interface{}) map[string]interf
// Handle hidden type
if styleType, ok := style["type"].(string); ok && styleType == "hidden" {
properties = append(properties, map[string]interface{}{
"id": "custom.hidden",
"id": "custom.hideFrom.viz",
"value": true,
})
}
@@ -216,7 +216,7 @@ func TestV24(t *testing.T) {
"options": "Hidden",
},
"properties": []interface{}{
map[string]interface{}{"id": "custom.hidden", "value": true},
map[string]interface{}{"id": "custom.hideFrom.viz", "value": true},
},
},
},
@@ -192,7 +192,7 @@
},
"properties": [
{
"id": "custom.hidden",
"id": "custom.hideFrom.viz",
"value": true
}
]
@@ -452,7 +452,7 @@
}
]
},
"pluginVersion": "12.2.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
@@ -602,7 +602,7 @@
}
]
},
"pluginVersion": "12.2.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"datasource": {
@@ -821,7 +821,7 @@
"showHeader": true,
"sortBy": []
},
"pluginVersion": "12.2.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"csvContent": "auto,link,links,bg,bg-link,bg-links,color,color-link,color-links,bg-row,bg-row-link,bg-row-links,color-row,color-row-link,color-row-links,links-cellType\na,a,a,a,a,a,a,a,a,1,1,1,1,1,1,1\nb,b,b,b,b,b,b,b,b,2,2,2,2,2,2,2\nc,c,c,c,c,c,c,c,c,3,3,3,3,3,3,3\nd,d,d,d,d,d,d,d,d,4,4,4,4,4,4,4",
@@ -984,7 +984,7 @@
"showHeader": true,
"sortBy": []
},
"pluginVersion": "12.2.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"csvContent": "use-case,normal,color-text,color-bg,highlight\n\"color bg and apply to row\",1,0,1,1\n\"color text and apply to row\",1,1,0,1\n\"color text + color bg + apply to row\",1,1,1,1\n\"only apply to row\",1,0,0,1\n\"only color bg\",1,0,1,0\n\"only color text\",1,1,0,0\n\"color text + color bg\",1,1,1,0\n\"no colorization\",1,0,0,0",
@@ -1132,7 +1132,7 @@
},
"showHeader": true
},
"pluginVersion": "12.2.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"name\": \"A\",\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Min\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Max\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Info\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"State\",\n \"type\": \"enum\",\n \"typeInfo\": {\n \"frame\": \"enum\"\n },\n \"config\": {\n \"type\": {\n \"enum\": {\n \"text\": [\n \"Unknown\",\n \"Up\",\n \"Down\"\n ]\n }\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1754578636810,\n 1754580436810,\n 1754582236810,\n 1754584036810,\n 1754585836810,\n 1754587636810,\n 1754589436810,\n 1754591236810,\n 1754593036810,\n 1754594836810\n ],\n [\n 68.67815082171406,\n 68.21038738705283,\n 67.85748579518524,\n 67.48088827530566,\n 67.36362085363314,\n 67.75399477824531,\n 67.9006935145112,\n 68.2893033558627,\n 68.20547872017494,\n 67.96251158324088\n ],\n [\n 67.52655915076163,\n 68.03367017865995,\n 65.44308825471086,\n 66.94142900198796,\n 66.33531057489242,\n 66.97007154625013,\n 67.88134129373145,\n 65.92102089546943,\n 65.7896781452792,\n 67.77680599320166\n ],\n [\n 68.78026730954913,\n 68.46495903270079,\n 68.29118217530531,\n 67.90561937677037,\n 68.05316512901861,\n 70.26033810424696,\n 68.05691975265877,\n 68.76874724839202,\n 70.02772232081647,\n 70.38628221361253\n ],\n [\n \"up\",\n \"down fast\",\n \"down\",\n \"down\",\n \"down\",\n \"up\",\n \"up\",\n \"up\",\n \"down\",\n \"down\"\n ],\n [\n 1,\n 2,\n 2,\n 2,\n 2,\n 1,\n 1,\n 1,\n 2,\n 2\n ]\n ]\n }\n }\n]",
@@ -1279,7 +1279,7 @@
},
"showHeader": true
},
"pluginVersion": "12.2.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"name\": \"A\",\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Min\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Max\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Info\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"State\",\n \"type\": \"enum\",\n \"typeInfo\": {\n \"frame\": \"enum\"\n },\n \"config\": {\n \"type\": {\n \"enum\": {\n \"text\": [\n \"Unknown\",\n \"Up\",\n \"Down\"\n ]\n }\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1754579008785,\n 1754580808785,\n 1754582608785,\n 1754584408785,\n 1754586208785,\n 1754588008785,\n 1754589808785,\n 1754591608785,\n 1754593408785,\n 1754595208785\n ],\n [\n 89.82213040422468,\n 89.53763263564208,\n 90.03103064454615,\n 90.51535567776907,\n 90.88960652895187,\n 91.30921636258488,\n 91.12427953645643,\n 91.1507153149385,\n 90.81021008780039,\n 91.11327042147582\n ],\n [\n 87.33191559754641,\n 88.46410173314496,\n 88.06920794235972,\n 88.98473401700574,\n 90.60349141857057,\n 90.5858441719162,\n 90.42154517148411,\n 90.32413455176213,\n 90.72055904641739,\n 88.74774424339796\n ],\n [\n 90.58845140604446,\n 90.08173133413374,\n 90.58230197880795,\n 92.55531809442493,\n 93.09165646181742,\n 93.38354884838586,\n 92.68544239576967,\n 92.15770413350477,\n 92.77335012158814,\n 92.52115072193222\n ],\n [\n \"down\",\n \"down\",\n \"up fast\",\n \"up fast\",\n \"up\",\n \"up fast\",\n \"down\",\n \"up\",\n \"down\",\n \"up\"\n ],\n [\n 2,\n 2,\n 1,\n 1,\n 1,\n 1,\n 2,\n 1,\n 2,\n 1\n ]\n ]\n }\n }\n]",
@@ -1426,7 +1426,7 @@
},
"showHeader": true
},
"pluginVersion": "12.2.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"name\": \"A\",\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Min\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Max\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Info\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"State\",\n \"type\": \"enum\",\n \"typeInfo\": {\n \"frame\": \"enum\"\n },\n \"config\": {\n \"type\": {\n \"enum\": {\n \"text\": [\n \"Unknown\",\n \"Up\",\n \"Down\"\n ]\n }\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1754579078813,\n 1754580878813,\n 1754582678813,\n 1754584478813,\n 1754586278813,\n 1754588078813,\n 1754589878813,\n 1754591678813,\n 1754593478813,\n 1754595278813\n ],\n [\n 77.30289281564126,\n 87.14357945690202,\n 76.67466266903213,\n 76.27969352662608,\n 95.80292639037513,\n 75.68396049965999,\n 75.76117912054544,\n 76.05073308839697,\n 75.93744737071192,\n 75.82930696129104\n ],\n [\n 76.58900057075734,\n 76.01493865596112,\n 75.25501274661029,\n 75.52871130918913,\n 73.53324017648568,\n 74.74670600818675,\n 73.4627470190636,\n 75.38455781635166,\n 74.04318166296315,\n 74.79620005730261\n ],\n [\n 77.58417866514938,\n 77.9390885538468,\n 78.37258492198521,\n 76.82242506518101,\n 75.81423643776408,\n 75.86349731305505,\n 76.79031910137064,\n 77.71934637974913,\n 76.11981207380533,\n 77.29746416618009\n ],\n [\n \"down fast\",\n \"down\",\n \"down fast\",\n \"down\",\n \"down fast\",\n \"down\",\n \"up\",\n \"up\",\n \"down\",\n \"down\"\n ],\n [\n 2,\n 2,\n 2,\n 2,\n 2,\n 2,\n 1,\n 1,\n 2,\n 2\n ]\n ]\n }\n }\n]",
@@ -1573,7 +1573,7 @@
}
]
},
"pluginVersion": "12.2.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"name\": \"A\",\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Min\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Max\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Info\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"State\",\n \"type\": \"enum\",\n \"typeInfo\": {\n \"frame\": \"enum\"\n },\n \"config\": {\n \"type\": {\n \"enum\": {\n \"text\": [\n \"Unknown\",\n \"Up\",\n \"Down\"\n ]\n }\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1754579078813,\n 1754580878813,\n 1754582678813,\n 1754584478813,\n 1754586278813,\n 1754588078813,\n 1754589878813,\n 1754591678813,\n 1754593478813,\n 1754595278813\n ],\n [\n 77.30289281564126,\n 87.14357945690202,\n 76.67466266903213,\n 76.27969352662608,\n 95.80292639037513,\n 75.68396049965999,\n 75.76117912054544,\n 76.05073308839697,\n 75.93744737071192,\n 75.82930696129104\n ],\n [\n 76.58900057075734,\n 76.01493865596112,\n 75.25501274661029,\n 75.52871130918913,\n 73.53324017648568,\n 74.74670600818675,\n 73.4627470190636,\n 75.38455781635166,\n 74.04318166296315,\n 74.79620005730261\n ],\n [\n 77.58417866514938,\n 77.9390885538468,\n 78.37258492198521,\n 76.82242506518101,\n 75.81423643776408,\n 75.86349731305505,\n 76.79031910137064,\n 77.71934637974913,\n 76.11981207380533,\n 77.29746416618009\n ],\n [\n \"down fast\",\n \"down\",\n \"down fast\",\n \"down\",\n \"down fast\",\n \"down\",\n \"up\",\n \"up\",\n \"down\",\n \"down\"\n ],\n [\n 2,\n 2,\n 2,\n 2,\n 2,\n 2,\n 1,\n 1,\n 2,\n 2\n ]\n ]\n }\n }\n]",
@@ -1755,7 +1755,7 @@
"showHeader": true,
"sortBy": []
},
"pluginVersion": "12.2.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"rawFrameContent": "[\n {\n \"schema\": {\n \"refId\": \"A\",\n \"name\": \"A\",\n \"fields\": [\n {\n \"name\": \"Time\",\n \"type\": \"time\",\n \"typeInfo\": {\n \"frame\": \"time.Time\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Value\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Min\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Max\",\n \"type\": \"number\",\n \"typeInfo\": {\n \"frame\": \"float64\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"Info\",\n \"type\": \"string\",\n \"typeInfo\": {\n \"frame\": \"string\",\n \"nullable\": true\n },\n \"config\": {}\n },\n {\n \"name\": \"State\",\n \"type\": \"enum\",\n \"typeInfo\": {\n \"frame\": \"enum\"\n },\n \"config\": {\n \"type\": {\n \"enum\": {\n \"text\": [\n \"Unknown\",\n \"Up\",\n \"Down\"\n ]\n }\n }\n }\n }\n ]\n },\n \"data\": {\n \"values\": [\n [\n 1754579078813,\n 1754580878813,\n 1754582678813,\n 1754584478813,\n 1754586278813,\n 1754588078813,\n 1754589878813,\n 1754591678813,\n 1754593478813,\n 1754595278813\n ],\n [\n 77.30289281564126,\n 87.14357945690202,\n 76.67466266903213,\n 76.27969352662608,\n 95.80292639037513,\n 75.68396049965999,\n 75.76117912054544,\n 76.05073308839697,\n 75.93744737071192,\n 75.82930696129104\n ],\n [\n 76.58900057075734,\n 76.01493865596112,\n 75.25501274661029,\n 75.52871130918913,\n 73.53324017648568,\n 74.74670600818675,\n 73.4627470190636,\n 75.38455781635166,\n 74.04318166296315,\n 74.79620005730261\n ],\n [\n 77.58417866514938,\n 77.9390885538468,\n 78.37258492198521,\n 76.82242506518101,\n 75.81423643776408,\n 75.86349731305505,\n 76.79031910137064,\n 77.71934637974913,\n 76.11981207380533,\n 77.29746416618009\n ],\n [\n \"down fast\",\n \"down\",\n \"down fast\",\n \"down\",\n \"down fast\",\n \"down\",\n \"up\",\n \"up\",\n \"down\",\n \"down\"\n ],\n [\n 2,\n 2,\n 2,\n 2,\n 2,\n 2,\n 1,\n 1,\n 2,\n 2\n ]\n ]\n }\n }\n]",
@@ -1816,7 +1816,7 @@
},
"showHeader": true
},
"pluginVersion": "12.2.0-pre",
"pluginVersion": "12.1.0-pre",
"targets": [
{
"csvContent": "a,b,c",
@@ -1852,5 +1852,5 @@
"timezone": "",
"title": "Panel Tests - Table - Kitchen Sink",
"uid": "dcb9f5e9-8066-4397-889e-864b99555dbb",
"version": 32
"version": 33
}
File diff suppressed because it is too large Load Diff
+1
View File
@@ -100,6 +100,7 @@
"table_sparkline_cell": (import '../dev-dashboards/panel-table/table_sparkline_cell.json'),
"table_tests": (import '../dev-dashboards/panel-table/table_tests.json'),
"table_tests_new": (import '../dev-dashboards/panel-table/table_tests_new.json'),
"table_v12_2_migrations": (import '../dev-dashboards/panel-table/table_v12_2_migrations.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-repeating-panels": (import '../dev-dashboards/feature-templating/templating-repeating-panels.json'),
@@ -209,6 +209,7 @@ This option is only available when you're editing the panel.
| Column width | Define a column width, in pixels, rather than allowing the width to be set automatically. By default, Grafana calculates the column width based on the table size and the minimum column width. |
| Column alignment | Set how Grafana should align cell contents. Choose from: **Auto** (default), **Left**, **Center**, or **Right**. |
| Column filter | Temporarily change how column data is displayed. For example, show or hide specific values. For more information, refer to [Column filtering](#column-filtering). |
| Wrap text | Enables text wrapping for cell content. |
| Wrap header text | Enables text wrapping for column headers. |
<!-- prettier-ignore-end -->
@@ -288,24 +289,17 @@ The colored background cell type has the following options:
| ------ | ----------- |
| Background display mode | Choose between **Basic** and **Gradient**. |
| Apply to entire row | Toggle the switch on to apply the background color that's configured for the cell to the whole row. |
| Wrap text | Toggle the **Wrap text** switch to wrap text in the cell that contains the longest content in your table. To wrap the text _in a specific column only_, use a **Fields with name** [field override](ref:field-override), select the **Cell options > Cell type** override property, and toggle on the **Wrap text** switch. |
| Cell value inspect | <p>Enables value inspection from table cells. When the switch is toggled on, clicking the inspect icon in a cell opens the **Inspect value** drawer which contains two tabs: **Plain text** and **Code editor**.</p><p>Grafana attempts to automatically detect the type of data in the cell and opens the drawer with the associated tab showing. However, you can switch back and forth between tabs.</p> |
| Tooltip from field | Toggle on the **Tooltip from field** switch to use the values from another field (or column) in a tooltip. For more information, refer to the [Tooltip from field](#tooltip-from-field). |
<!-- prettier-ignore-end -->
<!-- The wrap text, cell value inspect, and tooltip from field descriptions above should be copied from docs/sources/shared/visualizations/cell-options.md -->
<!-- The cell value inspect and tooltip from field descriptions above should be copied from docs/sources/shared/visualizations/cell-options.md -->
#### Data links
If you've configured data links, when the cell type is **Auto**, the cell text becomes clickable.
If you change the cell type to **Data links**, the cell text reflects the titles of the configured data links. To control the application of data link text more granularly, use a **Cell option > Cell type > Data links** field override.
Data links cells also support text wrapping.
Toggle the **Wrap text** switch to wrap text in the cell that contains the longest content in your table.
To wrap the text _in a specific column only_, use a **Fields with name** [field override](ref:field-override), select the **Cell options > Cell type** override property, and toggle on the **Wrap text** switch.
<!-- The wrap text description above should be copied from docs/sources/shared/visualizations/cell-options.md -->
#### Gauge
With this cell type, cells can be displayed as a graphical gauge, with several different presentation types.
@@ -402,12 +396,6 @@ The **Pill** cell type displays each item in a comma-separated string in a color
The colors applied to each piece of text are maintained throughout the table.
For example, if the word "test" is first displayed in a red pill, it will always be displayed in a red pill.
Pill cells also support text wrapping.
Toggle the **Wrap text** switch to wrap text in the cell that contains the longest content in your table.
To wrap the text _in a specific column only_, use a **Fields with name** [field override](ref:field-override), select the **Cell options > Cell type** override property, and toggle on the **Wrap text** switch.
<!-- The wrap text description above should be copied from docs/sources/shared/visualizations/cell-options.md -->
The following data formats are supported for the pill cell type:
- Comma-separated values (`cows,chickens,goats`)
@@ -5,7 +5,6 @@ title: Cell options
<!-- prettier-ignore-start -->
| Option | Description |
| ------ | ----------- |
| Wrap text | Toggle the **Wrap text** switch to wrap text in the cell that contains the longest content in your table. To wrap the text _in a specific column only_, use a **Fields with name** [field override](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-overrides/), select the **Cell options > Cell type** override property, and toggle on the **Wrap text** switch. |
| Cell value inspect | <p>Enables value inspection from table cells. When the switch is toggled on, clicking the inspect icon in a cell opens the **Inspect value** drawer which contains two tabs: **Plain text** and **Code editor**.</p><p>Grafana attempts to automatically detect the type of data in the cell and opens the drawer with the associated tab showing. However, you can switch back and forth between tabs.</p> |
| Tooltip from field | Toggle on the **Tooltip from field** switch to use the values from another field (or column) in a tooltip. For more information, refer to [Tooltip from field](#tooltip-from-field). |
<!-- prettier-ignore-end -->
@@ -43,15 +43,13 @@ const getColumnIdx = async (loc: Page | Locator, columnName: string) => {
const disableAllTextWrap = async (loc: Page | Locator, selectors: E2ESelectorGroups) => {
// disable text wrapping for all of the columns, since long text with links in them can push the links off the screen.
const wrapTextToggle = loc.locator(
`[aria-label="${selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text')}"]`
);
const wrapTextToggle = loc.getByLabel('Wrap text');
const count = await wrapTextToggle.count();
for (let i = 0; i < count; i++) {
const toggle = wrapTextToggle.nth(i);
if ((await toggle.locator('//preceding-sibling::input').getAttribute('checked')) !== null) {
await toggle.click();
if ((await toggle.getAttribute('checked')) !== null) {
await toggle.click({ force: true });
}
}
};
@@ -77,7 +75,7 @@ test.describe('Panels test: Table - Kitchen Sink', { tag: ['@panels', '@table']
await dashboardPage
.getByGrafanaSelector(selectors.components.OptionsGroup.group('panel-options-override-12'))
.getByLabel(selectors.components.PanelEditor.OptionsPane.fieldLabel('Wrap text'))
.getByText('Wrap text')
.click();
await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeLessThan(100);
@@ -745,16 +745,6 @@ export enum TableCellBackgroundDisplayMode {
Gradient = 'gradient',
}
/**
* Whenever we add text wrapping, we should add all text wrapping options at once
*/
export interface TableWrapTextOptions {
/**
* if true, wrap the text content of the cell
*/
wrapText?: boolean;
}
/**
* Sort by field state
*/
@@ -788,14 +778,14 @@ export const defaultTableFooterOptions: Partial<TableFooterOptions> = {
/**
* Auto mode table cell options
*/
export interface TableAutoCellOptions extends TableWrapTextOptions {
export interface TableAutoCellOptions {
type: TableCellDisplayMode.Auto;
}
/**
* Colored text cell options
*/
export interface TableColorTextCellOptions extends TableWrapTextOptions {
export interface TableColorTextCellOptions {
type: TableCellDisplayMode.ColorText;
}
@@ -818,7 +808,7 @@ export interface TableImageCellOptions {
/**
* Show data links in the cell
*/
export interface TableDataLinksCellOptions extends TableWrapTextOptions {
export interface TableDataLinksCellOptions {
type: TableCellDisplayMode.DataLinks;
}
@@ -849,13 +839,13 @@ export interface TableSparklineCellOptions extends GraphFieldConfig {
/**
* Colored background cell options
*/
export interface TableColoredBackgroundCellOptions extends TableWrapTextOptions {
export interface TableColoredBackgroundCellOptions {
applyToRow?: boolean;
mode?: TableCellBackgroundDisplayMode;
type: TableCellDisplayMode.ColorBackground;
}
export interface TablePillCellOptions extends TableWrapTextOptions {
export interface TablePillCellOptions {
type: TableCellDisplayMode.Pill;
}
@@ -1006,7 +996,7 @@ export enum TableCellTooltipPlacement {
* Field options for each field within a table (e.g 10, "The String", 64.20, etc.)
* Generally defines alignment, filtering capabilties, display options, etc.
*/
export interface TableFieldOptions {
export interface TableFieldOptions extends HideableFieldConfig {
align: FieldTextAlignment;
cellOptions: TableCellOptions;
/**
@@ -1014,7 +1004,6 @@ export interface TableFieldOptions {
*/
displayMode?: TableCellDisplayMode;
filterable?: boolean;
hidden?: boolean; // ?? default is missing or false ??
/**
* Hides any header for a column, useful for columns that show some static content or buttons.
*/
@@ -1039,6 +1028,10 @@ export interface TableFieldOptions {
* Enables text wrapping for column headers
*/
wrapHeaderText?: boolean;
/**
* if true, wrap the text content of the cell
*/
wrapText?: boolean;
}
export const defaultTableFieldOptions: Partial<TableFieldOptions> = {
+8 -13
View File
@@ -11,12 +11,6 @@ TableCellDisplayMode: "auto" | "color-text" | "color-background" | "color-backgr
// or a gradient.
TableCellBackgroundDisplayMode: "basic" | "gradient" @cuetsy(kind="enum",memberNames="Basic|Gradient")
// Whenever we add text wrapping, we should add all text wrapping options at once
TableWrapTextOptions: {
// if true, wrap the text content of the cell
wrapText?: bool
} @cuetsy(kind="interface")
// Sort by field state
TableSortByFieldState: {
// Sets the display name of the field to sort by
@@ -37,12 +31,12 @@ TableFooterOptions: {
// Auto mode table cell options
TableAutoCellOptions: {
type: TableCellDisplayMode & "auto"
} & TableWrapTextOptions @cuetsy(kind="interface")
} @cuetsy(kind="interface")
// Colored text cell options
TableColorTextCellOptions: {
type: TableCellDisplayMode & "color-text"
} & TableWrapTextOptions @cuetsy(kind="interface")
} @cuetsy(kind="interface")
// Json view cell options
TableJsonViewCellOptions: {
@@ -59,7 +53,7 @@ TableImageCellOptions: {
// Show data links in the cell
TableDataLinksCellOptions: {
type: TableCellDisplayMode & "data-links"
} & TableWrapTextOptions @cuetsy(kind="interface")
} @cuetsy(kind="interface")
// Show actions in the cell
TableActionsCellOptions: {
@@ -85,11 +79,11 @@ TableColoredBackgroundCellOptions: {
type: TableCellDisplayMode & "color-background"
mode?: TableCellBackgroundDisplayMode
applyToRow?: bool
} & TableWrapTextOptions @cuetsy(kind="interface")
} @cuetsy(kind="interface")
TablePillCellOptions: {
type: TableCellDisplayMode & "pill"
} & TableWrapTextOptions @cuetsy(kind="interface")
} @cuetsy(kind="interface")
TableMarkdownCellOptions: {
type: TableCellDisplayMode & "markdown"
@@ -131,13 +125,14 @@ TableFieldOptions: {
// This field is deprecated in favor of using cellOptions
displayMode?: TableCellDisplayMode
cellOptions: TableCellOptions
hidden?: bool // ?? default is missing or false ??
inspect: bool | *false
filterable?: bool
// Hides any header for a column, useful for columns that show some static content or buttons.
hideHeader?: bool
// if true, wrap the text content of the cell
wrapText?: bool
// Enables text wrapping for column headers
wrapHeaderText?: bool
// Selecting or hovering this field will show a tooltip containing the content within the target field
tooltip?: TableCellTooltipOptions
} @cuetsy(kind="interface")
} & HideableFieldConfig @cuetsy(kind="interface")
@@ -17,7 +17,7 @@ import { MarkdownCell, getStyles as getMarkdownCellStyles } from './MarkdownCell
import { PillCell, getStyles as getPillStyles } from './PillCell';
import { SparklineCell, getStyles as getSparklineCellStyles } from './SparklineCell';
const AutoCellRenderer = memo((props: TableCellRendererProps) => (
export const AutoCellRenderer = memo((props: TableCellRendererProps) => (
<AutoCell value={props.value} field={props.field} rowIdx={props.rowIdx} />
));
AutoCellRenderer.displayName = 'AutoCellRenderer';
@@ -143,13 +143,13 @@ const createNestedDataFrame = (): DataFrame => {
name: '__depth',
type: FieldType.number,
values: [0, 0],
config: { custom: { hidden: true } },
config: { custom: { hideFrom: { viz: true } } },
},
{
name: '__index',
type: FieldType.number,
values: [0, 1],
config: { custom: { hidden: true } },
config: { custom: { hideFrom: { viz: true } } },
},
{
name: '__nestedFrames',
@@ -1258,10 +1258,7 @@ describe('TableNG', () => {
const frame = createBasicDataFrame();
frame.fields.forEach((field) => {
if (field.config?.custom) {
field.config.custom.cellOptions = {
...field.config.custom.cellOptions,
wrapText: true,
};
field.config.custom.wrapText = true;
}
});
@@ -1274,9 +1271,7 @@ describe('TableNG', () => {
fieldConfig={{
defaults: {
custom: {
cellOptions: {
wrapText: true,
},
wrapText: true,
},
},
overrides: [],
@@ -719,9 +719,9 @@ describe('TableNG hooks', () => {
...field.config,
custom: {
...field.config?.custom,
wrapText: true,
cellOptions: {
cellType: TableCellDisplayMode.Auto,
wrapText: true,
},
},
},
@@ -547,14 +547,14 @@ describe('TableNG utils', () => {
name: 'test',
type: FieldType.string,
config: {
custom: { cellOptions: { type: TableCellDisplayMode.ColorText, inspectEnabled: false, wrapText: true } },
custom: { cellOptions: { type: TableCellDisplayMode.ColorText, inspectEnabled: false } },
},
values: [],
};
const options = getCellOptions(field);
expect(options).toEqual({ type: TableCellDisplayMode.ColorText, inspectEnabled: false, wrapText: true });
expect(options).toEqual({ type: TableCellDisplayMode.ColorText, inspectEnabled: false });
});
it('should handle legacy displayMode property', () => {
@@ -1074,12 +1074,12 @@ describe('TableNG utils', () => {
it('sets up text height measurers for each text column if wrapping is on', () => {
const fields: Field[] = [
{ name: 'Name', type: FieldType.string, values: [], config: { custom: { cellOptions: { wrapText: true } } } },
{ name: 'Name', type: FieldType.string, values: [], config: { custom: { wrapText: true } } },
{
name: 'Address',
type: FieldType.string,
values: [],
config: { custom: { cellOptions: { wrapText: true } } },
config: { custom: { wrapText: true } },
},
];
const measurers = buildCellHeightMeasurers(fields, ctx);
@@ -1094,7 +1094,7 @@ describe('TableNG utils', () => {
name: 'Address',
type: FieldType.string,
values: [],
config: { custom: { cellOptions: { wrapText: true } } },
config: { custom: { wrapText: true } },
},
];
@@ -1108,7 +1108,7 @@ describe('TableNG utils', () => {
name: 'Tags',
type: FieldType.string,
values: ['tag1,tag2', 'tag3', '["tag4","tag5","tag6"]'],
config: { custom: { cellOptions: { type: TableCellDisplayMode.Pill, wrapText: true } } },
config: { custom: { wrapText: true, cellOptions: { type: TableCellDisplayMode.Pill } } },
},
];
const measurers = buildCellHeightMeasurers(fields, ctx);
@@ -1125,7 +1125,7 @@ describe('TableNG utils', () => {
name: 'Links',
type: FieldType.string,
values: ['http://example.com/1', 'http://example.com/2'],
config: { custom: { cellOptions: { type: TableCellDisplayMode.DataLinks, wrapText: true } } },
config: { custom: { wrapText: true, cellOptions: { type: TableCellDisplayMode.DataLinks } } },
getLinks: jest.fn((): LinkModel[] => [
{ title: 'Link 1', href: 'http://example.com/1', target: '_blank', origin: { datasourceUid: 'test' } },
{ title: 'Link 2', href: 'http://example.com/2', target: '_self', origin: { datasourceUid: 'test' } },
@@ -1141,12 +1141,12 @@ describe('TableNG utils', () => {
it('does not enable text counting for non-string fields', () => {
const fields: Field[] = [
{ name: 'Name', type: FieldType.string, values: [], config: { custom: {} } },
{ name: 'Age', type: FieldType.number, values: [], config: { custom: { cellOptions: { wrapText: true } } } },
{ name: 'Age', type: FieldType.number, values: [], config: { custom: { wrapText: true } } },
];
const measurers = buildCellHeightMeasurers(fields, ctx);
// empty array - we had one column that indicated it wraps, but it was numeric, so we just ignore it
expect(measurers).toEqual([]);
expect(measurers).toBeUndefined();
});
it('returns an undefined if no columns are wrapped', () => {
@@ -1170,13 +1170,13 @@ describe('TableNG utils', () => {
name: 'Name',
type: FieldType.string,
values: ['foo', 'bar', 'baz', 'longer one here', 'shorter'],
config: { custom: { cellOptions: { wrapText: true } } },
config: { custom: { wrapText: true } },
},
{
name: 'Age',
type: FieldType.number,
values: [1, 2, 3, 123456, 789122349932],
config: { custom: { cellOptions: { wrapText: true } } },
config: { custom: { wrapText: true } },
},
];
measurers = [
@@ -28,6 +28,7 @@ import { getTextColorForAlphaBackground } from '../../../utils/colors';
import { TableCellOptions } from '../types';
import { inferPills } from './Cells/PillCell';
import { AutoCellRenderer, getCellRenderer } from './Cells/renderers';
import { COLUMN, TABLE } from './constants';
import {
TableRow,
@@ -80,12 +81,7 @@ export function isCellInspectEnabled(field: Field): boolean {
* Returns true if text wrapping should be applied to the cell.
*/
export function shouldTextWrap(field: Field): boolean {
const cellOptions = getCellOptions(field);
// @ts-ignore - a handful of cellTypes have boolean wrapText, but not all of them.
// we should be very careful to only use boolean type for cellOptions.wrapText.
// TBH we will probably move this up to a field option which is showIf rendered anyway,
// but that'll be a migration to do, so it needs to happen post-GA.
return Boolean(cellOptions?.wrapText);
return Boolean(field.config.custom?.wrapText);
}
/**
@@ -301,8 +297,14 @@ export function buildCellHeightMeasurers(
setupMeasurerForIdx(TableCellDisplayMode.DataLinks, fieldIdx);
} else if (cellType === TableCellDisplayMode.Pill) {
setupMeasurerForIdx(TableCellDisplayMode.Pill, fieldIdx);
} else if (field.type === FieldType.string) {
} else if (
field.type === FieldType.string &&
getCellRenderer(field, getCellOptions(field)) === AutoCellRenderer
) {
setupMeasurerForIdx(TableCellDisplayMode.Auto, fieldIdx);
} else {
// no measurer was configured for this cell type
wrappedFields--;
}
}
}
@@ -849,7 +851,7 @@ export const predicateByName = (name: string) => (f: Field) => f.name === name |
* returns only fields that are not nested tables and not explicitly hidden
*/
export function getVisibleFields(fields: Field[]): Field[] {
return fields.filter((field) => field.type !== FieldType.nestedFrames && field.config.custom?.hidden !== true);
return fields.filter((field) => field.type !== FieldType.nestedFrames && field.config.custom?.hideFrom?.viz !== true);
}
/**
@@ -114,7 +114,12 @@ export function getColumns(
for (const [fieldIndex, field] of data.fields.entries()) {
const fieldTableOptions: TableFieldOptions = field.config.custom || {};
if (fieldTableOptions.hidden || field.type === FieldType.nestedFrames) {
if (
// @ts-ignore this was the former hidden option; we support it for legacy use cases while TableRT is being sunset.
fieldTableOptions.hidden ||
fieldTableOptions.hideFrom?.viz ||
field.type === FieldType.nestedFrames
) {
continue;
}
@@ -112,7 +112,9 @@ export function getMostFiredInstancesScene(datasource: DataSourceRef, panelTitle
.setHeaderActions([new InsightsMenuButton({ panel: panelTitle })])
.setOverrides((builder) =>
// Hide the rule UID field, if we omit it in a transformation the custom cell renderer will not work
builder.matchFieldsWithName(RULE_UID_FIELD_NAME).overrideCustomFieldConfig('hidden', true)
builder
.matchFieldsWithName(RULE_UID_FIELD_NAME)
.overrideCustomFieldConfig('hideFrom', { viz: true, legend: false, tooltip: false })
)
.build(),
});
@@ -4,7 +4,7 @@ import { useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { t } from '@grafana/i18n';
import { TableCellOptions, TableWrapTextOptions } from '@grafana/schema';
import { TableCellOptions } from '@grafana/schema';
import { Combobox, ComboboxOption, Field, TableCellDisplayMode, useStyles2 } from '@grafana/ui';
import { BarGaugeCellOptionsEditor } from './cells/BarGaugeCellOptionsEditor';
@@ -12,7 +12,6 @@ import { ColorBackgroundCellOptionsEditor } from './cells/ColorBackgroundCellOpt
import { ImageCellOptionsEditor } from './cells/ImageCellOptionsEditor';
import { MarkdownCellOptionsEditor } from './cells/MarkdownCellOptionsEditor';
import { SparklineCellOptionsEditor } from './cells/SparklineCellOptionsEditor';
import { TextWrapOptionsEditor } from './cells/TextWrapOptionsEditor';
// The props that any cell type editor are expected
// to handle. In this case the generic type should
@@ -28,19 +27,6 @@ interface Props {
id?: string;
}
const TEXT_WRAP_CELL_TYPES = new Set([
TableCellDisplayMode.Auto,
TableCellDisplayMode.Sparkline,
TableCellDisplayMode.ColorText,
TableCellDisplayMode.ColorBackground,
TableCellDisplayMode.DataLinks,
TableCellDisplayMode.Pill,
]);
function isTextWrapCellType(value: TableCellOptions): value is TableCellOptions & TableWrapTextOptions {
return TEXT_WRAP_CELL_TYPES.has(value.type);
}
export const TableCellOptionEditor = ({ value, onChange, id }: Props) => {
const cellType = value.type;
const styles = useStyles2(getStyles);
@@ -95,7 +81,6 @@ export const TableCellOptionEditor = ({ value, onChange, id }: Props) => {
<Field>
<Combobox id={id} options={cellDisplayModeOptions} value={currentMode} onChange={onCellTypeChange} />
</Field>
{isTextWrapCellType(value) && <TextWrapOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />}
{cellType === TableCellDisplayMode.Gauge && (
<BarGaugeCellOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />
)}
@@ -8,8 +8,6 @@ import { Field, RadioButtonGroup, Switch } from '@grafana/ui';
import { TableCellEditorProps } from '../TableCellOptionEditor';
import { TextWrapOptionsEditor } from './TextWrapOptionsEditor';
const colorBackgroundOpts: Array<SelectableValue<TableCellBackgroundDisplayMode>> = [
{ value: TableCellBackgroundDisplayMode.Basic, label: 'Basic' },
{ value: TableCellBackgroundDisplayMode.Gradient, label: 'Gradient' },
@@ -57,14 +55,6 @@ export const ColorBackgroundCellOptionsEditor = ({
onChange={onColorRowChange}
/>
</Field>
<TextWrapOptionsEditor
cellOptions={cellOptions}
onChange={(updatedCellOptions) => {
cellOptions.wrapText = updatedCellOptions.wrapText;
onChange(cellOptions);
}}
/>
</>
);
};
@@ -1,34 +0,0 @@
import { useId } from 'react';
import { selectors } from '@grafana/e2e-selectors';
import { t } from '@grafana/i18n';
import { TableCellOptions, TableWrapTextOptions } from '@grafana/schema';
import { Field, Switch } from '@grafana/ui';
import { TableCellEditorProps } from '../TableCellOptionEditor';
export const TextWrapOptionsEditor = ({
cellOptions,
onChange,
}: TableCellEditorProps<TableCellOptions & TableWrapTextOptions>) => {
// Handle row coloring changes
const onWrapTextChange = () => {
cellOptions.wrapText = !cellOptions.wrapText;
onChange(cellOptions);
};
const htmlId = useId();
return (
<>
<Field label={t('table.text-wrap-options.label-wrap-text', 'Wrap text')}>
<Switch
id={htmlId}
label={selectors.components.PanelEditor.OptionsPane.fieldLabel(`Wrap text`)}
value={cellOptions.wrapText}
onChange={onWrapTextChange}
/>
</Field>
</>
);
};
@@ -1,6 +1,12 @@
import { createDataFrame, FieldType, PanelModel } from '@grafana/data';
import { TableCellDisplayMode } from '@grafana/ui';
import { migrateFromParentRowIndexToNestedFrames, tablePanelChangedHandler } from './migrations';
import {
migrateFromParentRowIndexToNestedFrames,
migrateHiddenFields,
migrateTextWrapToFieldLevel,
tablePanelChangedHandler,
} from './migrations';
describe('Table Migrations', () => {
it('migrates transform out to core transforms', () => {
@@ -361,4 +367,111 @@ describe('Table Migrations', () => {
expect(newFormat[1].fields[1].values[0][0].fields[0].values[0]).toBe('0_subA');
expect(newFormat[1].fields[1].values[1][0].fields[0].values[0]).toBe('1_subA');
});
describe('migrateTextWrapToFieldLevel', () => {
it('migrates a top-level config.custom.cellOptions.wrapText to a config.custom.wrapText', () => {
const panel = {
fieldConfig: {
defaults: {
custom: {
cellOptions: {
wrapText: true,
},
},
},
overrides: [],
},
} as unknown as PanelModel;
migrateTextWrapToFieldLevel(panel);
expect(panel.fieldConfig.defaults.custom.wrapText).toBe(true);
expect(panel.fieldConfig.defaults.custom.cellOptions.wrapText).toBeUndefined();
});
it('migrates field override config.custom.cellOptions.wrapTexts to a field override config.custom.wrapTexts', () => {
const panel = {
fieldConfig: {
defaults: {
custom: {},
},
overrides: [
{
matcher: { id: 'byName', options: 'field1' },
properties: [
{
id: 'custom.cellOptions',
value: { wrapText: true, type: TableCellDisplayMode.Pill },
},
],
},
],
},
} as unknown as PanelModel;
migrateTextWrapToFieldLevel(panel);
expect(panel.fieldConfig.overrides[0].properties).toEqual(
expect.arrayContaining([
{ id: 'custom.wrapText', value: true },
{ id: 'custom.cellOptions', value: { type: TableCellDisplayMode.Pill } },
])
);
});
it('does not overwrite field overrides for cellOptions which do not have wrapText set', () => {
const panel = {
fieldConfig: {
defaults: {
custom: {},
},
overrides: [
{
matcher: { id: 'byName', options: 'field1' },
properties: [
{
id: 'custom.cellOptions',
value: { type: TableCellDisplayMode.Pill },
},
],
},
],
},
} as unknown as PanelModel;
migrateTextWrapToFieldLevel(panel);
// passes the override through with no changes
expect(panel.fieldConfig.overrides[0]).toBe(panel.fieldConfig.overrides[0]);
});
});
describe('migrateHiddenFields', () => {
it('migrates fields with config.custom.hidden=true to config.custom.hideFrom.viz=true', () => {
const panel = {
fieldConfig: {
defaults: {
custom: {},
},
overrides: [
{
matcher: { id: 'byName', options: 'field1' },
properties: [
{
id: 'custom.hidden',
value: true,
},
],
},
],
},
} as unknown as PanelModel;
migrateHiddenFields(panel);
expect(panel.fieldConfig.overrides[0].properties).toEqual(
expect.arrayContaining([{ id: 'custom.hideFrom.viz', value: true }])
);
expect(panel.fieldConfig.overrides[0].properties).not.toEqual(
expect.arrayContaining([{ id: 'custom.hidden', value: true }])
);
});
});
});
+50 -1
View File
@@ -1,4 +1,4 @@
import { omitBy, isNil, isNumber, defaultTo, groupBy } from 'lodash';
import { omitBy, isNil, isNumber, defaultTo, groupBy, omit } from 'lodash';
import {
PanelModel,
@@ -25,6 +25,9 @@ export const tableMigrationHandler = (panel: PanelModel<Options>): Partial<Optio
console.log('Was angular table', panel);
}
migrateTextWrapToFieldLevel(panel);
migrateHiddenFields(panel);
// Nothing changed
return panel.options;
};
@@ -297,3 +300,49 @@ export const migrateFromParentRowIndexToNestedFrames = (frames: DataFrame[] | nu
export const hasDeprecatedParentRowIndex = (frames: DataFrame[] | null) => {
return frames?.some((df) => df.meta?.custom?.parentRowIndex !== undefined);
};
export const migrateTextWrapToFieldLevel = (panel: PanelModel<Partial<Options>>) => {
if (panel.fieldConfig?.defaults.custom?.wrapText !== undefined) {
// already migrated
return;
}
const legacyDefaultWrapText: boolean | undefined = panel.fieldConfig?.defaults.custom?.cellOptions?.wrapText;
panel.fieldConfig.overrides = panel.fieldConfig.overrides.map((override) => {
if (override.properties) {
override.properties = override.properties.flatMap((property) => {
if (property.id === 'custom.cellOptions' && property.value && property.value.wrapText !== undefined) {
return [
{ ...property, value: { ...omit(property.value, 'wrapText') } },
{ id: 'custom.wrapText', value: property.value.wrapText },
];
}
return [property];
});
}
return override;
});
panel.fieldConfig.defaults.custom = panel.fieldConfig.defaults.custom ?? {};
panel.fieldConfig.defaults.custom.wrapText = legacyDefaultWrapText;
delete panel.fieldConfig.defaults.custom.cellOptions?.wrapText;
return panel;
};
export const migrateHiddenFields = (panel: PanelModel<Partial<Options>>) => {
panel.fieldConfig.overrides = panel.fieldConfig.overrides.map((override) => {
if (override.properties) {
override.properties = override.properties.map((property) => {
if (property.id === 'custom.hidden') {
return { ...property, id: 'custom.hideFrom.viz' };
}
return property;
});
}
return override;
});
return panel;
};
+9 -6
View File
@@ -10,9 +10,9 @@ import {
} from '@grafana/data';
import { t } from '@grafana/i18n';
import {
defaultTableFieldOptions,
TableCellOptions,
TableCellDisplayMode,
defaultTableFieldOptions,
TableCellHeight,
TableCellTooltipPlacement,
} from '@grafana/schema';
@@ -109,14 +109,17 @@ export const plugin = new PanelPlugin<Options, FieldConfig>(TablePanel)
defaultValue: defaultTableFieldOptions.filterable,
})
.addBooleanSwitch({
path: 'wrapHeaderText',
name: t('table.name-wrap-header-text', 'Wrap header text'),
description: t('table.description-wrap-header-text', 'Enables text wrapping for column headers'),
path: 'wrapText',
name: t('table.name-wrap-text', 'Wrap text'),
category,
defaultValue: undefined,
})
.addBooleanSwitch({
path: 'hidden',
path: 'wrapHeaderText',
name: t('table.name-wrap-header-text', 'Wrap header text'),
category,
})
.addBooleanSwitch({
path: 'hideFrom.viz',
name: t('table.name-hide-in-table', 'Hide in table'),
category,
defaultValue: undefined,
+1 -4
View File
@@ -12771,7 +12771,6 @@
"description-frozen-columns": "Columns are frozen from the left side of the table",
"description-min-column-width": "The minimum width for column auto resizing",
"description-tooltip-from-field": "Render a cell from a field (hidden or visible) in a tooltip",
"description-wrap-header-text": "Enables text wrapping for column headers",
"image-cell-options-editor": {
"description-alt-text": "Alternative text that will be displayed if an image can't be displayed or for users who use a screen reader",
"description-title-text": "Text that will be displayed when the image is hovered by a cursor",
@@ -12806,11 +12805,9 @@
"name-tooltip-from-field": "Tooltip from field",
"name-tooltip-placement": "Tooltip placement",
"name-wrap-header-text": "Wrap header text",
"name-wrap-text": "Wrap text",
"placeholder-column-width": "auto",
"placeholder-fields": "All Numeric Fields",
"text-wrap-options": {
"label-wrap-text": "Wrap text"
},
"tooltip-placement-options": {
"label-auto": "Auto",
"label-bottom": "Bottom",