diff --git a/public/app/features/dashboard-scene/scene/DashboardGridItem.test.tsx b/public/app/features/dashboard-scene/scene/DashboardGridItem.test.tsx index fc045edfa16..a0e90f58d7a 100644 --- a/public/app/features/dashboard-scene/scene/DashboardGridItem.test.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardGridItem.test.tsx @@ -40,6 +40,54 @@ describe('PanelRepeaterGridItem', () => { expect(repeater.state.repeatedPanels?.length).toBe(5); }); + it('Should display a panel when there are no options', async () => { + const { scene, repeater } = buildPanelRepeaterScene({ variableQueryTime: 1, numberOfOptions: 0 }); + + activateFullSceneTree(scene); + + expect(repeater.state.repeatedPanels?.length).toBe(0); + + await new Promise((r) => setTimeout(r, 10)); + + expect(repeater.state.repeatedPanels?.length).toBe(1); + }); + + it('Should display a panel when there are variable errors', () => { + const { scene, repeater } = buildPanelRepeaterScene({ + variableQueryTime: 0, + numberOfOptions: 0, + throwError: 'Error', + }); + + // we expect console.error when variable encounters an error + const origError = console.error; + console.error = jest.fn(); + + activateFullSceneTree(scene); + + expect(repeater.state.repeatedPanels?.length).toBe(1); + console.error = origError; + }); + + it('Should display a panel when there are variable errors async query', async () => { + const { scene, repeater } = buildPanelRepeaterScene({ + variableQueryTime: 1, + numberOfOptions: 0, + throwError: 'Error', + }); + + // we expect console.error when variable encounters an error + const origError = console.error; + console.error = jest.fn(); + + activateFullSceneTree(scene); + + await new Promise((r) => setTimeout(r, 10)); + + expect(repeater.state.repeatedPanels?.length).toBe(1); + console.error = origError; + }); + it('Should adjust container height to fit panels direction is horizontal', async () => { const { scene, repeater } = buildPanelRepeaterScene({ variableQueryTime: 0, maxPerRow: 2, itemHeight: 10 }); diff --git a/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx b/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx index 0a48d944190..d6d66259527 100644 --- a/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx +++ b/public/app/features/dashboard-scene/scene/DashboardGridItem.tsx @@ -167,12 +167,26 @@ export class DashboardGridItem extends SceneObjectBase i const panelToRepeat = this.state.body instanceof LibraryVizPanel ? this.state.body.state.panel! : this.state.body; const repeatedPanels: VizPanel[] = []; + // when variable has no options (due to error or similar) it will not render any panels at all + // adding a placeholder in this case so that there is at least empty panel that can display error + const emptyVariablePlaceholderOption = { + values: [''], + texts: variable.hasAllValue() ? ['All'] : ['None'], + }; + + const variableValues = values.length ? values : emptyVariablePlaceholderOption.values; + const variableTexts = texts.length ? texts : emptyVariablePlaceholderOption.texts; + // Loop through variable values and create repeats - for (let index = 0; index < values.length; index++) { + for (let index = 0; index < variableValues.length; index++) { const cloneState: Partial = { $variables: new SceneVariableSet({ variables: [ - new LocalValueVariable({ name: variable.state.name, value: values[index], text: String(texts[index]) }), + new LocalValueVariable({ + name: variable.state.name, + value: variableValues[index], + text: String(variableTexts[index]), + }), ], }), key: `${panelToRepeat.state.key}-clone-${index}`, diff --git a/public/app/features/dashboard-scene/scene/RowRepeaterBehavior.ts b/public/app/features/dashboard-scene/scene/RowRepeaterBehavior.ts index 09b590a9a47..12d11116678 100644 --- a/public/app/features/dashboard-scene/scene/RowRepeaterBehavior.ts +++ b/public/app/features/dashboard-scene/scene/RowRepeaterBehavior.ts @@ -107,8 +107,10 @@ export class RowRepeaterBehavior extends SceneObjectBase