diff --git a/.eslintignore b/.eslintignore index 4f91b76..79228c0 100644 --- a/.eslintignore +++ b/.eslintignore @@ -12,3 +12,6 @@ dist/ **/*.test.ts **/*.test.tsx __mocks__ + +# Declarations +**/*.d.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 13e79e7..d69f0fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## 4.3.0 (IN PROGRESS) + +### Features / Enhancements + +- Update ESLint configuration and refactor (#239) +- Update Collapse from @volkovlabs/components (#239) + ## 4.2.0 (2023-11-20) ### Features / Enhancements diff --git a/cypress/integration/01-view-panel.test.ts b/cypress/integration/01-view-panel.test.ts index 30d8d8d..41f86ba 100644 --- a/cypress/integration/01-view-panel.test.ts +++ b/cypress/integration/01-view-panel.test.ts @@ -1,5 +1,5 @@ import { e2e } from '@grafana/e2e'; -import { TestIds } from '../../src/constants'; +import { TEST_IDS } from '../../src/constants'; /** * Dashboard @@ -29,7 +29,7 @@ describe('Viewing a panel with Dynamic Text', () => { /** * Chart */ - const chart = currentPanel.find(getTestIdSelector(TestIds.panel.root)); + const chart = currentPanel.find(getTestIdSelector(TEST_IDS.panel.root)); chart.should('be.visible'); /** diff --git a/package-lock.json b/package-lock.json index cbe9fcd..8eb1054 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "marcusolsson-dynamictext-panel", - "version": "4.2.0", + "version": "4.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "marcusolsson-dynamictext-panel", - "version": "4.2.0", + "version": "4.3.0", "license": "Apache-2.0", "dependencies": { "@emotion/css": "^11.11.2", @@ -45,7 +45,8 @@ "@types/node": "^18.18.10", "@types/react-beautiful-dnd": "^13.1.7", "@types/uuid": "^9.0.7", - "@volkovlabs/eslint-config": "^1.1.0", + "@volkovlabs/components": "^1.1.0", + "@volkovlabs/eslint-config": "^1.2.1", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.8.1", "eslint-webpack-plugin": "^4.0.1", @@ -6540,10 +6541,25 @@ "dev": true, "peer": true }, - "node_modules/@volkovlabs/eslint-config": { + "node_modules/@volkovlabs/components": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@volkovlabs/eslint-config/-/eslint-config-1.1.0.tgz", - "integrity": "sha512-yYXJkayS6rUoJzXHnthvViXLbCaLHWBcMHtrlonJ0kqME7A0VDYaqRbNJu+5+Mj8Czim4jxcUWKw1LWNFTECig==", + "resolved": "https://registry.npmjs.org/@volkovlabs/components/-/components-1.1.0.tgz", + "integrity": "sha512-pNTfieF4pYvyRfvhO0tvkM5FK0b19w6jhjdMgbviwPdMLD7HlIj/DVmOjW5d9v2s6arIvz03LylHpACUD4Iy0Q==", + "dev": true, + "dependencies": { + "@emotion/css": "^11.11.2", + "@grafana/data": "^10.2.1", + "@grafana/ui": "^10.2.1" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@volkovlabs/eslint-config": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@volkovlabs/eslint-config/-/eslint-config-1.2.1.tgz", + "integrity": "sha512-O/0kwVIlmWkqQpCgZQ4ZwzUBOabYTZEa0VXlXwDXjq2ACpmyCFNBpupyudbSEiwdjJ18skf5EAG3sLTs3HYUGA==", "dev": true, "dependencies": { "@typescript-eslint/eslint-plugin": "^6.0.0", diff --git a/package.json b/package.json index 4b65513..53a67fa 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,8 @@ "@types/node": "^18.18.10", "@types/react-beautiful-dnd": "^13.1.7", "@types/uuid": "^9.0.7", - "@volkovlabs/eslint-config": "^1.1.0", + "@volkovlabs/components": "^1.1.0", + "@volkovlabs/eslint-config": "^1.2.1", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.8.1", "eslint-webpack-plugin": "^4.0.1", @@ -80,5 +81,5 @@ "test:ci": "jest --maxWorkers 4 --coverage", "upgrade": "npm upgrade --save" }, - "version": "4.2.0" + "version": "4.3.0" } diff --git a/src/components/Collapse/Collapse.test.tsx b/src/components/Collapse/Collapse.test.tsx deleted file mode 100644 index 10bfd30..0000000 --- a/src/components/Collapse/Collapse.test.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { fireEvent, render, screen } from '@testing-library/react'; -import React from 'react'; - -import { Collapse } from './Collapse'; - -type Props = React.ComponentProps; - -/** - * In Test Ids - */ -const InTestIds = { - header: 'data-testid header', - content: 'data-testid content', - buttonRemove: 'data-testid button-remove', -}; - -describe('Collapse', () => { - /** - * Get Tested Component - */ - const getComponent = (props: Partial) => { - return ; - }; - - it('Should expand content', () => { - const { rerender } = render(getComponent({ isOpen: false })); - - expect(screen.queryByTestId(InTestIds.content)).not.toBeInTheDocument(); - - rerender(getComponent({ isOpen: true })); - - expect(screen.getByTestId(InTestIds.content)).toBeInTheDocument(); - }); - - it('Actions should not affect collapse state', () => { - const onToggle = jest.fn(); - - render(getComponent({ onToggle, actions: })); - - fireEvent.click(screen.getByTestId(InTestIds.buttonRemove)); - - expect(onToggle).not.toHaveBeenCalled(); - }); -}); diff --git a/src/components/Collapse/Collapse.tsx b/src/components/Collapse/Collapse.tsx deleted file mode 100644 index ded6e05..0000000 --- a/src/components/Collapse/Collapse.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { IconButton, useTheme2 } from '@grafana/ui'; -import React from 'react'; - -import { Styles } from './styles'; - -/** - * Properties - */ -interface Props { - /** - * Title - */ - title?: React.ReactElement | string; - - /** - * Actions - */ - actions?: React.ReactElement; - - /** - * Children - */ - children?: React.ReactElement | string; - - /** - * Is Open? - */ - isOpen?: boolean; - - /** - * On Toggle - */ - onToggle?: () => void; - - /** - * Header Test Id - */ - headerTestId?: string; - - /** - * Content Test Id - */ - contentTestId?: string; -} - -/** - * Collapse - */ -export const Collapse: React.FC = ({ - title, - actions, - children, - isOpen = false, - onToggle, - headerTestId, - contentTestId, -}) => { - /** - * Styles and Theme - */ - const theme = useTheme2(); - const styles = Styles(theme); - - return ( -
-
- -
{title}
- {actions && ( -
event.stopPropagation()}> - {actions} -
- )} -
- {isOpen && ( -
- {children} -
- )} -
- ); -}; diff --git a/src/components/Collapse/index.ts b/src/components/Collapse/index.ts deleted file mode 100644 index 5fda998..0000000 --- a/src/components/Collapse/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './Collapse'; diff --git a/src/components/Collapse/styles.ts b/src/components/Collapse/styles.ts deleted file mode 100644 index c8d7656..0000000 --- a/src/components/Collapse/styles.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { css } from '@emotion/css'; -import { GrafanaTheme2 } from '@grafana/data'; - -/** - * Styles - */ -export const Styles = (theme: GrafanaTheme2) => { - return { - root: css` - margin-bottom: ${theme.spacing(1)}; - `, - header: css` - label: Header; - padding: ${theme.spacing(0.5, 0.5)}; - border-radius: ${theme.shape.radius.default}; - background: ${theme.colors.background.secondary}; - min-height: ${theme.spacing(4)}; - display: flex; - align-items: center; - justify-content: space-between; - white-space: nowrap; - - &:focus { - outline: none; - } - `, - title: css` - font-weight: ${theme.typography.fontWeightBold}; - margin-left: ${theme.spacing(0.5)}; - overflow: hidden; - text-overflow: ellipsis; - `, - collapseIcon: css` - margin-left: ${theme.spacing(0.5)}; - color: ${theme.colors.text.disabled}; - `, - actions: css` - margin-left: auto; - display: flex; - align-items: center; - `, - content: css` - margin-top: ${theme.spacing(0.5)}; - margin-left: ${theme.spacing(0.5)}; - `, - }; -}; diff --git a/src/components/CustomEditor/CustomEditor.tsx b/src/components/CustomEditor/CustomEditor.tsx index 052a427..9b91294 100644 --- a/src/components/CustomEditor/CustomEditor.tsx +++ b/src/components/CustomEditor/CustomEditor.tsx @@ -7,7 +7,7 @@ import { CodeEditor, CodeEditorSuggestionItem, CodeEditorSuggestionItemKind } fr import type * as monacoType from 'monaco-editor/esm/vs/editor/editor.api'; import React, { useCallback, useMemo } from 'react'; -import { CodeLanguage, EditorType, Format, HelpersEditorSuggestions, TestIds } from '../../constants'; +import { CodeLanguage, EditorType, Format, HELPERS_EDITOR_SUGGESTIONS, TEST_IDS } from '../../constants'; /** * Properties @@ -64,7 +64,7 @@ export const CustomEditor: React.FC = ({ value, onChange, context, type = return suggestions; } - return HelpersEditorSuggestions.concat(suggestions); + return HELPERS_EDITOR_SUGGESTIONS.concat(suggestions); }, [templateSrv, type]); /** @@ -92,7 +92,7 @@ export const CustomEditor: React.FC = ({ value, onChange, context, type = }, [context.options.editor.language, type]); return ( -
+
{ it('Should find component', async () => { render(getComponent({})); - expect(screen.getByTestId(TestIds.textEditor.root)).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.textEditor.root)).toBeInTheDocument(); }); it('Should show mini map if value more than 100 symbols', () => { @@ -204,7 +204,7 @@ describe('Custom Editor', () => { () => ({ getVariables: jest.fn().mockImplementation(() => variables), - } as any) + }) as any ); render(); @@ -244,12 +244,12 @@ describe('Custom Editor', () => { () => ({ getVariables: jest.fn().mockImplementation(() => variables), - } as any) + }) as any ); render(); - expect(suggestionsResult).toEqual(expect.arrayContaining(HelpersEditorSuggestions)); + expect(suggestionsResult).toEqual(expect.arrayContaining(HELPERS_EDITOR_SUGGESTIONS)); expect(suggestionsResult).toEqual( expect.arrayContaining([ { @@ -302,7 +302,7 @@ describe('Custom Editor', () => { () => ({ getVariables: jest.fn().mockImplementation(() => variables), - } as any) + }) as any ); render(); diff --git a/src/components/ResourcesEditor/styles.ts b/src/components/ResourcesEditor/ResourceEditor.styles.ts similarity index 91% rename from src/components/ResourcesEditor/styles.ts rename to src/components/ResourcesEditor/ResourceEditor.styles.ts index 4d91526..f6904d1 100644 --- a/src/components/ResourcesEditor/styles.ts +++ b/src/components/ResourcesEditor/ResourceEditor.styles.ts @@ -4,7 +4,7 @@ import { GrafanaTheme2 } from '@grafana/data'; /** * Styles */ -export const Styles = (theme: GrafanaTheme2) => { +export const getStyles = (theme: GrafanaTheme2) => { return { newGroup: css` margin: ${theme.spacing(2)} 0; diff --git a/src/components/ResourcesEditor/ResourcesEditor.test.tsx b/src/components/ResourcesEditor/ResourcesEditor.test.tsx index 265c91e..38ef9b3 100644 --- a/src/components/ResourcesEditor/ResourcesEditor.test.tsx +++ b/src/components/ResourcesEditor/ResourcesEditor.test.tsx @@ -2,7 +2,7 @@ import { act, fireEvent, render, screen, within } from '@testing-library/react'; import React from 'react'; import { DragDropContext, DropResult } from 'react-beautiful-dnd'; -import { TestIds } from '../../constants'; +import { TEST_IDS } from '../../constants'; import { ResourcesEditor } from './ResourcesEditor'; /** @@ -50,8 +50,8 @@ describe('ResourcesEditor', () => { }) ); - expect(screen.getByTestId(TestIds.resourcesEditor.itemLabel('abc'))).toBeInTheDocument(); - expect(screen.getByTestId(TestIds.resourcesEditor.itemLabel('aaa'))).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.resourcesEditor.itemLabel('abc'))).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.resourcesEditor.itemLabel('aaa'))).toBeInTheDocument(); }); it('Should render component if no value', () => { @@ -61,7 +61,7 @@ describe('ResourcesEditor', () => { }) ); - expect(screen.getByTestId(TestIds.resourcesEditor.newItem)).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.resourcesEditor.newItem)).toBeInTheDocument(); }); it('Should add new item', async () => { @@ -84,13 +84,13 @@ describe('ResourcesEditor', () => { ); await act(() => - fireEvent.change(screen.getByTestId(TestIds.resourcesEditor.newItemName), { target: { value: 'bbb' } }) + fireEvent.change(screen.getByTestId(TEST_IDS.resourcesEditor.newItemName), { target: { value: 'bbb' } }) ); - expect(screen.getByTestId(TestIds.resourcesEditor.buttonAddNew)).toBeInTheDocument(); - expect(screen.getByTestId(TestIds.resourcesEditor.buttonAddNew)).not.toBeDisabled(); + expect(screen.getByTestId(TEST_IDS.resourcesEditor.buttonAddNew)).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.resourcesEditor.buttonAddNew)).not.toBeDisabled(); - await act(() => fireEvent.click(screen.getByTestId(TestIds.resourcesEditor.buttonAddNew))); + await act(() => fireEvent.click(screen.getByTestId(TEST_IDS.resourcesEditor.buttonAddNew))); expect(onChange).toHaveBeenCalledWith( expect.arrayContaining([ @@ -120,7 +120,7 @@ describe('ResourcesEditor', () => { }) ); - const itemLabel = screen.getByTestId(TestIds.resourcesEditor.itemLabel('aaa')); + const itemLabel = screen.getByTestId(TEST_IDS.resourcesEditor.itemLabel('aaa')); /** * Check item presence @@ -129,7 +129,7 @@ describe('ResourcesEditor', () => { await act(() => fireEvent.click(itemLabel)); - const itemContent = screen.getByTestId(TestIds.resourcesEditor.itemContent('aaa')); + const itemContent = screen.getByTestId(TEST_IDS.resourcesEditor.itemContent('aaa')); /** * Check content presence */ @@ -139,7 +139,7 @@ describe('ResourcesEditor', () => { * Change */ await act(() => - fireEvent.change(within(itemContent).getByTestId(TestIds.resourcesEditor.fieldUrl), { + fireEvent.change(within(itemContent).getByTestId(TEST_IDS.resourcesEditor.fieldUrl), { target: { value: 'aaa123' }, }) ); @@ -173,7 +173,7 @@ describe('ResourcesEditor', () => { }) ); - const item = screen.getByTestId(TestIds.resourcesEditor.itemLabel('aaa')); + const item = screen.getByTestId(TEST_IDS.resourcesEditor.itemLabel('aaa')); /** * Check item presence @@ -183,7 +183,7 @@ describe('ResourcesEditor', () => { /** * Remove */ - await act(() => fireEvent.click(within(item).getByTestId(TestIds.resourcesEditor.buttonRemove))); + await act(() => fireEvent.click(within(item).getByTestId(TEST_IDS.resourcesEditor.buttonRemove))); expect(onChange).toHaveBeenCalledWith([ { diff --git a/src/components/ResourcesEditor/ResourcesEditor.tsx b/src/components/ResourcesEditor/ResourcesEditor.tsx index 4cba199..59a122a 100644 --- a/src/components/ResourcesEditor/ResourcesEditor.tsx +++ b/src/components/ResourcesEditor/ResourcesEditor.tsx @@ -1,5 +1,6 @@ import { StandardEditorProps } from '@grafana/data'; -import { Button, Icon, InlineField, InlineFieldRow, Input, useTheme2 } from '@grafana/ui'; +import { Button, Icon, InlineField, InlineFieldRow, Input, useStyles2 } from '@grafana/ui'; +import { Collapse } from '@volkovlabs/components'; import React, { useCallback, useState } from 'react'; import { DragDropContext, @@ -11,10 +12,9 @@ import { } from 'react-beautiful-dnd'; import { v4 as uuidv4 } from 'uuid'; -import { TestIds } from '../../constants'; +import { TEST_IDS } from '../../constants'; import { PanelOptions, Resource } from '../../types'; -import { Collapse } from '../Collapse'; -import { Styles } from './styles'; +import { getStyles } from './ResourceEditor.styles'; /** * Properties @@ -52,8 +52,7 @@ export const ResourcesEditor: React.FC = ({ value, onChange }) => { /** * Styles and Theme */ - const theme = useTheme2(); - const styles = Styles(theme); + const styles = useStyles2(getStyles); /** * States @@ -146,8 +145,8 @@ export const ResourcesEditor: React.FC = ({ value, onChange }) => { > {url}
} - headerTestId={TestIds.resourcesEditor.itemLabel(url)} - contentTestId={TestIds.resourcesEditor.itemContent(url)} + headerTestId={TEST_IDS.resourcesEditor.itemLabel(url)} + contentTestId={TEST_IDS.resourcesEditor.itemContent(url)} actions={ <> diff --git a/src/components/Row/Row.tsx b/src/components/Row/Row.tsx index 0332091..3a82500 100644 --- a/src/components/Row/Row.tsx +++ b/src/components/Row/Row.tsx @@ -2,7 +2,7 @@ import { EventBus, InterpolateFunction } from '@grafana/data'; import { locationService } from '@grafana/runtime'; import React, { useEffect, useRef } from 'react'; -import { TestIds } from '../../constants'; +import { TEST_IDS } from '../../constants'; import { RowItem } from '../../types'; /** @@ -85,7 +85,7 @@ export const Row: React.FC = ({ className, item, afterRender, replaceVari ref={ref} className={className} dangerouslySetInnerHTML={{ __html: item.html }} - data-testid={TestIds.text.content} + data-testid={TEST_IDS.text.content} /> ); }; diff --git a/src/components/Text/Text.styles.ts b/src/components/Text/Text.styles.ts index 4e4e94f..12c3c83 100644 --- a/src/components/Text/Text.styles.ts +++ b/src/components/Text/Text.styles.ts @@ -1,12 +1,12 @@ import { css } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; -import { HighlightDark, HighlightLight } from '../../constants'; +import { HIGHLIGHT_DARK, HIGHLIGHT_LIGHT } from '../../constants'; /** * Styles */ -export const Styles = (theme: GrafanaTheme2) => { +export const getStyles = (theme: GrafanaTheme2) => { /** * Frame */ @@ -63,7 +63,7 @@ export const Styles = (theme: GrafanaTheme2) => { /** * Highlight */ - const highlight = theme.isDark ? HighlightDark : HighlightLight; + const highlight = theme.isDark ? HIGHLIGHT_DARK : HIGHLIGHT_LIGHT; return { frame, diff --git a/src/components/Text/Text.test.tsx b/src/components/Text/Text.test.tsx index 1714a29..d7e15d2 100644 --- a/src/components/Text/Text.test.tsx +++ b/src/components/Text/Text.test.tsx @@ -2,7 +2,7 @@ import { FieldType, toDataFrame } from '@grafana/data'; import { render, screen } from '@testing-library/react'; import React from 'react'; -import { DefaultOptions, TestIds } from '../../constants'; +import { DEFAULT_OPTIONS, TEST_IDS } from '../../constants'; import { Props, Text } from './Text'; /** @@ -15,7 +15,7 @@ describe('', () => { it('Should render default content when there is no dataframe', async () => { const props: Props = { options: { - ...DefaultOptions, + ...DEFAULT_OPTIONS, content: 'Test content', defaultContent: 'Test default content', everyRow: true, @@ -28,8 +28,8 @@ describe('', () => { render(); - expect(screen.getByTestId(TestIds.text.content)).toBeInTheDocument(); - expect(screen.getByTestId(TestIds.text.content)).toHaveTextContent('Test default content'); + expect(screen.getByTestId(TEST_IDS.text.content)).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.text.content)).toHaveTextContent('Test default content'); }); it('Should apply styles', async () => { @@ -39,7 +39,7 @@ describe('', () => { const replaceVariables = jest.fn((str: string) => str); const props: Props = { options: { - ...DefaultOptions, + ...DEFAULT_OPTIONS, content: 'Test content', defaultContent: 'Test default content', everyRow: true, @@ -55,8 +55,8 @@ describe('', () => { expect(replaceVariables).toHaveBeenCalledWith(styles); - expect(screen.getByTestId(TestIds.text.content)).toBeInTheDocument(); - expect(screen.getByTestId(TestIds.text.content)).toHaveStyle({ color: 'red' }); + expect(screen.getByTestId(TEST_IDS.text.content)).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.text.content)).toHaveStyle({ color: 'red' }); }); describe('After Render Function', () => { @@ -68,7 +68,7 @@ describe('', () => { const props: Props = { options: { - ...DefaultOptions, + ...DEFAULT_OPTIONS, defaultContent: '
', afterRender: ` context.grafana.eventBus.publish('ready', context.element.querySelector('#element')); @@ -94,7 +94,7 @@ describe('', () => { const props: Props = { options: { - ...DefaultOptions, + ...DEFAULT_OPTIONS, defaultContent: '
', afterRender: ` return () => context.grafana.eventBus.publish('destroy'); @@ -133,7 +133,7 @@ describe('', () => { const props: Props = { frame: dataFrame, options: { - ...DefaultOptions, + ...DEFAULT_OPTIONS, status: 'value', content: '
{{status}}
', defaultContent: 'Test default content', @@ -176,7 +176,7 @@ describe('', () => { ], }), options: { - ...DefaultOptions, + ...DEFAULT_OPTIONS, content: 'Test content', defaultContent: 'Test default content', everyRow: true, @@ -189,10 +189,10 @@ describe('', () => { render(); - expect(screen.getAllByTestId(TestIds.text.content)[0]).toBeInTheDocument(); - expect(screen.getAllByTestId(TestIds.text.content)[0]).toHaveTextContent('Test content'); - expect(screen.getAllByTestId(TestIds.text.content)[1]).toBeInTheDocument(); - expect(screen.getAllByTestId(TestIds.text.content)[1]).toHaveTextContent('Test content'); + expect(screen.getAllByTestId(TEST_IDS.text.content)[0]).toBeInTheDocument(); + expect(screen.getAllByTestId(TEST_IDS.text.content)[0]).toHaveTextContent('Test content'); + expect(screen.getAllByTestId(TEST_IDS.text.content)[1]).toBeInTheDocument(); + expect(screen.getAllByTestId(TEST_IDS.text.content)[1]).toHaveTextContent('Test content'); }); /** @@ -205,7 +205,7 @@ describe('', () => { length: 2, }, options: { - ...DefaultOptions, + ...DEFAULT_OPTIONS, content: 'Test content', defaultContent: 'Test default content', everyRow: false, @@ -254,7 +254,7 @@ describe('', () => { length: 2, }), options: { - ...DefaultOptions, + ...DEFAULT_OPTIONS, content: template, defaultContent: 'Test default content', everyRow: false, diff --git a/src/components/Text/Text.tsx b/src/components/Text/Text.tsx index 56582bf..5d2338d 100644 --- a/src/components/Text/Text.tsx +++ b/src/components/Text/Text.tsx @@ -4,11 +4,11 @@ import { TimeZone } from '@grafana/schema'; import { Alert, useStyles2 } from '@grafana/ui'; import React, { useCallback, useEffect, useState } from 'react'; -import { TestIds } from '../../constants'; +import { TEST_IDS } from '../../constants'; import { generateHtml } from '../../helpers'; import { PanelOptions, RowItem } from '../../types'; import { Row } from '../Row'; -import { Styles } from './Text.styles'; +import { getStyles } from './Text.styles'; /** * Properties @@ -74,7 +74,7 @@ export const Text: React.FC = ({ options, frame, timeRange, timeZone, rep /** * Styles */ - const styles = useStyles2(Styles); + const styles = useStyles2(getStyles); const className = cx( styles.highlight, styles.frame, @@ -131,21 +131,24 @@ export const Text: React.FC = ({ options, frame, timeRange, timeZone, rep /** * Frame returned */ - const data = frame.fields.reduce((acc, { config, name, values, display }) => { - values.toArray().forEach((value, i) => { - /** - * Status Color - */ - const statusColor = options.status === name ? display?.(value).color : undefined; - - /** - * Set Value and Status Color - */ - acc[i] = { ...acc[i], [config.displayName || name]: value, statusColor }; - }); - - return acc; - }, [] as Array>); + const data = frame.fields.reduce( + (acc, { config, name, values, display }) => { + values.toArray().forEach((value, i) => { + /** + * Status Color + */ + const statusColor = options.status === name ? display?.(value).color : undefined; + + /** + * Set Value and Status Color + */ + acc[i] = { ...acc[i], [config.displayName || name]: value, statusColor }; + }); + + return acc; + }, + [] as Array> + ); if (options.everyRow) { /** @@ -201,11 +204,11 @@ export const Text: React.FC = ({ options, frame, timeRange, timeZone, rep if (error) { return (
- + Please make sure the Content is a valid template and Helpers are correct. - {
{error instanceof Error ? error.message : `${error}`}
} + {
{error instanceof Error ? error.message : `${error}`}
}
); } diff --git a/src/components/TextPanel/TextPanel.styles.ts b/src/components/TextPanel/TextPanel.styles.ts index b5aad47..56ed643 100644 --- a/src/components/TextPanel/TextPanel.styles.ts +++ b/src/components/TextPanel/TextPanel.styles.ts @@ -4,7 +4,7 @@ import { GrafanaTheme2 } from '@grafana/data'; /** * Styles */ -export const Styles = (theme: GrafanaTheme2) => { +export const getStyles = (theme: GrafanaTheme2) => { return { root: css` display: flex; diff --git a/src/components/TextPanel/TextPanel.test.tsx b/src/components/TextPanel/TextPanel.test.tsx index b8d9f7c..0ee832d 100644 --- a/src/components/TextPanel/TextPanel.test.tsx +++ b/src/components/TextPanel/TextPanel.test.tsx @@ -2,7 +2,7 @@ import { FieldType, toDataFrame } from '@grafana/data'; import { fireEvent, render, screen } from '@testing-library/react'; import React from 'react'; -import { TestIds } from '../../constants'; +import { TEST_IDS } from '../../constants'; import { TextPanel } from './TextPanel'; /** @@ -76,7 +76,7 @@ describe('Panel', () => { it('Should find component', async () => { render(getComponent({ options: { defaultContent: 'hello' }, replaceVariables: (str: string) => str })); - expect(screen.getByTestId(TestIds.panel.root)).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.panel.root)).toBeInTheDocument(); }); describe('Helpers execution', () => { @@ -286,9 +286,9 @@ describe('Panel', () => { /** * Check if error is shown */ - expect(screen.getByTestId(TestIds.text.error)).toBeInTheDocument(); - expect(screen.getByTestId(TestIds.text.errorContent)).toBeInTheDocument(); - expect(screen.queryByTestId(TestIds.text.content)).not.toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.text.error)).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.text.errorContent)).toBeInTheDocument(); + expect(screen.queryByTestId(TEST_IDS.text.content)).not.toBeInTheDocument(); /** * Render without errors @@ -307,12 +307,12 @@ describe('Panel', () => { /** * Check if error is not rendered */ - expect(screen.queryByTestId(TestIds.text.error)).not.toBeInTheDocument(); + expect(screen.queryByTestId(TEST_IDS.text.error)).not.toBeInTheDocument(); /** * Check if content is rendered */ - expect(screen.getByTestId(TestIds.text.content)).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.text.content)).toBeInTheDocument(); }); it('Should show custom execution error', () => { @@ -333,9 +333,9 @@ describe('Panel', () => { /** * Check if error is shown */ - expect(screen.getByTestId(TestIds.text.error)).toBeInTheDocument(); - expect(screen.getByTestId(TestIds.text.errorContent)).toBeInTheDocument(); - expect(screen.getByTestId(TestIds.text.errorContent)).toHaveTextContent('abc'); + expect(screen.getByTestId(TEST_IDS.text.error)).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.text.errorContent)).toBeInTheDocument(); + expect(screen.getByTestId(TEST_IDS.text.errorContent)).toHaveTextContent('abc'); }); }); @@ -358,7 +358,7 @@ describe('Panel', () => { }) ); - const fieldFrame = screen.getByTestId(TestIds.panel.fieldFrame); + const fieldFrame = screen.getByTestId(TEST_IDS.panel.fieldFrame); expect(fieldFrame).toBeInTheDocument(); expect(fieldFrame).toHaveValue('A'); }); @@ -381,7 +381,7 @@ describe('Panel', () => { }) ); - const fieldFrame = screen.getByTestId(TestIds.panel.fieldFrame); + const fieldFrame = screen.getByTestId(TEST_IDS.panel.fieldFrame); fireEvent.change(fieldFrame, { target: { value: 'B' } }); diff --git a/src/components/TextPanel/TextPanel.tsx b/src/components/TextPanel/TextPanel.tsx index 9fc22fc..b3dc365 100644 --- a/src/components/TextPanel/TextPanel.tsx +++ b/src/components/TextPanel/TextPanel.tsx @@ -3,16 +3,16 @@ import { PanelProps, SelectableValue } from '@grafana/data'; import { Select, useStyles2 } from '@grafana/ui'; import React, { useCallback, useMemo, useState } from 'react'; -import { TestIds } from '../../constants'; +import { TEST_IDS } from '../../constants'; import { useExternalResources } from '../../hooks'; import { PanelOptions, ResourceType } from '../../types'; import { Text } from '../Text'; -import { Styles } from './TextPanel.styles'; +import { getStyles } from './TextPanel.styles'; /** * Properties */ -type Props = PanelProps +type Props = PanelProps; /** * Panel @@ -35,7 +35,7 @@ export const TextPanel: React.FC = ({ /** * Styles */ - const styles = useStyles2(Styles); + const styles = useStyles2(getStyles); /** * Change Frame @@ -93,7 +93,7 @@ export const TextPanel: React.FC = ({ height: ${height}px; ` )} - data-testid={TestIds.panel.root} + data-testid={TEST_IDS.panel.root} > {isScriptsLoaded && ( <> @@ -122,7 +122,7 @@ export const TextPanel: React.FC = ({ onChange={onChangeFrame} value={frame.refId} options={selectableFrames} - data-testid={TestIds.panel.fieldFrame} + data-testid={TEST_IDS.panel.fieldFrame} />
)} diff --git a/src/constants/default.ts b/src/constants/default.ts index 72462ad..c5bed44 100644 --- a/src/constants/default.ts +++ b/src/constants/default.ts @@ -4,7 +4,7 @@ import { CodeLanguage, Format } from './editor'; /** * Default Options */ -export const DefaultOptions: PanelOptions = { +export const DEFAULT_OPTIONS: PanelOptions = { afterRender: '', content: '```json\n{{{json @root}}}\n```', defaultContent: "The query didn't return any results.", diff --git a/src/constants/editor.ts b/src/constants/editor.ts index db9ab5e..395602e 100644 --- a/src/constants/editor.ts +++ b/src/constants/editor.ts @@ -14,7 +14,7 @@ export const enum CodeLanguage { /** * Supported Languages Options */ -export const CodeLanguageOptions = [ +export const CODE_LANGUAGE_OPTIONS = [ { value: CodeLanguage.HANDLEBARS, label: 'Handlebars' }, { value: CodeLanguage.HTML, label: 'HTML' }, { value: CodeLanguage.MARKDOWN, label: 'Markdown' }, @@ -31,7 +31,7 @@ export enum Format { /** * Format Options */ -export const FormatOptions = [ +export const FORMAT_OPTIONS = [ { value: Format.AUTO, label: 'Auto', icon: 'font' }, { value: Format.NONE, label: 'Disabled', icon: 'times-circle' }, ]; @@ -39,7 +39,7 @@ export const FormatOptions = [ /** * Helpers Suggestions */ -export const HelpersEditorSuggestions: CodeEditorSuggestionItem[] = [ +export const HELPERS_EDITOR_SUGGESTIONS: CodeEditorSuggestionItem[] = [ { label: 'data', kind: CodeEditorSuggestionItemKind.Property, diff --git a/src/constants/highlight.ts b/src/constants/highlight.ts index 401c71f..72d70b4 100644 --- a/src/constants/highlight.ts +++ b/src/constants/highlight.ts @@ -1,6 +1,6 @@ import { css } from '@emotion/css'; -export const HighlightLight = css` +export const HIGHLIGHT_LIGHT = css` pre code.hljs { display: block; overflow-x: auto; @@ -87,7 +87,7 @@ Based on the Tomorrow Night Eighties theme: https://github.com/isagalaev/highlig } `; -export const HighlightDark = css` +export const HIGHLIGHT_DARK = css` pre code.hljs { display: block; overflow-x: auto; diff --git a/src/constants/panel.ts b/src/constants/panel.ts index b698e91..b176708 100644 --- a/src/constants/panel.ts +++ b/src/constants/panel.ts @@ -3,7 +3,7 @@ import { EditorType } from '../types'; /** * Rows Options */ -export const EveryRowOptions = [ +export const EVERY_ROW_OPTIONS = [ { value: true, label: 'Every row', icon: 'list-ul' }, { value: false, label: 'All rows', icon: 'book' }, ]; @@ -11,7 +11,7 @@ export const EveryRowOptions = [ /** * Editors Options */ -export const EditorsOptions = [ +export const EDITORS_OPTIONS = [ { value: EditorType.DEFAULT, label: 'Default content' }, { value: EditorType.HELPERS, label: 'JavaScript code before content rendering' }, { value: EditorType.AFTER_RENDER, label: 'JavaScript code after content ready' }, @@ -21,7 +21,7 @@ export const EditorsOptions = [ /** * Wrap Options */ -export const WrapOptions = [ +export const WRAP_OPTIONS = [ { value: true, label: 'Enabled', icon: 'file-copy-alt' }, { value: false, label: 'Disabled', icon: 'times-circle' }, ]; diff --git a/src/constants/tests.ts b/src/constants/tests.ts index 39304cc..5b8bea6 100644 --- a/src/constants/tests.ts +++ b/src/constants/tests.ts @@ -1,7 +1,7 @@ /** * Tests Identifiers */ -export const TestIds = { +export const TEST_IDS = { panel: { root: 'data-testid panel', fieldFrame: 'data-testid panel field-frame', diff --git a/src/module.test.ts b/src/module.test.ts index f469051..bea1c08 100644 --- a/src/module.test.ts +++ b/src/module.test.ts @@ -1,7 +1,7 @@ import { Field, FieldType, PanelPlugin } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { DefaultOptions } from './constants'; +import { DEFAULT_OPTIONS } from './constants'; import { plugin } from './module'; import { EditorType, PanelOptions } from './types'; @@ -82,7 +82,7 @@ describe('plugin', () => { builder.addCustomEditor.mockImplementation( addInputImplementation( - { defaultContent: DefaultOptions.defaultContent + '123', editors: [] }, + { defaultContent: DEFAULT_OPTIONS.defaultContent + '123', editors: [] }, shownOptionsPaths ) ); diff --git a/src/module.ts b/src/module.ts index d8769ae..9dfc179 100644 --- a/src/module.ts +++ b/src/module.ts @@ -3,12 +3,12 @@ import { config } from '@grafana/runtime'; import { HelpersEditor, ResourcesEditor, StylesEditor, TextEditor, TextPanel } from './components'; import { - CodeLanguageOptions, - DefaultOptions, - EditorsOptions, - EveryRowOptions, - FormatOptions, - WrapOptions, + CODE_LANGUAGE_OPTIONS, + DEFAULT_OPTIONS, + EDITORS_OPTIONS, + EVERY_ROW_OPTIONS, + FORMAT_OPTIONS, + WRAP_OPTIONS, } from './constants'; import { EditorType, PanelOptions } from './types'; @@ -36,17 +36,17 @@ export const plugin = new PanelPlugin(TextPanel) path: 'everyRow', name: 'Render template', settings: { - options: EveryRowOptions, + options: EVERY_ROW_OPTIONS, }, - defaultValue: DefaultOptions.everyRow, + defaultValue: DEFAULT_OPTIONS.everyRow, }) .addMultiSelect({ path: 'editors', name: 'Select Editors to display. Editors with updated values always displayed.', settings: { - options: EditorsOptions as never, + options: EDITORS_OPTIONS as never, }, - defaultValue: DefaultOptions.editors, + defaultValue: DEFAULT_OPTIONS.editors, }) .addFieldNamePicker({ path: 'status', @@ -65,7 +65,7 @@ export const plugin = new PanelPlugin(TextPanel) id: 'externalStyles', path: 'externalStyles', name: 'Styles', - defaultValue: DefaultOptions.externalStyles, + defaultValue: DEFAULT_OPTIONS.externalStyles, editor: ResourcesEditor, category: ['External Resources'], showIf: () => config.disableSanitizeHtml, @@ -74,7 +74,7 @@ export const plugin = new PanelPlugin(TextPanel) id: 'externalScripts', path: 'externalScripts', name: 'Scripts', - defaultValue: DefaultOptions.externalScripts, + defaultValue: DEFAULT_OPTIONS.externalScripts, editor: ResourcesEditor, category: ['External Resources'], showIf: () => config.disableSanitizeHtml, @@ -89,24 +89,24 @@ export const plugin = new PanelPlugin(TextPanel) name: 'Primary Content Language', description: 'Used for formatting and suggestions.', settings: { - options: CodeLanguageOptions, + options: CODE_LANGUAGE_OPTIONS, }, - defaultValue: DefaultOptions.editor.language, + defaultValue: DEFAULT_OPTIONS.editor.language, category: ['Editor'], }) .addRadio({ path: 'editor.format', name: 'Formatting', settings: { - options: FormatOptions, + options: FORMAT_OPTIONS, }, - defaultValue: DefaultOptions.editor.format, + defaultValue: DEFAULT_OPTIONS.editor.format, category: ['Editor'], }) .addSliderInput({ path: 'editor.height', name: 'Height, px', - defaultValue: DefaultOptions.editor.height, + defaultValue: DEFAULT_OPTIONS.editor.height, settings: { min: 100, max: 2000, @@ -122,9 +122,9 @@ export const plugin = new PanelPlugin(TextPanel) path: 'wrap', name: 'Wrap automatically in paragraphs', description: 'If disabled, result will NOT be wrapped into

tags.', - defaultValue: DefaultOptions.wrap, + defaultValue: DEFAULT_OPTIONS.wrap, settings: { - options: WrapOptions, + options: WRAP_OPTIONS, }, category: ['Content'], }) @@ -132,7 +132,7 @@ export const plugin = new PanelPlugin(TextPanel) id: 'content', path: 'content', name: 'Content', - defaultValue: DefaultOptions.content, + defaultValue: DEFAULT_OPTIONS.content, editor: TextEditor, category: ['Content'], }) @@ -141,11 +141,11 @@ export const plugin = new PanelPlugin(TextPanel) path: 'defaultContent', name: 'Default Content', description: 'Displayed when query result is empty.', - defaultValue: DefaultOptions.defaultContent, + defaultValue: DEFAULT_OPTIONS.defaultContent, editor: TextEditor, category: ['Content'], showIf: (config) => - config.editors.includes(EditorType.DEFAULT) || config.defaultContent !== DefaultOptions.defaultContent, + config.editors.includes(EditorType.DEFAULT) || config.defaultContent !== DEFAULT_OPTIONS.defaultContent, }); /** @@ -157,10 +157,10 @@ export const plugin = new PanelPlugin(TextPanel) path: 'helpers', name: 'Before Content Rendering', description: 'Allows to execute code before content rendering. E.g. add Handlebars Helpers.', - defaultValue: DefaultOptions.helpers, + defaultValue: DEFAULT_OPTIONS.helpers, editor: HelpersEditor, category: ['JavaScript'], - showIf: (config) => config.editors.includes(EditorType.HELPERS) || config.helpers !== DefaultOptions.helpers, + showIf: (config) => config.editors.includes(EditorType.HELPERS) || config.helpers !== DEFAULT_OPTIONS.helpers, }) .addCustomEditor({ id: 'afterRender', @@ -168,11 +168,11 @@ export const plugin = new PanelPlugin(TextPanel) name: 'After Content Ready', description: 'Allows to execute code after content is ready. E.g. use element for drawing chart or event listeners.', - defaultValue: DefaultOptions.afterRender, + defaultValue: DEFAULT_OPTIONS.afterRender, editor: HelpersEditor, category: ['JavaScript'], showIf: (config) => - config.editors.includes(EditorType.AFTER_RENDER) || config.afterRender !== DefaultOptions.afterRender, + config.editors.includes(EditorType.AFTER_RENDER) || config.afterRender !== DEFAULT_OPTIONS.afterRender, }); /** @@ -183,10 +183,10 @@ export const plugin = new PanelPlugin(TextPanel) path: 'styles', name: 'CSS Styles', description: 'Allows to add styles. Use & {} for parent style.', - defaultValue: DefaultOptions.styles, + defaultValue: DEFAULT_OPTIONS.styles, editor: StylesEditor, category: ['CSS Styles'], - showIf: (config) => config.editors.includes(EditorType.STYLES) || config.styles !== DefaultOptions.styles, + showIf: (config) => config.editors.includes(EditorType.STYLES) || config.styles !== DEFAULT_OPTIONS.styles, }); return builder;