From 8081fc862a98a6107578f25d03c199999ccfe93f Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Wed, 20 Nov 2024 13:05:55 +0300 Subject: [PATCH 1/7] checkbox type and editor added --- .../CellRenderer/BooleanCellRenderer.test.tsx | 31 +++++++++++++++++++ .../CellRenderer/BooleanCellRenderer.tsx | 25 +++++++++++++++ .../CellRenderer/CellRenderer.test.tsx | 14 +++++++++ .../components/CellRenderer/CellRenderer.tsx | 4 +++ .../EditableColumnEditor.tsx | 1 + .../EditableColumnEditorsRegistry.test.tsx | 29 +++++++++++++++++ .../EditableColumnEditorsRegistry.tsx | 19 +++++++++++- .../components/ColumnEditor/ColumnEditor.tsx | 4 +++ src/constants.ts | 4 +++ src/types/column-editor.ts | 5 +++ src/types/table.ts | 1 + 11 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx create mode 100644 src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx diff --git a/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx b/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx new file mode 100644 index 0000000..169686e --- /dev/null +++ b/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx @@ -0,0 +1,31 @@ +import { render, screen } from '@testing-library/react'; +import { getJestSelectors } from '@volkovlabs/jest-selectors'; +import React from 'react'; + +import { TEST_IDS } from '@/constants'; + +import { BooleanCellRenderer } from './BooleanCellRenderer'; + +type Props = React.ComponentProps; + +describe('BooleanCellRenderer', () => { + /** + * Selectors + */ + const getSelectors = getJestSelectors(TEST_IDS.booleanCellRenderer); + const selectors = getSelectors(screen); + + /** + * Get component + */ + const getComponent = (props: Partial) => { + return ; + }; + + it('Should render value', () => { + render(getComponent({ value: false })); + + expect(selectors.root()).toBeInTheDocument(); + expect(selectors.root()).toBeDisabled(); + }); +}); diff --git a/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx b/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx new file mode 100644 index 0000000..bd4ab64 --- /dev/null +++ b/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx @@ -0,0 +1,25 @@ +import { InlineSwitch } from '@grafana/ui'; +import React from 'react'; + +import { TEST_IDS } from '@/constants'; + +/** + * Properties + */ +interface Props { + /** + * Value + * + * @type {boolean} + */ + value: boolean; +} + +/** + * Boolean Cell Renderer + * @param value + * @constructor + */ +export const BooleanCellRenderer: React.FC = ({ value }) => { + return ; +}; diff --git a/src/components/Table/components/CellRenderer/CellRenderer.test.tsx b/src/components/Table/components/CellRenderer/CellRenderer.test.tsx index 5839acd..b700df8 100644 --- a/src/components/Table/components/CellRenderer/CellRenderer.test.tsx +++ b/src/components/Table/components/CellRenderer/CellRenderer.test.tsx @@ -5,6 +5,7 @@ import React from 'react'; import { CellType, ColumnMeta } from '@/types'; import { createColumnConfig } from '@/utils'; +import { BooleanCellRenderer } from './BooleanCellRenderer'; import { CellRenderer } from './CellRenderer'; import { DefaultCellRenderer } from './DefaultCellRenderer'; import { LayoutCellRenderer } from './LayoutCellRenderer'; @@ -18,6 +19,13 @@ jest.mock('./DefaultCellRenderer', () => ({ DefaultCellRenderer: jest.fn(() => null), })); +/** + * Mock Boolean Cell Renderer + */ +jest.mock('./BooleanCellRenderer', () => ({ + BooleanCellRenderer: jest.fn(() => null), +})); + /** * Mock Layout Cell Renderer */ @@ -99,4 +107,10 @@ describe('CellRenderer', () => { expect(DefaultCellRenderer).toHaveBeenCalled(); }); + + it('Should render boolean cell', () => { + render(getComponent({ column: createColumnWithMeta({ config: createColumnConfig({ type: CellType.BOOLEAN }) }) })); + + expect(BooleanCellRenderer).toHaveBeenCalled(); + }); }); diff --git a/src/components/Table/components/CellRenderer/CellRenderer.tsx b/src/components/Table/components/CellRenderer/CellRenderer.tsx index b5dbf1b..afebd52 100644 --- a/src/components/Table/components/CellRenderer/CellRenderer.tsx +++ b/src/components/Table/components/CellRenderer/CellRenderer.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { CellType } from '@/types'; +import { BooleanCellRenderer } from './BooleanCellRenderer'; import { DefaultCellRenderer } from './DefaultCellRenderer'; import { LayoutCellRenderer } from './LayoutCellRenderer'; @@ -44,6 +45,9 @@ export const CellRenderer: React.FC = ({ renderValue, column, bgColor, ro case CellType.RICH_TEXT: { return ; } + case CellType.BOOLEAN: { + return ; + } default: { return ; } diff --git a/src/components/editors/EditableColumnEditor/EditableColumnEditor.tsx b/src/components/editors/EditableColumnEditor/EditableColumnEditor.tsx index 8a37f4d..54ea76b 100644 --- a/src/components/editors/EditableColumnEditor/EditableColumnEditor.tsx +++ b/src/components/editors/EditableColumnEditor/EditableColumnEditor.tsx @@ -25,6 +25,7 @@ interface Props extends EditorProps { */ const columnEditorOptions = [ { value: ColumnEditorType.DATETIME, label: 'Date Time' }, + { value: ColumnEditorType.BOOLEAN, label: 'Boolean' }, { value: ColumnEditorType.NUMBER, label: 'Number' }, { value: ColumnEditorType.SELECT, label: 'Select' }, { value: ColumnEditorType.STRING, label: 'String' }, diff --git a/src/components/editors/EditableColumnEditor/EditableColumnEditorsRegistry.test.tsx b/src/components/editors/EditableColumnEditor/EditableColumnEditorsRegistry.test.tsx index 500f3ab..1c1f32d 100644 --- a/src/components/editors/EditableColumnEditor/EditableColumnEditorsRegistry.test.tsx +++ b/src/components/editors/EditableColumnEditor/EditableColumnEditorsRegistry.test.tsx @@ -498,4 +498,33 @@ describe('editableColumnEditorsRegistry', () => { expect(onChange).toHaveBeenCalledWith('line\\nline-2\\nline-3'); }); }); + + describe('Boolean', () => { + it('Should render editor', () => { + render( + getEditorComponent({ value: createColumnEditConfig({ editor: { type: ColumnEditorType.BOOLEAN } }).editor }) + ); + + /** + * Boolean doesn't have editor yet + */ + expect(true).toBeTruthy(); + }); + + it('Should render control', () => { + render( + getControlComponent({ + value: false, + config: createColumnEditConfig({ editor: { type: ColumnEditorType.BOOLEAN } }).editor, + }) + ); + + expect(controlSelectors.fieldBoolean()).toBeInTheDocument(); + expect(controlSelectors.fieldBoolean()).not.toBeChecked(); + + fireEvent.click(controlSelectors.fieldBoolean()); + + expect(onChange).toHaveBeenCalledWith(true); + }); + }); }); diff --git a/src/components/editors/EditableColumnEditor/EditableColumnEditorsRegistry.tsx b/src/components/editors/EditableColumnEditor/EditableColumnEditorsRegistry.tsx index 622ad9c..46c246b 100644 --- a/src/components/editors/EditableColumnEditor/EditableColumnEditorsRegistry.tsx +++ b/src/components/editors/EditableColumnEditor/EditableColumnEditorsRegistry.tsx @@ -1,5 +1,5 @@ import { dateTime } from '@grafana/data'; -import { DateTimePicker, InlineField, InlineFieldRow, Input, Select, TextArea } from '@grafana/ui'; +import { DateTimePicker, InlineField, InlineFieldRow, InlineSwitch, Input, Select, TextArea } from '@grafana/ui'; import { NumberInput } from '@volkovlabs/components'; import React, { ChangeEvent } from 'react'; @@ -34,6 +34,23 @@ export const editableColumnEditorsRegistry = createEditableColumnEditorsRegistry ), getControlOptions: (params) => params.config, }), + createEditableColumnEditorRegistryItem({ + id: ColumnEditorType.BOOLEAN, + editor: () => null, + control: ({ value, onChange, isSaving }) => { + return ( + { + onChange(event.currentTarget.checked); + }} + disabled={isSaving} + value={value as boolean} + {...TEST_IDS.editableCell.fieldBoolean.apply()} + /> + ); + }, + getControlOptions: (params) => params.config, + }), createEditableColumnEditorRegistryItem({ id: ColumnEditorType.TEXTAREA, editor: () => null, diff --git a/src/components/editors/TablesEditor/components/TableEditor/components/ColumnsEditor/components/ColumnEditor/ColumnEditor.tsx b/src/components/editors/TablesEditor/components/TableEditor/components/ColumnsEditor/components/ColumnEditor/ColumnEditor.tsx index 367a205..9066ea3 100644 --- a/src/components/editors/TablesEditor/components/TableEditor/components/ColumnsEditor/components/ColumnEditor/ColumnEditor.tsx +++ b/src/components/editors/TablesEditor/components/TableEditor/components/ColumnsEditor/components/ColumnEditor/ColumnEditor.tsx @@ -60,6 +60,10 @@ const cellTypeOptions = [ value: CellType.AUTO, label: 'Auto', }, + { + value: CellType.BOOLEAN, + label: 'Boolean', + }, { value: CellType.COLORED_BACKGROUND, label: 'Colored background', diff --git a/src/constants.ts b/src/constants.ts index 6ef8073..1639cb6 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -119,6 +119,9 @@ export const TEST_IDS = { layoutCellRenderer: { root: createSelector('data-testid layout-cell-renderer'), }, + booleanCellRenderer: { + root: createSelector('data-testid boolean-cell-renderer'), + }, aggregatedCellRenderer: { root: createSelector('data-testid aggregated-cell-renderer'), }, @@ -198,6 +201,7 @@ export const TEST_IDS = { fieldDatetime: createSelector('data-testid editableCell field-datetime'), fieldSelect: createSelector('data-testid editableCell field-select'), fieldTextarea: createSelector('data-testid editableCell field-textarea'), + fieldBoolean: createSelector('data-testid editableCell field-boolean'), }, tableActionsCell: { buttonStartEdit: createSelector('data-testid table-actions-cell button-start-edit'), diff --git a/src/types/column-editor.ts b/src/types/column-editor.ts index 61172d5..eb93a08 100644 --- a/src/types/column-editor.ts +++ b/src/types/column-editor.ts @@ -11,6 +11,7 @@ export enum ColumnEditorType { SELECT = 'select', DATETIME = 'datetime', TEXTAREA = 'textarea', + BOOLEAN = 'boolean', } /** @@ -92,6 +93,7 @@ interface EditorDatetimeOptions { export type ColumnEditorConfig = | { type: ColumnEditorType.STRING } | { type: ColumnEditorType.TEXTAREA } + | { type: ColumnEditorType.BOOLEAN } | ({ type: ColumnEditorType.NUMBER } & EditorNumberOptions) | ({ type: ColumnEditorType.SELECT } & EditorSelectOptions) | ({ type: ColumnEditorType.DATETIME } & EditorDatetimeOptions); @@ -103,6 +105,9 @@ export type ColumnEditorControlOptions = | { type: ColumnEditorType.STRING; } + | { + type: ColumnEditorType.BOOLEAN; + } | { type: ColumnEditorType.TEXTAREA } | ({ type: ColumnEditorType.NUMBER } & EditorNumberOptions) | ({ type: ColumnEditorType.DATETIME } & EditorDatetimeOptions) diff --git a/src/types/table.ts b/src/types/table.ts index d96e2cf..7976a32 100644 --- a/src/types/table.ts +++ b/src/types/table.ts @@ -10,6 +10,7 @@ export enum CellType { COLORED_BACKGROUND = 'coloredBackground', RICH_TEXT = 'rich_text', NESTED_OBJECTS = 'nested_objects', + BOOLEAN = 'boolean', } /** From 50a759cdaa096c7f9afbb17f4044ec6f1c2fe1ac Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Wed, 20 Nov 2024 13:10:34 +0300 Subject: [PATCH 2/7] rename label to switch --- .../editors/EditableColumnEditor/EditableColumnEditor.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/editors/EditableColumnEditor/EditableColumnEditor.tsx b/src/components/editors/EditableColumnEditor/EditableColumnEditor.tsx index 54ea76b..16a1b27 100644 --- a/src/components/editors/EditableColumnEditor/EditableColumnEditor.tsx +++ b/src/components/editors/EditableColumnEditor/EditableColumnEditor.tsx @@ -25,7 +25,7 @@ interface Props extends EditorProps { */ const columnEditorOptions = [ { value: ColumnEditorType.DATETIME, label: 'Date Time' }, - { value: ColumnEditorType.BOOLEAN, label: 'Boolean' }, + { value: ColumnEditorType.BOOLEAN, label: 'Switch' }, { value: ColumnEditorType.NUMBER, label: 'Number' }, { value: ColumnEditorType.SELECT, label: 'Select' }, { value: ColumnEditorType.STRING, label: 'String' }, From 70496952e2815df408e06e2ca073c308b8120996 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Wed, 20 Nov 2024 13:15:12 +0300 Subject: [PATCH 3/7] build fix --- src/components/Table/components/CellRenderer/CellRenderer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Table/components/CellRenderer/CellRenderer.tsx b/src/components/Table/components/CellRenderer/CellRenderer.tsx index afebd52..a3dc54c 100644 --- a/src/components/Table/components/CellRenderer/CellRenderer.tsx +++ b/src/components/Table/components/CellRenderer/CellRenderer.tsx @@ -46,7 +46,7 @@ export const CellRenderer: React.FC = ({ renderValue, column, bgColor, ro return ; } case CellType.BOOLEAN: { - return ; + return ; } default: { return ; From 0647cace71fd152237704689bc54e501eb59b121 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Wed, 20 Nov 2024 13:27:06 +0300 Subject: [PATCH 4/7] provisioning upd --- provisioning/dashboards/panels.json | 475 +++++++++++++++++++++++++++- 1 file changed, 465 insertions(+), 10 deletions(-) diff --git a/provisioning/dashboards/panels.json b/provisioning/dashboards/panels.json index bb6408a..72d3756 100644 --- a/provisioning/dashboards/panels.json +++ b/provisioning/dashboards/panels.json @@ -24,7 +24,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 4, + "id": 5, "links": [], "panels": [ { @@ -320,6 +320,7 @@ "enabled": false, "mode": "client" }, + "showHeader": true, "update": { "datasource": "", "payload": {} @@ -636,6 +637,7 @@ "enabled": false, "mode": "client" }, + "showHeader": true, "update": { "datasource": "", "payload": {} @@ -747,6 +749,7 @@ "enabled": false, "mode": "client" }, + "showHeader": true, "update": { "datasource": "", "payload": {} @@ -822,6 +825,457 @@ ], "type": "volkovlabs-table-panel" }, + { + "datasource": { + "default": true, + "type": "marcusolsson-static-datasource", + "uid": "P1D2C73DC01F2359B" + }, + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 14, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 14, + "options": { + "columnMode": "auto", + "columns": [], + "nestedObjects": [], + "tables": [ + { + "items": [ + { + "appearance": { + "alignment": "end", + "background": { + "applyToRow": false + }, + "header": { + "fontSize": "md" + }, + "width": { + "auto": true, + "max": 50, + "value": 100 + }, + "wrap": true + }, + "edit": { + "editor": { + "type": "string" + }, + "enabled": false, + "permission": { + "field": { + "name": "", + "source": "" + }, + "mode": "", + "userRole": [] + } + }, + "enabled": true, + "field": { + "name": "id", + "source": "A" + }, + "filter": { + "enabled": false, + "mode": "client", + "variable": "" + }, + "footer": [], + "group": false, + "label": "", + "pin": "left", + "sort": { + "descFirst": false, + "enabled": true + }, + "type": "coloredBackground" + }, + { + "appearance": { + "alignment": "start", + "background": { + "applyToRow": false + }, + "header": { + "fontSize": "md" + }, + "width": { + "auto": true, + "min": 200, + "value": 100 + }, + "wrap": true + }, + "edit": { + "editor": { + "type": "string" + }, + "enabled": false, + "permission": { + "field": { + "name": "", + "source": "" + }, + "mode": "", + "userRole": [] + } + }, + "enabled": true, + "field": { + "name": "name", + "source": "A" + }, + "filter": { + "enabled": true, + "mode": "client", + "variable": "" + }, + "footer": [], + "group": false, + "label": "", + "pin": "", + "sort": { + "descFirst": true, + "enabled": true + }, + "type": "auto" + }, + { + "appearance": { + "alignment": "start", + "background": { + "applyToRow": false + }, + "header": { + "fontSize": "md" + }, + "width": { + "auto": true, + "min": 100, + "value": 100 + }, + "wrap": true + }, + "edit": { + "editor": { + "type": "string" + }, + "enabled": false, + "permission": { + "field": { + "name": "", + "source": "" + }, + "mode": "", + "userRole": [] + } + }, + "enabled": true, + "field": { + "name": "value", + "source": "A" + }, + "filter": { + "enabled": true, + "mode": "client", + "variable": "" + }, + "footer": ["mean"], + "group": false, + "label": "", + "pin": "", + "sort": { + "descFirst": false, + "enabled": false + }, + "type": "auto" + }, + { + "aggregation": "none", + "appearance": { + "alignment": "start", + "background": { + "applyToRow": false + }, + "colors": {}, + "header": { + "fontSize": "md" + }, + "width": { + "auto": true, + "min": 20, + "value": 100 + }, + "wrap": true + }, + "edit": { + "editor": { + "type": "string" + }, + "enabled": true, + "permission": { + "mode": "", + "userRole": [] + } + }, + "enabled": true, + "field": { + "name": "enable", + "source": "A" + }, + "filter": { + "enabled": false, + "mode": "client", + "variable": "" + }, + "footer": [], + "group": false, + "label": "", + "objectId": "", + "pin": "", + "sort": { + "descFirst": false, + "enabled": false + }, + "type": "boolean" + }, + { + "appearance": { + "alignment": "start", + "background": { + "applyToRow": false + }, + "header": { + "fontSize": "md" + }, + "width": { + "auto": true, + "min": 200, + "value": 100 + }, + "wrap": true + }, + "edit": { + "editor": { + "type": "string" + }, + "enabled": false, + "permission": { + "field": { + "name": "", + "source": "" + }, + "mode": "", + "userRole": [] + } + }, + "enabled": true, + "field": { + "name": "json", + "source": "A" + }, + "filter": { + "enabled": false, + "mode": "client", + "variable": "" + }, + "footer": [], + "group": false, + "label": "", + "pin": "", + "sort": { + "descFirst": false, + "enabled": false + }, + "type": "auto" + }, + { + "aggregation": "none", + "appearance": { + "alignment": "start", + "background": { + "applyToRow": false + }, + "header": { + "fontSize": "md" + }, + "width": { + "auto": false, + "min": 100, + "value": 150 + }, + "wrap": true + }, + "edit": { + "editor": { + "type": "string" + }, + "enabled": false, + "permission": { + "mode": "", + "userRole": [] + } + }, + "enabled": true, + "field": { + "name": "comment.info.name", + "source": "A" + }, + "filter": { + "enabled": false, + "mode": "client", + "variable": "" + }, + "footer": [], + "group": false, + "label": "", + "objectId": "", + "pin": "right", + "sort": { + "descFirst": false, + "enabled": false + }, + "type": "auto" + } + ], + "name": "Main", + "pagination": { + "enabled": false, + "mode": "client" + }, + "showHeader": true, + "update": { + "datasource": "timescale", + "payload": { + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "UPDATE\n devices2\nSET\n enable = ${payload.enable}\nWHERE\n id = ${payload.id}", + "refId": "A", + "sql": { + "columns": [ + { + "parameters": [], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + } + } + } + } + ], + "tabsSorting": false, + "toolbar": { + "export": true + } + }, + "targets": [ + { + "datasource": { + "type": "marcusolsson-static-datasource", + "uid": "P1D2C73DC01F2359B" + }, + "frame": { + "fields": [ + { + "config": {}, + "name": "id", + "type": "number", + "values": [1, 2, 3] + }, + { + "config": {}, + "name": "name", + "type": "string", + "values": ["DeviceWithVeryLongTitle", "Device 2", "Device 3"] + }, + { + "config": {}, + "name": "value", + "type": "number", + "values": [10, 15, 20] + }, + { + "config": {}, + "name": "json", + "type": "string", + "values": ["{ \"name\": \"Device1\" }", "{ \"name\": \"Device2\" }", "{ \"name\": \"Device3\" }"] + }, + { + "config": {}, + "name": "comment.info.name", + "type": "string", + "values": ["Some comment", "Some notes", ""] + }, + { + "config": {}, + "name": "enable", + "type": "boolean", + "values": [null, null, null] + } + ], + "meta": { + "custom": { + "customCode": "const result = {\n ...frame,\n fields: frame.fields.map((field) => {\n\n if (field.name === 'id') {\n return ({\n ...field,\n values: [1, 2, 3]\n })\n }\n\n if (field.name === 'name') {\n return ({\n ...field,\n values: ['Name - 1', 'Name - 2', 'Name - 3']\n })\n }\n\n if (field.name === 'value') {\n return ({\n ...field,\n values: [10, 15, 20]\n })\n }\n\n if (field.name === 'json') {\n return ({\n ...field,\n values: ['{ \"name\": \"Device1\" }', '{ \"name\": \"Device2\" }', '{ \"name\": \"Device3\" }']\n })\n }\n\n if (field.name === 'enable') {\n return ({\n ...field,\n values: [true, false, false]\n })\n }\n\n return ({\n ...field,\n values: []\n })\n })\n}\n\nreturn Promise.resolve(result);", + "valuesEditor": "custom" + } + }, + "name": "data" + }, + "refId": "A" + } + ], + "title": "Table with boolean", + "transformations": [ + { + "id": "organize", + "options": { + "excludeByName": {}, + "includeByName": {}, + "indexByName": {}, + "renameByName": { + "id": "", + "name": "Name", + "value": "Value" + } + } + } + ], + "type": "volkovlabs-table-panel" + }, { "datasource": { "type": "marcusolsson-static-datasource", @@ -853,7 +1307,7 @@ "h": 10, "w": 24, "x": 0, - "y": 13 + "y": 27 }, "id": 12, "options": { @@ -1063,6 +1517,7 @@ "enabled": false, "mode": "client" }, + "showHeader": true, "update": { "datasource": "", "payload": {} @@ -1152,7 +1607,7 @@ "h": 13, "w": 24, "x": 0, - "y": 23 + "y": 37 }, "id": 11, "options": { @@ -1359,6 +1814,7 @@ "enabled": false, "mode": "client" }, + "showHeader": true, "update": { "datasource": "", "payload": {} @@ -1470,6 +1926,7 @@ "enabled": false, "mode": "client" }, + "showHeader": true, "update": { "datasource": "", "payload": {} @@ -1558,8 +2015,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1574,7 +2030,7 @@ "h": 13, "w": 24, "x": 0, - "y": 36 + "y": 50 }, "id": 13, "options": { @@ -1822,8 +2278,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1838,7 +2293,7 @@ "h": 8, "w": 24, "x": 0, - "y": 49 + "y": 63 }, "id": 8, "options": { @@ -2047,6 +2502,6 @@ "timezone": "", "title": "Panels", "uid": "O4tc_E6Gz", - "version": 1, + "version": 6, "weekStart": "" } From b582902ae995eb5135d67c7ad23b8056f1ae4b02 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Wed, 20 Nov 2024 13:53:35 +0300 Subject: [PATCH 5/7] upd editor type --- provisioning/dashboards/panels.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provisioning/dashboards/panels.json b/provisioning/dashboards/panels.json index 72d3756..66268ac 100644 --- a/provisioning/dashboards/panels.json +++ b/provisioning/dashboards/panels.json @@ -1034,7 +1034,7 @@ }, "edit": { "editor": { - "type": "string" + "type": "boolean" }, "enabled": true, "permission": { From b5dc996b18d98b451bb05ec8bc313b30555787b5 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Wed, 20 Nov 2024 16:37:41 +0300 Subject: [PATCH 6/7] update boolean type to icon display --- .../CellRenderer/BooleanCellRenderer.test.tsx | 31 ++++++++++++++++--- .../CellRenderer/BooleanCellRenderer.tsx | 28 +++++++++++++++-- .../components/CellRenderer/CellRenderer.tsx | 2 +- src/constants.ts | 2 +- 4 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx b/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx index 169686e..b5425d2 100644 --- a/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx +++ b/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx @@ -8,11 +8,21 @@ import { BooleanCellRenderer } from './BooleanCellRenderer'; type Props = React.ComponentProps; +/** + * Mock @grafana/ui for Component to heck bgColor and color for cell + */ +jest.mock('@grafana/ui', () => ({ + ...jest.requireActual('@grafana/ui'), + Icon: (props: any) => { + return {props.name}; + }, +})); + describe('BooleanCellRenderer', () => { /** * Selectors */ - const getSelectors = getJestSelectors(TEST_IDS.booleanCellRenderer); + const getSelectors = getJestSelectors(TEST_IDS.booleanCellRenderer, ['icon']); const selectors = getSelectors(screen); /** @@ -22,10 +32,23 @@ describe('BooleanCellRenderer', () => { return ; }; - it('Should render value', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('Should render false value', () => { render(getComponent({ value: false })); + expect(selectors.icon(false, 'circle')).toBeInTheDocument(); + }); + + it('Should render true value', () => { + render(getComponent({ value: true, bgColor: '#000000' })); + expect(selectors.icon(false, 'check-circle')).toBeInTheDocument(); + }); - expect(selectors.root()).toBeInTheDocument(); - expect(selectors.root()).toBeDisabled(); + it('Should render value with contrast color', () => { + render(getComponent({ value: true, bgColor: '#E02F44' })); + expect(selectors.icon(false, 'check-circle')).toBeInTheDocument(); + expect(selectors.icon(false, 'check-circle')).toHaveStyle({ color: 'rgb(255, 255, 255)' }); }); }); diff --git a/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx b/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx index bd4ab64..9ea2d65 100644 --- a/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx +++ b/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx @@ -1,4 +1,4 @@ -import { InlineSwitch } from '@grafana/ui'; +import { Icon, useTheme2 } from '@grafana/ui'; import React from 'react'; import { TEST_IDS } from '@/constants'; @@ -13,13 +13,35 @@ interface Props { * @type {boolean} */ value: boolean; + + /** + * Bg Color + * + * @type {string} + */ + bgColor?: string; } /** * Boolean Cell Renderer * @param value + * @param bgColor * @constructor */ -export const BooleanCellRenderer: React.FC = ({ value }) => { - return ; +export const BooleanCellRenderer: React.FC = ({ value, bgColor }) => { + /** + * Theme + */ + const theme = useTheme2(); + + return ( + + ); }; diff --git a/src/components/Table/components/CellRenderer/CellRenderer.tsx b/src/components/Table/components/CellRenderer/CellRenderer.tsx index a3dc54c..a6e9b9f 100644 --- a/src/components/Table/components/CellRenderer/CellRenderer.tsx +++ b/src/components/Table/components/CellRenderer/CellRenderer.tsx @@ -46,7 +46,7 @@ export const CellRenderer: React.FC = ({ renderValue, column, bgColor, ro return ; } case CellType.BOOLEAN: { - return ; + return ; } default: { return ; diff --git a/src/constants.ts b/src/constants.ts index 1639cb6..838516a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -120,7 +120,7 @@ export const TEST_IDS = { root: createSelector('data-testid layout-cell-renderer'), }, booleanCellRenderer: { - root: createSelector('data-testid boolean-cell-renderer'), + icon: createSelector((name: unknown) => String(name), 'data-testid'), }, aggregatedCellRenderer: { root: createSelector('data-testid aggregated-cell-renderer'), From 56093a5c016cb873b5fa9aa48253e096b6a93c15 Mon Sep 17 00:00:00 2001 From: vitPinchuk Date: Wed, 20 Nov 2024 17:11:05 +0300 Subject: [PATCH 7/7] update PR request --- .../CellRenderer/BooleanCellRenderer.test.tsx | 31 +++++++++++++------ .../CellRenderer/BooleanCellRenderer.tsx | 2 +- src/constants.ts | 2 +- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx b/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx index b5425d2..c157c65 100644 --- a/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx +++ b/src/components/Table/components/CellRenderer/BooleanCellRenderer.test.tsx @@ -1,3 +1,4 @@ +import { Icon } from '@grafana/ui'; import { render, screen } from '@testing-library/react'; import { getJestSelectors } from '@volkovlabs/jest-selectors'; import React from 'react'; @@ -8,21 +9,24 @@ import { BooleanCellRenderer } from './BooleanCellRenderer'; type Props = React.ComponentProps; +/** + * Mock Icon + */ +const IconMock = ({ name, ...restProps }: any) => {name}; + /** * Mock @grafana/ui for Component to heck bgColor and color for cell */ jest.mock('@grafana/ui', () => ({ ...jest.requireActual('@grafana/ui'), - Icon: (props: any) => { - return {props.name}; - }, + Icon: jest.fn(), })); describe('BooleanCellRenderer', () => { /** * Selectors */ - const getSelectors = getJestSelectors(TEST_IDS.booleanCellRenderer, ['icon']); + const getSelectors = getJestSelectors(TEST_IDS.booleanCellRenderer); const selectors = getSelectors(screen); /** @@ -33,22 +37,31 @@ describe('BooleanCellRenderer', () => { }; beforeEach(() => { + jest.mocked(Icon).mockImplementation(IconMock); + }); + + afterEach(() => { jest.clearAllMocks(); }); it('Should render false value', () => { render(getComponent({ value: false })); - expect(selectors.icon(false, 'circle')).toBeInTheDocument(); + expect(selectors.root()).toBeInTheDocument(); + expect(selectors.root()).toHaveTextContent('circle'); + expect(selectors.root()).toHaveStyle({ color: 'inherit' }); }); it('Should render true value', () => { - render(getComponent({ value: true, bgColor: '#000000' })); - expect(selectors.icon(false, 'check-circle')).toBeInTheDocument(); + render(getComponent({ value: true, bgColor: '#ffffff' })); + expect(selectors.root()).toBeInTheDocument(); + expect(selectors.root()).toHaveTextContent('check-circle'); + expect(selectors.root()).toHaveStyle({ color: 'rgb(0, 0, 0)' }); }); it('Should render value with contrast color', () => { render(getComponent({ value: true, bgColor: '#E02F44' })); - expect(selectors.icon(false, 'check-circle')).toBeInTheDocument(); - expect(selectors.icon(false, 'check-circle')).toHaveStyle({ color: 'rgb(255, 255, 255)' }); + expect(selectors.root()).toBeInTheDocument(); + expect(selectors.root()).toHaveTextContent('check-circle'); + expect(selectors.root()).toHaveStyle({ color: 'rgb(255, 255, 255)' }); }); }); diff --git a/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx b/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx index 9ea2d65..db0b7ae 100644 --- a/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx +++ b/src/components/Table/components/CellRenderer/BooleanCellRenderer.tsx @@ -36,7 +36,7 @@ export const BooleanCellRenderer: React.FC = ({ value, bgColor }) => { return ( String(name), 'data-testid'), + root: createSelector('data-testid boolean-cell-renderer'), }, aggregatedCellRenderer: { root: createSelector('data-testid aggregated-cell-renderer'),