Improve TechDocs page titles (especially for nested pages) (#31054)

* Replace underscores in techdocs titles

Signed-off-by: Luna Stadler <luc@spreadshirt.net>

* Make techdocs titles similar to component titles

The pattern for components is entity name, page/tab and then app title.
This ordering makes it easier to distinguish tabs at a glance.

Signed-off-by: Luna Stadler <luc@spreadshirt.net>

* Abbreviate nested pages in techdocs

A deeply nested page like `/really/very/deeply/nested/page`, will now
become "Really | ... | Nested | Page".

This should preserve some of the context and support docs whith deeply
nested pages.

Signed-off-by: Luna Stadler <luc@spreadshirt.net>

* Add changeset for TechDocs page title improvements

Signed-off-by: Luna Stadler <luc@spreadshirt.net>

* Display the full title based on all parts of the path

Signed-off-by: Luna Stadler <luc@spreadshirt.net>

---------

Signed-off-by: Luna Stadler <luc@spreadshirt.net>
This commit is contained in:
Luna Stadler
2025-09-10 20:34:58 +02:00
committed by GitHub
parent d03010ec9b
commit 8d18d23e34
3 changed files with 35 additions and 8 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs': patch
---
TechDocs page titles have been improved, especially for deeply nested pages.
@@ -52,13 +52,11 @@ const mockTechDocsMetadata = {
site_description: 'test-site-desc',
};
const mockUseParams = jest.fn();
mockUseParams.mockReturnValue({ '*': 'foo/bar/baz/' });
let useParamsPath = '/';
jest.mock('react-router-dom', () => {
return {
...(jest.requireActual('react-router-dom') as any),
useParams: () => mockUseParams(),
useParams: () => ({ '*': useParamsPath }),
};
});
@@ -189,6 +187,7 @@ describe('<TechDocsReaderPageHeader />', () => {
getEntityMetadata.mockResolvedValue(mockEntityMetadata);
getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata);
useParamsPath = 'foo/bar/baz/';
await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
@@ -203,7 +202,31 @@ describe('<TechDocsReaderPageHeader />', () => {
await waitFor(() => {
expect(document.title).toEqual(
'Backstage | Test Entity | Foo | Bar | Baz',
'Test Entity | Foo | Bar | Baz | Backstage',
);
});
});
it('The header title is abbreviated if path is too long', async () => {
getEntityMetadata.mockResolvedValue(mockEntityMetadata);
getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata);
useParamsPath = 'foo/bar/baz/qux/quux/';
await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
'/docs': rootRouteRef,
},
},
);
await waitFor(() => {
expect(document.title).toEqual(
'Test Entity | Foo | Bar | Baz | Qux | Quux | Backstage',
);
});
});
@@ -172,17 +172,16 @@ export const TechDocsReaderPageHeader = (
const removeTrailingSlash = (str: string) => str.replace(/\/$/, '');
const normalizeAndSpace = (str: string) =>
str.replace(/-/g, ' ').split(' ').map(capitalize).join(' ');
str.replace(/[-_]/g, ' ').split(' ').map(capitalize).join(' ');
let techdocsTabTitleItems: string[] = [];
if (path !== '')
techdocsTabTitleItems = removeTrailingSlash(path)
.split('/')
.slice(0, 3)
.map(normalizeAndSpace);
const tabTitleItems = [appTitle, entityDisplayName, ...techdocsTabTitleItems];
const tabTitleItems = [entityDisplayName, ...techdocsTabTitleItems, appTitle];
const tabTitle = tabTitleItems.join(' | ');
return (