From de8930b92ab776c6e7ac380d9a719eb0c2ed2780 Mon Sep 17 00:00:00 2001 From: Ashley Harrison Date: Fri, 29 Aug 2025 09:05:46 +0100 Subject: [PATCH] Chore: Define components outside of the scene class (#110180) define components outside of the scene class --- .betterer.results | 19 +- .../inspect/InspectJsonTab.tsx | 80 ++--- .../panel-edit/PanelOptionsPane.tsx | 140 ++++---- .../saving/SaveDashboardDrawer.tsx | 128 +++---- .../settings/GeneralSettingsEditView.tsx | 329 +++++++++--------- .../settings/JsonModelEditView.tsx | 272 ++++++++------- 6 files changed, 477 insertions(+), 491 deletions(-) diff --git a/.betterer.results b/.betterer.results index 1a4c5338c06..181f71742b4 100644 --- a/.betterer.results +++ b/.betterer.results @@ -1734,8 +1734,7 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"] ], "public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx:5381": [ - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], - [0, 0, 0, "React Hook \\"useStyles2\\" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.", "1"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], "public/app/features/dashboard-scene/pages/DashboardScenePage.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -1752,9 +1751,7 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], "public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.tsx:5381": [ - [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], - [0, 0, 0, "React Hook \\"useStyles2\\" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.", "1"], - [0, 0, 0, "React Hook \\"useToggle\\" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.", "2"] + [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], "public/app/features/dashboard-scene/panel-edit/PanelVizTypePicker.tsx:5381": [ [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] @@ -1765,9 +1762,6 @@ exports[`better eslint`] = { [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "2"], [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "3"] ], - "public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx:5381": [ - [0, 0, 0, "React Hook \\"useIsProvisionedNG\\" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.", "0"] - ], "public/app/features/dashboard-scene/saving/SaveDashboardForm.tsx:5381": [ [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"] ], @@ -1866,15 +1860,6 @@ exports[`better eslint`] = { "public/app/features/dashboard-scene/serialization/transformToV1TypesUtils.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], - "public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx:5381": [ - [0, 0, 0, "React Hook \\"useDashboardEditPageNav\\" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.", "0"] - ], - "public/app/features/dashboard-scene/settings/JsonModelEditView.tsx:5381": [ - [0, 0, 0, "React Hook \\"useDashboardEditPageNav\\" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.", "0"], - [0, 0, 0, "React Hook \\"useSaveDashboard\\" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.", "1"], - [0, 0, 0, "React Hook \\"useState\\" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.", "2"], - [0, 0, 0, "React Hook \\"useStyles2\\" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function.", "3"] - ], "public/app/features/dashboard-scene/settings/annotations/AnnotationSettingsEdit.tsx:5381": [ [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"], [0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"], diff --git a/public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx b/public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx index fbe3044ef20..6085174b9af 100644 --- a/public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx +++ b/public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx @@ -166,48 +166,50 @@ export class InspectJsonTab extends SceneObjectBase { return dashboard.state.meta.canEdit; } - static Component = ({ model }: SceneComponentProps) => { - const { source: show, jsonText } = model.useState(); - const styles = useStyles2(getPanelInspectorStyles2); - const options = model.getOptions(); + static Component = InspectJsonTabComponent; +} - return ( -
-
- - v.value === show) ?? options[0].value} + onChange={model.onChangeSource} + /> + + {model.isEditable() && ( + + )}
- ); - }; + +
+ + {({ height }) => ( + 100} + value={jsonText} + readOnly={!model.isEditable()} + onBlur={model.onCodeEditorBlur} + /> + )} + +
+
+ ); } function getJsonText(show: ShowContent, panel: VizPanel): string { diff --git a/public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.tsx b/public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.tsx index 0358184401f..5540da1f494 100644 --- a/public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.tsx +++ b/public/app/features/dashboard-scene/panel-edit/PanelOptionsPane.tsx @@ -119,78 +119,80 @@ export class PanelOptionsPane extends SceneObjectBase { ]; } - static Component = ({ model }: SceneComponentProps) => { - const { isVizPickerOpen, searchQuery, listMode, panelRef } = model.useState(); - const panel = panelRef.resolve(); - const { pluginId } = panel.useState(); - const { data } = sceneGraph.getData(panel).useState(); - const styles = useStyles2(getStyles); - const isSearching = searchQuery.length > 0; - const hasFieldConfig = !isSearching && !panel.getPlugin()?.fieldConfigRegistry.isEmpty(); - const [isSearchingOptions, setIsSearchingOptions] = useToggle(false); - const onlyOverrides = listMode === OptionFilter.Overrides; + static Component = PanelOptionsPaneComponent; +} - return ( - <> - {!isVizPickerOpen && ( - <> -
- - - -
- - - - - )} - {isVizPickerOpen && ( - - )} - - ); - }; + {hasFieldConfig && ( + { + model.onSetListMode(onlyOverrides ? OptionFilter.All : OptionFilter.Overrides); + }} + /> + )} + + + + {isSearchingOptions && ( + { + if (searchQuery.length === 0) { + setIsSearchingOptions(false); + } + }} + /> + )} + + + + + + )} + {isVizPickerOpen && ( + + )} + + ); } function getStyles(theme: GrafanaTheme2) { diff --git a/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx b/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx index 09a45b1bb8c..9e7ea7c8357 100644 --- a/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx +++ b/public/app/features/dashboard-scene/saving/SaveDashboardDrawer.tsx @@ -45,80 +45,80 @@ export class SaveDashboardDrawer extends SceneObjectBase) => { - const { showDiff, saveAsCopy, saveTimeRange, saveVariables, saveRefresh } = model.useState(); + static Component = SaveDashboardDrawerComponent; +} - const changeInfo = model.state.dashboardRef - .resolve() - .getDashboardChanges(saveTimeRange, saveVariables, saveRefresh); +function SaveDashboardDrawerComponent({ model }: SceneComponentProps) { + const { showDiff, saveAsCopy, saveTimeRange, saveVariables, saveRefresh } = model.useState(); - const { changedSaveModel, initialSaveModel, diffs, diffCount, hasFolderChanges, hasMigratedToV2 } = changeInfo; - const changesCount = diffCount + (hasFolderChanges ? 1 : 0); - const dashboard = model.state.dashboardRef.resolve(); - const { meta } = dashboard.useState(); - const { provisioned: isProvisioned, folderTitle } = meta; - const managedResourceCannotBeEdited = dashboard.managedResourceCannotBeEdited(); - const isProvisionedNG = useIsProvisionedNG(dashboard); + const changeInfo = model.state.dashboardRef.resolve().getDashboardChanges(saveTimeRange, saveVariables, saveRefresh); - const tabs = ( - + const { changedSaveModel, initialSaveModel, diffs, diffCount, hasFolderChanges, hasMigratedToV2 } = changeInfo; + const changesCount = diffCount + (hasFolderChanges ? 1 : 0); + const dashboard = model.state.dashboardRef.resolve(); + const { meta } = dashboard.useState(); + const { provisioned: isProvisioned, folderTitle } = meta; + const managedResourceCannotBeEdited = dashboard.managedResourceCannotBeEdited(); + const isProvisionedNG = useIsProvisionedNG(dashboard); + + const tabs = ( + + model.setState({ showDiff: false })} + /> + {changesCount > 0 && !managedResourceCannotBeEdited && ( model.setState({ showDiff: false })} + label={t('dashboard-scene.save-dashboard-drawer.tabs.label-changes', 'Changes')} + active={showDiff} + onChangeTab={() => model.setState({ showDiff: true })} + counter={changesCount} /> - {changesCount > 0 && !managedResourceCannotBeEdited && ( - model.setState({ showDiff: true })} - counter={changesCount} - /> - )} - - ); + )} + + ); - let title = t('dashboard-scene.save-dashboard-drawer.tabs.title', 'Save dashboard'); - if (saveAsCopy) { - title = t('dashboard-scene.save-dashboard-drawer.tabs.title-copy', 'Save dashboard copy'); - } else if (isProvisioned || isProvisionedNG) { - title = t('dashboard-scene.save-dashboard-drawer.tabs.title-provisioned', 'Provisioned dashboard'); + let title = t('dashboard-scene.save-dashboard-drawer.tabs.title', 'Save dashboard'); + if (saveAsCopy) { + title = t('dashboard-scene.save-dashboard-drawer.tabs.title-copy', 'Save dashboard copy'); + } else if (isProvisioned || isProvisionedNG) { + title = t('dashboard-scene.save-dashboard-drawer.tabs.title-provisioned', 'Provisioned dashboard'); + } + + const renderBody = () => { + if (showDiff) { + return ( + + ); } - const renderBody = () => { - if (showDiff) { - return ( - - ); - } + if (isProvisionedNG) { + return ; + } - if (isProvisionedNG) { - return ; - } + if (saveAsCopy || changeInfo.isNew) { + return ; + } - if (saveAsCopy || changeInfo.isNew) { - return ; - } + if (isProvisioned || managedResourceCannotBeEdited) { + return ; + } - if (isProvisioned || managedResourceCannotBeEdited) { - return ; - } - - return ; - }; - - return ( - - {renderBody()} - - ); + return ; }; + + return ( + + {renderBody()} + + ); } diff --git a/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx b/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx index aee4461f43e..c30513be7e8 100644 --- a/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx +++ b/public/app/features/dashboard-scene/settings/GeneralSettingsEditView.tsx @@ -182,7 +182,7 @@ export class GeneralSettingsEditView }); }; - private onMoveSuccess = (folderUID: string, folderTitle: string) => { + public onMoveSuccess = (folderUID: string, folderTitle: string) => { const newMeta = { ...this._dashboard.state.meta, folderUid: folderUID, @@ -192,181 +192,176 @@ export class GeneralSettingsEditView this.onMoveModalDismiss(); }; - static Component = ({ model }: SceneComponentProps) => { - const dashboard = model.getDashboard(); - const { navModel, pageNav } = useDashboardEditPageNav(dashboard, model.getUrlKey()); - const { title, description, tags, meta, editable } = dashboard.useState(); - const { showMoveModal, moveModalProps } = model.useState(); - const { sync: graphTooltip } = model.getCursorSync()?.useState() || {}; - const { timeZone, weekStart, UNSAFE_nowDelay: nowDelay } = model.getTimeRange().useState(); - const { intervals } = model.getRefreshPicker().useState(); - const { hideTimeControls } = model.getDashboardControls().useState(); - const { enabled: liveNow } = model.getLiveNowTimer().useState(); - const EDITABLE_OPTIONS = [ - { - label: t('dashboard-scene.general-settings-edit-view.editable_options.label.editable', 'Editable'), - value: true, - }, - { - label: t('dashboard-scene.general-settings-edit-view.editable_options.label.readonly', 'Read-only'), - value: false, - }, - ]; + static Component = GeneralSettingsEditViewComponent; +} - const GRAPH_TOOLTIP_OPTIONS = [ - { - value: 0, - label: t('dashboard-scene.general-settings-edit-view.graph_tooltip_options.label.default', 'Default'), - }, - { - value: 1, - label: t( - 'dashboard-scene.general-settings-edit-view.graph_tooltip_options.label.shared-crosshair', - 'Shared crosshair' - ), - }, - { - value: 2, - label: t( - 'dashboard-scene.general-settings-edit-view.graph_tooltip_options.label.shared-tooltip', - 'Shared tooltip' - ), - }, - ]; +function GeneralSettingsEditViewComponent({ model }: SceneComponentProps) { + const dashboard = model.getDashboard(); + const { navModel, pageNav } = useDashboardEditPageNav(dashboard, model.getUrlKey()); + const { title, description, tags, meta, editable } = dashboard.useState(); + const { showMoveModal, moveModalProps } = model.useState(); + const { sync: graphTooltip } = model.getCursorSync()?.useState() || {}; + const { timeZone, weekStart, UNSAFE_nowDelay: nowDelay } = model.getTimeRange().useState(); + const { intervals } = model.getRefreshPicker().useState(); + const { hideTimeControls } = model.getDashboardControls().useState(); + const { enabled: liveNow } = model.getLiveNowTimer().useState(); + const EDITABLE_OPTIONS = [ + { + label: t('dashboard-scene.general-settings-edit-view.editable_options.label.editable', 'Editable'), + value: true, + }, + { + label: t('dashboard-scene.general-settings-edit-view.editable_options.label.readonly', 'Read-only'), + value: false, + }, + ]; - return ( - - -
- + const GRAPH_TOOLTIP_OPTIONS = [ + { + value: 0, + label: t('dashboard-scene.general-settings-edit-view.graph_tooltip_options.label.default', 'Default'), + }, + { + value: 1, + label: t( + 'dashboard-scene.general-settings-edit-view.graph_tooltip_options.label.shared-crosshair', + 'Shared crosshair' + ), + }, + { + value: 2, + label: t( + 'dashboard-scene.general-settings-edit-view.graph_tooltip_options.label.shared-tooltip', + 'Shared tooltip' + ), + }, + ]; + + return ( + + +
+ + + + {config.featureToggles.dashgpt && ( + model.onTitleChange(title)} /> + )} + + } + > + ) => model.onTitleChange(e.target.value)} + /> + + + + {config.featureToggles.dashgpt && ( + model.onDescriptionChange(description)} /> + )} + + } + > +