diff --git a/e2e-playwright/dashboard-new-layouts/dashboards-edit-panel-transparent-bg.spec.ts b/e2e-playwright/dashboard-new-layouts/dashboards-edit-panel-transparent-bg.spec.ts index d483b7b8005..a7ef95b1f4d 100644 --- a/e2e-playwright/dashboard-new-layouts/dashboards-edit-panel-transparent-bg.spec.ts +++ b/e2e-playwright/dashboard-new-layouts/dashboards-edit-panel-transparent-bg.spec.ts @@ -34,7 +34,7 @@ test.describe( const initialBackground = await panelTitle.evaluate((el) => getComputedStyle(el).background); expect(initialBackground).not.toMatch(/rgba\(0, 0, 0, 0\)/); - await page.locator('#transparent-background').click({ force: true }); + await page.getByRole('switch', { name: 'Transparent background' }).click({ force: true }); const transparentBackground = await panelTitle.evaluate((el) => getComputedStyle(el).background); expect(transparentBackground).toMatch(/rgba\(0, 0, 0, 0\)/); diff --git a/e2e-playwright/panels-suite/table-kitchenSink.spec.ts b/e2e-playwright/panels-suite/table-kitchenSink.spec.ts index 5ddb7c4d4e4..237ea5272a9 100644 --- a/e2e-playwright/panels-suite/table-kitchenSink.spec.ts +++ b/e2e-playwright/panels-suite/table-kitchenSink.spec.ts @@ -95,8 +95,8 @@ test.describe('Panels test: Table - Kitchen Sink', { tag: ['@panels', '@table'] await dashboardPage .getByGrafanaSelector(selectors.components.PanelEditor.OptionsPane.fieldLabel('Cell options Cell value inspect')) .first() - .locator('label[for="custom.inspect"]') - .click(); + .getByRole('switch', { name: 'Cell value inspect' }) + .click({ force: true }); await loremIpsumCell.hover(); await expect(getCellHeight(page, 1, longTextColIdx)).resolves.toBeLessThan(100); diff --git a/e2e/old-arch/panels-suite/geomap-map-controls.spec.ts b/e2e/old-arch/panels-suite/geomap-map-controls.spec.ts index b344dae5abe..fcadcb55a47 100644 --- a/e2e/old-arch/panels-suite/geomap-map-controls.spec.ts +++ b/e2e/old-arch/panels-suite/geomap-map-controls.spec.ts @@ -17,35 +17,35 @@ describe('Geomap layer controls options', () => { e2e.components.PanelEditor.showZoomField() .should('be.visible') .within(() => { - cy.get('input[type="checkbox"]').check({ force: true }).should('be.checked'); + cy.get('input[type="checkbox"]').check({ force: true }); }); // Show attribution e2e.components.PanelEditor.showAttributionField() .should('be.visible') .within(() => { - cy.get('input[type="checkbox"]').check({ force: true }).should('be.checked'); + cy.get('input[type="checkbox"]').check({ force: true }); }); // Show scale e2e.components.PanelEditor.showScaleField() .should('be.visible') .within(() => { - cy.get('input[type="checkbox"]').check({ force: true }).should('be.checked'); + cy.get('input[type="checkbox"]').check({ force: true }); }); // Show measure tool e2e.components.PanelEditor.showMeasureField() .should('be.visible') .within(() => { - cy.get('input[type="checkbox"]').check({ force: true }).should('be.checked'); + cy.get('input[type="checkbox"]').check({ force: true }); }); // Show debug e2e.components.PanelEditor.showDebugField() .should('be.visible') .within(() => { - cy.get('input[type="checkbox"]').check({ force: true }).should('be.checked'); + cy.get('input[type="checkbox"]').check({ force: true }); }); e2e.components.Panels.Panel.content({ timeout: TIMEOUT }) diff --git a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx index 5b23a4fefa6..43daedcbdb6 100644 --- a/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx +++ b/packages/grafana-ui/src/components/ColorPicker/ColorPicker.tsx @@ -37,7 +37,7 @@ export const colorPickerFactory = ( pickerTriggerRef = createRef(); render() { - const { theme, children, onChange, color } = this.props; + const { theme, children, onChange, color, id } = this.props; const styles = getStyles(theme); const popoverElement = React.createElement(popover, { ...{ ...this.props, children: null }, @@ -67,6 +67,7 @@ export const colorPickerFactory = ( }) ) : ( extends ColorPickerProps, PopoverContentProps { diff --git a/packages/grafana-ui/src/components/MatchersUI/FieldNamePicker.tsx b/packages/grafana-ui/src/components/MatchersUI/FieldNamePicker.tsx index 63240a9b0eb..cce936f01f7 100644 --- a/packages/grafana-ui/src/components/MatchersUI/FieldNamePicker.tsx +++ b/packages/grafana-ui/src/components/MatchersUI/FieldNamePicker.tsx @@ -10,7 +10,7 @@ import { useFieldDisplayNames, useSelectOptions, frameHasName } from './utils'; type Props = StandardEditorProps; // Pick a field name out of the fields -export const FieldNamePicker = ({ value, onChange, context, item }: Props) => { +export const FieldNamePicker = ({ value, onChange, context, item, id }: Props) => { const settings: FieldNamePickerConfigSettings = item.settings ?? {}; const names = useFieldDisplayNames(context.data, settings?.filter); const selectOptions = useSelectOptions(names, value, undefined, undefined, settings.baseNameMode); @@ -29,6 +29,7 @@ export const FieldNamePicker = ({ value, onChange, context, item }: Props) => { return ( <> void; placeholder?: string; + id?: string; } -export function RefIDMultiPicker({ value, data, onChange, placeholder }: MultiProps) { +export function RefIDMultiPicker({ value, data, onChange, placeholder, id }: MultiProps) { const listOfRefIds = useMemo(() => getListOfQueryRefIds(data), [data]); const [priorSelectionState, updatePriorSelectionState] = useState<{ @@ -172,6 +175,7 @@ export function RefIDMultiPicker({ value, data, onChange, placeholder }: MultiPr } return ( void; value?: string; width?: number; + id?: string; } function formatCreateLabel(input: string) { @@ -21,7 +22,7 @@ export class UnitPicker extends PureComponent { }; render() { - const { value, width } = this.props; + const { value, width, id } = this.props; // Set the current selection let current: SelectableValue | undefined = undefined; @@ -56,6 +57,7 @@ export class UnitPicker extends PureComponent { return ( { return ( void; settings?: ColorValueEditorSettings; @@ -25,7 +26,7 @@ interface Props { /** * @alpha * */ -export const ColorValueEditor = ({ value, settings, onChange, details }: Props) => { +export const ColorValueEditor = ({ value, settings, onChange, details, id }: Props) => { const theme = useTheme2(); const styles = useStyles2(getStyles); @@ -37,6 +38,7 @@ export const ColorValueEditor = ({ value, settings, onChange, details }: Props)
extends PureComponent, State> render() { const { options, isLoading } = this.state; - const { value, onChange, item } = this.props; + const { value, onChange, item, id } = this.props; const { settings } = item; return ( + inputId={id} isLoading={isLoading} value={value} defaultValue={value} diff --git a/public/app/core/components/OptionsUI/number.tsx b/public/app/core/components/OptionsUI/number.tsx index e7b0db58bbe..cd69f2b0370 100644 --- a/public/app/core/components/OptionsUI/number.tsx +++ b/public/app/core/components/OptionsUI/number.tsx @@ -6,7 +6,7 @@ import { NumberInput } from './NumberInput'; type Props = StandardEditorProps; -export const NumberValueEditor = ({ value, onChange, item }: Props) => { +export const NumberValueEditor = ({ value, onChange, item, id }: Props) => { const { settings } = item; const onValueChange = useCallback( @@ -18,6 +18,7 @@ export const NumberValueEditor = ({ value, onChange, item }: Props) => { return ( extends PureComponent, State> { render() { const { options, isLoading } = this.state; - const { value, onChange, item } = this.props; + const { value, onChange, item, id } = this.props; const { settings } = item; let current = options.find((v) => v.value === value); @@ -63,6 +63,7 @@ export class SelectValueEditor extends PureComponent, State> { } return ( + inputId={id} isLoading={isLoading} value={current} defaultValue={value} diff --git a/public/app/core/components/OptionsUI/slider.tsx b/public/app/core/components/OptionsUI/slider.tsx index b9bb41bbd90..01835ea9a38 100644 --- a/public/app/core/components/OptionsUI/slider.tsx +++ b/public/app/core/components/OptionsUI/slider.tsx @@ -11,7 +11,7 @@ import { NumberInput } from './NumberInput'; type Props = StandardEditorProps; -export const SliderValueEditor = ({ value, onChange, item }: Props) => { +export const SliderValueEditor = ({ value, onChange, item, id }: Props) => { // Input reference const inputRef = useRef(null); @@ -109,7 +109,7 @@ export const SliderValueEditor = ({ value, onChange, item }: Props) => { included={included} /> - +
diff --git a/public/app/core/components/OptionsUI/string.tsx b/public/app/core/components/OptionsUI/string.tsx index 8001bf096af..7ec4635f7c8 100644 --- a/public/app/core/components/OptionsUI/string.tsx +++ b/public/app/core/components/OptionsUI/string.tsx @@ -8,7 +8,7 @@ interface Props extends StandardEditorProps { suffix?: ReactNode; } -export const StringValueEditor = ({ value, onChange, item, suffix }: Props) => { +export const StringValueEditor = ({ value, onChange, item, suffix, id }: Props) => { const Component = item.settings?.useTextarea ? TextArea : Input; const onValueChange = useCallback( ( @@ -36,6 +36,7 @@ export const StringValueEditor = ({ value, onChange, item, suffix }: Props) => { return ( ; -export function UnitValueEditor({ value, onChange, item }: Props) { +export function UnitValueEditor({ value, onChange, item, id }: Props) { const styles = useStyles2(getStyles); if (item?.settings?.isClearable && value != null) { return (
- + ); } - return ; + return ; } const getStyles = (theme: GrafanaTheme2) => ({ diff --git a/public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingEditor.tsx b/public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingEditor.tsx index bab710c70c2..9df4bd0de02 100644 --- a/public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingEditor.tsx +++ b/public/app/features/dashboard-scene/conditional-rendering/ConditionalRenderingEditor.tsx @@ -1,3 +1,5 @@ +import { v4 as uuidv4 } from 'uuid'; + import { t } from '@grafana/i18n'; import { Icon, Stack, Tooltip } from '@grafana/ui'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; @@ -29,6 +31,7 @@ export function useConditionalRenderingEditor( }).addItem( new OptionsPaneItemDescriptor({ title, + id: uuidv4(), render: () => , }) ); diff --git a/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx b/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx index 93e5ec503d8..730c16aa410 100644 --- a/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx +++ b/public/app/features/dashboard-scene/edit-pane/DashboardEditableElement.tsx @@ -1,4 +1,5 @@ import { ReactNode, useMemo, useRef } from 'react'; +import { v4 as uuidv4 } from 'uuid'; import { Trans, t } from '@grafana/i18n'; import { SceneObject } from '@grafana/scenes'; @@ -38,8 +39,8 @@ export class DashboardEditableElement implements EditableDashboardElement { const { body } = dashboard.useState(); const dashboardOptions = useMemo(() => { - const dashboardTitleInputId = 'dashboard-title-input'; - const dashboardDescriptionInputId = 'dashboard-description-input'; + const dashboardTitleInputId = uuidv4(); + const dashboardDescriptionInputId = uuidv4(); const editPaneHeaderOptions = new OptionsPaneCategoryDescriptor({ title: '', id: 'dashboard-options' }) .addItem( new OptionsPaneItemDescriptor({ diff --git a/public/app/features/dashboard-scene/edit-pane/VizPanelEditableElement.tsx b/public/app/features/dashboard-scene/edit-pane/VizPanelEditableElement.tsx index 574028a75d8..c5c55bbaf61 100644 --- a/public/app/features/dashboard-scene/edit-pane/VizPanelEditableElement.tsx +++ b/public/app/features/dashboard-scene/edit-pane/VizPanelEditableElement.tsx @@ -1,4 +1,5 @@ import { useMemo } from 'react'; +import { v4 as uuidv4 } from 'uuid'; import { Trans, t } from '@grafana/i18n'; import { locationService } from '@grafana/runtime'; @@ -48,13 +49,14 @@ export class VizPanelEditableElement implements EditableDashboardElement, BulkAc .addItem( new OptionsPaneItemDescriptor({ title: '', + id: uuidv4(), render: () => , }) ) .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.viz-panel.options.title-option', 'Title'), - id: 'PanelFrameTitle', + id: uuidv4(), value: panel.state.title, popularRank: 1, render: (descriptor) => ( @@ -65,7 +67,7 @@ export class VizPanelEditableElement implements EditableDashboardElement, BulkAc .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.viz-panel.options.description', 'Description'), - id: 'description-text-area', + id: uuidv4(), value: panel.state.description, render: (descriptor) => , }) @@ -73,7 +75,7 @@ export class VizPanelEditableElement implements EditableDashboardElement, BulkAc .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.viz-panel.options.transparent-background', 'Transparent background'), - id: 'transparent-background', + id: uuidv4(), render: (descriptor) => , }) ); @@ -133,9 +135,7 @@ export class VizPanelEditableElement implements EditableDashboardElement, BulkAc } } -type OpenPanelEditVizProps = { - panel: VizPanel; -}; +type OpenPanelEditVizProps = { panel: VizPanel }; const OpenPanelEditViz = ({ panel }: OpenPanelEditVizProps) => { return ( diff --git a/public/app/features/dashboard-scene/panel-edit/getPanelFrameOptions.tsx b/public/app/features/dashboard-scene/panel-edit/getPanelFrameOptions.tsx index 366fcdc81e4..a46c113c7c9 100644 --- a/public/app/features/dashboard-scene/panel-edit/getPanelFrameOptions.tsx +++ b/public/app/features/dashboard-scene/panel-edit/getPanelFrameOptions.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import { v4 as uuidv4 } from 'uuid'; import { CoreApp } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; @@ -37,7 +38,7 @@ export function getPanelFrameOptions(panel: VizPanel): OptionsPaneCategoryDescri .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard-scene.get-panel-frame-options.title.title', 'Title'), - id: 'PanelFrameTitle', + id: uuidv4(), value: panel.state.title, popularRank: 1, render: function renderTitle(descriptor) { @@ -55,7 +56,7 @@ export function getPanelFrameOptions(panel: VizPanel): OptionsPaneCategoryDescri .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard-scene.get-panel-frame-options.title.description', 'Description'), - id: 'description-text-area', + id: uuidv4(), value: panel.state.description, render: function renderDescription(descriptor) { return ; @@ -71,7 +72,7 @@ export function getPanelFrameOptions(panel: VizPanel): OptionsPaneCategoryDescri .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard-scene.get-panel-frame-options.title.transparent-background', 'Transparent background'), - id: 'transparent-background', + id: uuidv4(), render: function renderTransparent(descriptor) { return ; }, @@ -86,6 +87,7 @@ export function getPanelFrameOptions(panel: VizPanel): OptionsPaneCategoryDescri }).addItem( new OptionsPaneItemDescriptor({ title: t('dashboard-scene.get-panel-frame-options.title.panel-links', 'Panel links'), + id: uuidv4(), render: () => , }) ) diff --git a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItemEditor.tsx b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItemEditor.tsx index dc20ec7a8bd..93804685fcb 100644 --- a/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItemEditor.tsx +++ b/public/app/features/dashboard-scene/scene/layout-auto-grid/AutoGridItemEditor.tsx @@ -1,3 +1,5 @@ +import { v4 as uuidv4 } from 'uuid'; + import { t } from '@grafana/i18n'; import { OptionsPaneCategoryDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneCategoryDescriptor'; import { OptionsPaneItemDescriptor } from 'app/features/dashboard/components/PanelEditor/OptionsPaneItemDescriptor'; @@ -16,7 +18,7 @@ export function getOptions(model: AutoGridItem): OptionsPaneCategoryDescriptor[] }).addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.auto-grid.item-options.repeat.variable.title', 'Repeat by variable'), - id: 'repeat-by-variable-select', + id: uuidv4(), description: t( 'dashboard.auto-grid.item-options.repeat.variable.description', 'Repeat this panel for each value in the selected variable. This is not visible while in edit mode. You need to go back to dashboard and then update the variable or reload the dashboard.' diff --git a/public/app/features/dashboard-scene/scene/layout-default/DashboardGridItemEditor.tsx b/public/app/features/dashboard-scene/scene/layout-default/DashboardGridItemEditor.tsx index 3b18200fadc..e5b39a60c59 100644 --- a/public/app/features/dashboard-scene/scene/layout-default/DashboardGridItemEditor.tsx +++ b/public/app/features/dashboard-scene/scene/layout-default/DashboardGridItemEditor.tsx @@ -1,4 +1,5 @@ import { useCallback } from 'react'; +import { v4 as uuidv4 } from 'uuid'; import { SelectableValue } from '@grafana/data'; import { t } from '@grafana/i18n'; @@ -21,7 +22,7 @@ export function getDashboardGridItemOptions(gridItem: DashboardGridItem): Option .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.default-layout.item-options.repeat.variable.title', 'Repeat by variable'), - id: 'repeat-by-variable-select', + id: uuidv4(), description: t( 'dashboard.default-layout.item-options.repeat.variable.description', 'Repeat this panel for each value in the selected variable. This is not visible while in edit mode. You need to go back to dashboard and then update the variable or reload the dashboard.' @@ -42,11 +43,12 @@ export function getDashboardGridItemOptions(gridItem: DashboardGridItem): Option .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.default-layout.item-options.repeat.max', 'Max per row'), + id: uuidv4(), useShowIf: () => { const { variableName, repeatDirection } = gridItem.useState(); return Boolean(variableName) && repeatDirection === 'h'; }, - render: () => , + render: (descriptor) => , }) ); @@ -81,7 +83,7 @@ function RepeatDirectionOption({ gridItem }: OptionComponentProps) { ); } -function MaxPerRowOption({ gridItem }: OptionComponentProps) { +function MaxPerRowOption({ gridItem, id }: OptionComponentProps & { id?: string }) { const { maxPerRow } = gridItem.useState(); const maxPerRowOptions: Array> = [2, 3, 4, 6, 8, 12].map((value) => ({ label: value.toString(), @@ -90,6 +92,7 @@ function MaxPerRowOption({ gridItem }: OptionComponentProps) { return ( row.setState({ title: e.currentTarget.value })} />; + return row.setState({ title: e.currentTarget.value })} />; } -function RowRepeatSelect({ row, dashboard }: { row: SceneGridRow; dashboard: DashboardScene }) { +function RowRepeatSelect({ row, dashboard, id }: { row: SceneGridRow; dashboard: DashboardScene; id?: string }) { const { $behaviors, children } = row.useState(); let repeatBehavior = $behaviors?.find((b) => b instanceof RowRepeaterBehavior); const vizPanels = useMemo( @@ -104,6 +107,7 @@ function RowRepeatSelect({ row, dashboard }: { row: SceneGridRow; dashboard: Das return ( <> { diff --git a/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx b/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx index 396e4075587..a05393bca37 100644 --- a/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx +++ b/public/app/features/dashboard-scene/scene/layout-rows/RowItemEditor.tsx @@ -1,4 +1,5 @@ -import { useMemo } from 'react'; +import { useId, useMemo } from 'react'; +import { v4 as uuidv4 } from 'uuid'; import { selectors } from '@grafana/e2e-selectors'; import { Trans, t } from '@grafana/i18n'; @@ -32,13 +33,15 @@ export function useEditOptions(model: RowItem, isNewElement: boolean): OptionsPa .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.rows-layout.row-options.row.fill-screen', 'Fill screen'), - render: () => , + id: uuidv4(), + render: (descriptor) => , }) ) .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.rows-layout.row-options.row.hide-header', 'Hide row header'), - render: () => , + id: uuidv4(), + render: (descriptor) => , }) ), [model, isNewElement] @@ -53,11 +56,12 @@ export function useEditOptions(model: RowItem, isNewElement: boolean): OptionsPa }).addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.rows-layout.row-options.repeat.variable.title', 'Repeat by variable'), + id: uuidv4(), description: t( 'dashboard.rows-layout.row-options.repeat.variable.description', 'Repeat this row for each value in the selected variable.' ), - render: () => , + render: (descriptor) => , }) ), [model] @@ -94,6 +98,7 @@ function RowTitleInput({ row, isNewElement }: { row: RowItem; isNewElement: bool } > row.onHeaderHiddenToggle()} />; + return row.onHeaderHiddenToggle()} />; } -function FillScreenSwitch({ row }: { row: RowItem }) { +function FillScreenSwitch({ row, id }: { row: RowItem; id?: string }) { const { fillScreen } = row.useState(); - return row.onChangeFillScreen(!fillScreen)} />; + return row.onChangeFillScreen(!fillScreen)} />; } -function RowRepeatSelect({ row }: { row: RowItem }) { +function RowRepeatSelect({ row, id }: { row: RowItem; id?: string }) { const { layout } = row.useState(); const dashboard = useDashboard(row); @@ -131,6 +136,7 @@ function RowRepeatSelect({ row }: { row: RowItem }) { return ( <> row.onChangeRepeat(repeat)} diff --git a/public/app/features/dashboard-scene/scene/layout-tabs/TabItemEditor.tsx b/public/app/features/dashboard-scene/scene/layout-tabs/TabItemEditor.tsx index 0a2b0b3f30e..0c34c8a7776 100644 --- a/public/app/features/dashboard-scene/scene/layout-tabs/TabItemEditor.tsx +++ b/public/app/features/dashboard-scene/scene/layout-tabs/TabItemEditor.tsx @@ -1,4 +1,5 @@ import { useMemo } from 'react'; +import { v4 as uuidv4 } from 'uuid'; import { selectors } from '@grafana/e2e-selectors'; import { Trans, t } from '@grafana/i18n'; @@ -24,7 +25,8 @@ export function useEditOptions(model: TabItem, isNewElement: boolean): OptionsPa new OptionsPaneCategoryDescriptor({ title: '', id: 'tab-item-options' }).addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.tabs-layout.tab-options.title-option', 'Title'), - render: () => , + id: uuidv4(), + render: (descriptor) => , }) ), [model, isNewElement] @@ -39,11 +41,12 @@ export function useEditOptions(model: TabItem, isNewElement: boolean): OptionsPa }).addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.tabs-layout.tab-options.repeat.variable.title', 'Repeat by variable'), + id: uuidv4(), description: t( 'dashboard.tabs-layout.tab-options.repeat.variable.description', 'Repeat this tab for each value in the selected variable.' ), - render: () => , + render: (descriptor) => , }) ), [model] @@ -65,7 +68,7 @@ export function useEditOptions(model: TabItem, isNewElement: boolean): OptionsPa return editOptions; } -function TabTitleInput({ tab, isNewElement }: { tab: TabItem; isNewElement: boolean }) { +function TabTitleInput({ tab, isNewElement, id }: { tab: TabItem; isNewElement: boolean; id?: string }) { const { title } = tab.useState(); const ref = useEditPaneInputAutoFocus({ autoFocus: isNewElement }); @@ -79,6 +82,7 @@ function TabTitleInput({ tab, isNewElement }: { tab: TabItem; isNewElement: bool } > tab.onChangeRepeat(repeat)} diff --git a/public/app/features/dashboard-scene/settings/variables/VariableEditableElement.tsx b/public/app/features/dashboard-scene/settings/variables/VariableEditableElement.tsx index 86450b91f92..862a51ee1ac 100644 --- a/public/app/features/dashboard-scene/settings/variables/VariableEditableElement.tsx +++ b/public/app/features/dashboard-scene/settings/variables/VariableEditableElement.tsx @@ -1,4 +1,5 @@ -import { FormEvent, useMemo, useRef, useState } from 'react'; +import { FormEvent, useId, useMemo, useRef, useState } from 'react'; +import { v4 as uuidv4 } from 'uuid'; import { VariableHide } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; @@ -63,14 +64,16 @@ export class VariableEditableElement implements EditableDashboardElement, BulkAc .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.edit-pane.variable.label', 'Label'), + id: uuidv4(), description: t('dashboard.edit-pane.variable.label-description', 'Optional display name'), - render: () => , + render: (descriptor) => , }) ) .addItem( new OptionsPaneItemDescriptor({ title: t('dashboard.edit-pane.variable.description', 'Description'), - render: () => , + id: uuidv4(), + render: (descriptor) => , }) ) .addItem( @@ -117,6 +120,7 @@ export class VariableEditableElement implements EditableDashboardElement, BulkAc interface VariableInputProps { variable: SceneVariable; + id?: string; } function VariableNameInput({ variable, isNewElement }: { variable: SceneVariable; isNewElement: boolean }) { @@ -138,6 +142,7 @@ function VariableNameInput({ variable, isNewElement }: { variable: SceneVariable return ( { @@ -171,12 +176,13 @@ function VariableNameInput({ variable, isNewElement }: { variable: SceneVariable ); } -function VariableLabelInput({ variable }: VariableInputProps) { +function VariableLabelInput({ variable, id }: VariableInputProps) { const { label } = variable.useState(); const oldLabel = useRef(label ?? ''); return ( { oldLabel.current = label ?? ''; @@ -201,13 +207,13 @@ function VariableLabelInput({ variable }: VariableInputProps) { ); } -function VariableDescriptionTextArea({ variable }: VariableInputProps) { +function VariableDescriptionTextArea({ variable, id }: VariableInputProps) { const { description } = variable.useState(); const oldDescription = useRef(description ?? ''); return (