From 16bd94e0386a914f66585ddb3f5ad496bd5a2bfe Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 29 Jan 2025 09:59:49 +0100 Subject: [PATCH 1/4] catalog: add inspect dialog url Signed-off-by: Vincenzo Scamporlino --- .../InspectEntityDialog.tsx | 46 +++++++++++++++---- .../components/EntityLayout/EntityLayout.tsx | 33 ++++++++----- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/plugins/catalog-react/src/components/InspectEntityDialog/InspectEntityDialog.tsx b/plugins/catalog-react/src/components/InspectEntityDialog/InspectEntityDialog.tsx index 644167acbb..bcd84003ba 100644 --- a/plugins/catalog-react/src/components/InspectEntityDialog/InspectEntityDialog.tsx +++ b/plugins/catalog-react/src/components/InspectEntityDialog/InspectEntityDialog.tsx @@ -24,7 +24,7 @@ import DialogTitle from '@material-ui/core/DialogTitle'; import Tab from '@material-ui/core/Tab'; import Tabs from '@material-ui/core/Tabs'; import { makeStyles } from '@material-ui/core/styles'; -import React, { useEffect } from 'react'; +import React, { ComponentProps, useEffect } from 'react'; import { AncestryPage } from './components/AncestryPage'; import { ColocatedPage } from './components/ColocatedPage'; import { JsonPage } from './components/JsonPage'; @@ -85,6 +85,19 @@ function a11yProps(index: number) { }; } +const tabNames: Record< + NonNullable['initialTab']>, + string +> = { + overview: 'Overview', + ancestry: 'Ancestry', + colocated: 'Colocated', + json: 'Raw JSON', + yaml: 'Raw YAML', +} as const; + +const tabs = Object.keys(tabNames) as Array; + /** * A dialog that lets users inspect the low level details of their entities. * @@ -93,15 +106,20 @@ function a11yProps(index: number) { export function InspectEntityDialog(props: { open: boolean; entity: Entity; + initialTab?: 'overview' | 'ancestry' | 'colocated' | 'json' | 'yaml'; onClose: () => void; + onSelect?: (tab: string) => void; }) { const classes = useStyles(); - const [activeTab, setActiveTab] = React.useState(0); + + const [activeTab, setActiveTab] = React.useState( + getTabIndex(tabs, props.initialTab), + ); const { t } = useTranslationRef(catalogReactTranslationRef); useEffect(() => { - setActiveTab(0); - }, [props.open]); + getTabIndex(tabs, props.initialTab); + }, [props.open, props.initialTab]); if (!props.entity) { return null; @@ -125,15 +143,16 @@ export function InspectEntityDialog(props: { orientation="vertical" variant="scrollable" value={activeTab} - onChange={(_, newValue) => setActiveTab(newValue)} + onChange={(_, tabIndex) => { + setActiveTab(tabIndex); + props.onSelect?.(tabs[tabIndex]); + }} aria-label="Inspector options" className={classes.tabs} > - - - - - + {tabs.map((tab, index) => ( + + ))} @@ -161,3 +180,10 @@ export function InspectEntityDialog(props: { ); } + +function getTabIndex( + allTabs: string[], + initialTab: keyof typeof tabNames | undefined, +) { + return initialTab ? allTabs.indexOf(initialTab) : 0; +} diff --git a/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx b/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx index c06344c2a6..c1dff49c29 100644 --- a/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx +++ b/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx @@ -56,8 +56,8 @@ import Box from '@material-ui/core/Box'; import { makeStyles } from '@material-ui/core/styles'; import { TabProps } from '@material-ui/core/Tab'; import Alert from '@material-ui/lab/Alert'; -import React, { useEffect, useState } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; +import React, { ComponentProps, useEffect, useState } from 'react'; +import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'; import useAsync from 'react-use/esm/useAsync'; import { catalogTranslationRef } from '../../alpha/translation'; import { rootRouteRef, unregisterRedirectRouteRef } from '../../routes'; @@ -284,15 +284,15 @@ export const EntityLayout = (props: EntityLayoutProps) => { ); const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); - const [inspectionDialogOpen, setInspectionDialogOpen] = useState(false); const navigate = useNavigate(); + const [searchParams, setSearchParams] = useSearchParams(); + const catalogRoute = useRouteRef(rootRouteRef); const unregisterRedirectRoute = useRouteRef(unregisterRedirectRouteRef); const { t } = useTranslationRef(catalogTranslationRef); const cleanUpAfterRemoval = async () => { setConfirmationDialogOpen(false); - setInspectionDialogOpen(false); navigate( unregisterRedirectRoute ? unregisterRedirectRoute() : catalogRoute(), ); @@ -318,10 +318,12 @@ export const EntityLayout = (props: EntityLayoutProps) => { // to another entity. useEffect(() => { setConfirmationDialogOpen(false); - setInspectionDialogOpen(false); // eslint-disable-next-line react-hooks/exhaustive-deps }, [location.pathname]); + const selectedInspectTab = searchParams.get('inspect'); + const showInspectTab = typeof selectedInspectTab === 'string'; + return (
{ UNSTABLE_extraContextMenuItems={UNSTABLE_extraContextMenuItems} UNSTABLE_contextMenuOptions={UNSTABLE_contextMenuOptions} onUnregisterEntity={() => setConfirmationDialogOpen(true)} - onInspectEntity={() => setInspectionDialogOpen(true)} + onInspectEntity={() => setSearchParams('inspect')} /> )} @@ -385,17 +387,26 @@ export const EntityLayout = (props: EntityLayoutProps) => { )} + {showInspectTab && ( + ['initialTab']) || undefined + } + onSelect={newTab => setSearchParams(`inspect=${newTab}`)} + open + onClose={() => setSearchParams()} + /> + )} + setConfirmationDialogOpen(false)} /> - setInspectionDialogOpen(false)} - /> ); }; From b07756e4553d7ef42767fe586e7667f78e1fdcd1 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 29 Jan 2025 10:03:37 +0100 Subject: [PATCH 2/4] catalog: inspect dialog changeset Signed-off-by: Vincenzo Scamporlino --- .changeset/silly-vans-shop.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/silly-vans-shop.md diff --git a/.changeset/silly-vans-shop.md b/.changeset/silly-vans-shop.md new file mode 100644 index 0000000000..7bd283adbc --- /dev/null +++ b/.changeset/silly-vans-shop.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-catalog': patch +--- + +- The Entity Page now retains the visibility of the Inspect Dialog after a reload. This allows sharing the URL with the dialog open. From 719e48dbb4220553c4b81d7795e4cc233633435c Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 29 Jan 2025 10:15:30 +0100 Subject: [PATCH 3/4] catalog-graph: fix flickering when parent re-renders Signed-off-by: Vincenzo Scamporlino --- .changeset/short-keys-heal.md | 5 +++++ .../src/components/CatalogGraphCard/CatalogGraphCard.tsx | 4 ++-- plugins/catalog-react/report.api.md | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 .changeset/short-keys-heal.md diff --git a/.changeset/short-keys-heal.md b/.changeset/short-keys-heal.md new file mode 100644 index 0000000000..0c8b26055f --- /dev/null +++ b/.changeset/short-keys-heal.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-graph': patch +--- + +Fixed an issue causing the `CatalogGraphCard` to redraw its content whenever the parent component re-renders, resulting in flickering. diff --git a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx index e7ac8bf15b..73b4f2f2ee 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx @@ -28,7 +28,7 @@ import { } from '@backstage/plugin-catalog-react'; import { makeStyles, Theme } from '@material-ui/core/styles'; import qs from 'qs'; -import React, { MouseEvent, ReactNode, useCallback } from 'react'; +import React, { MouseEvent, ReactNode, useCallback, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import { catalogGraphRouteRef } from '../../routes'; import { @@ -86,7 +86,7 @@ export const CatalogGraphCard = ( } = props; const { entity } = useEntity(); - const entityName = getCompoundEntityRef(entity); + const entityName = useMemo(() => getCompoundEntityRef(entity), [entity]); const catalogEntityRoute = useRouteRef(entityRouteRef); const catalogGraphRoute = useRouteRef(catalogGraphRouteRef); const navigate = useNavigate(); diff --git a/plugins/catalog-react/report.api.md b/plugins/catalog-react/report.api.md index 1e637f79a5..b3495faf08 100644 --- a/plugins/catalog-react/report.api.md +++ b/plugins/catalog-react/report.api.md @@ -685,7 +685,9 @@ export function humanizeEntityRef( export function InspectEntityDialog(props: { open: boolean; entity: Entity; + initialTab?: 'overview' | 'ancestry' | 'colocated' | 'json' | 'yaml'; onClose: () => void; + onSelect?: (tab: string) => void; }): React_2.JSX.Element | null; // @public From 8b76bdb56fc47847e88bac7d6fcd23f4e60d6a92 Mon Sep 17 00:00:00 2001 From: Vincenzo Scamporlino Date: Wed, 29 Jan 2025 10:45:42 +0100 Subject: [PATCH 4/4] catalog: tweak changeset Signed-off-by: Vincenzo Scamporlino --- .changeset/silly-vans-shop.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/silly-vans-shop.md b/.changeset/silly-vans-shop.md index 7bd283adbc..7af818650b 100644 --- a/.changeset/silly-vans-shop.md +++ b/.changeset/silly-vans-shop.md @@ -1,6 +1,6 @@ --- '@backstage/plugin-catalog-react': patch -'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog': minor --- -- The Entity Page now retains the visibility of the Inspect Dialog after a reload. This allows sharing the URL with the dialog open. +The Entity Page now retains the visibility of the Inspect Dialog after a reload. This allows sharing the URL with the dialog open.