Address review feedback
- Rename 'alpha' to 'title' for content order enum value - Inline EntityContentGroupDefinition type into EntityContentGroupDefinitions - Rename defaultContentOrder prop to contentOrder - Apply content ordering to ungrouped tabs as well - Fix no-nested-ternary lint error Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -2,4 +2,4 @@
|
||||
'@backstage/plugin-catalog': minor
|
||||
---
|
||||
|
||||
Added support for group alias IDs and configurable content ordering on the entity page. Groups can now declare `aliases` so that content targeting an aliased group is included in the group. A new `contentOrder` option (default `alpha`) controls how content items within each group are sorted, with support for both a page-level default and per-group overrides.
|
||||
Added support for group alias IDs and configurable content ordering on the entity page. Groups can now declare `aliases` so that content targeting an aliased group is included in the group. A new `contentOrder` option (default `title`) controls how content items within each group are sorted, with support for both a page-level default and per-group overrides.
|
||||
|
||||
@@ -48,8 +48,8 @@ app:
|
||||
- page:catalog/entity:
|
||||
config:
|
||||
showNavItemIcons: true
|
||||
# default content order for all groups, can be 'alpha' or 'natural'
|
||||
# contentOrder: alpha
|
||||
# default content order for all groups, can be 'title' or 'natural'
|
||||
# contentOrder: title
|
||||
groups:
|
||||
# placing a tab at the beginning
|
||||
- overview:
|
||||
|
||||
@@ -339,18 +339,15 @@ export const EntityContentBlueprint: ExtensionBlueprint<{
|
||||
};
|
||||
}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type EntityContentGroupDefinition = {
|
||||
title: string;
|
||||
icon?: string | ReactElement;
|
||||
aliases?: string[];
|
||||
contentOrder?: 'alpha' | 'natural';
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type EntityContentGroupDefinitions = Record<
|
||||
string,
|
||||
EntityContentGroupDefinition
|
||||
{
|
||||
title: string;
|
||||
icon?: string | ReactElement;
|
||||
aliases?: string[];
|
||||
contentOrder?: 'title' | 'natural';
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
|
||||
@@ -41,20 +41,17 @@ export const entityFilterExpressionDataRef =
|
||||
id: 'catalog.entity-filter-expression',
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
export type EntityContentGroupDefinition = {
|
||||
title: string;
|
||||
icon?: string | ReactElement;
|
||||
/** Other group IDs that should be treated as aliases for this group. */
|
||||
aliases?: string[];
|
||||
/** How to sort the content items within this group. Overrides the page-level default. */
|
||||
contentOrder?: 'alpha' | 'natural';
|
||||
};
|
||||
|
||||
/** @alpha */
|
||||
export type EntityContentGroupDefinitions = Record<
|
||||
string,
|
||||
EntityContentGroupDefinition
|
||||
{
|
||||
title: string;
|
||||
icon?: string | ReactElement;
|
||||
/** Other group IDs that should be treated as aliases for this group. */
|
||||
aliases?: string[];
|
||||
/** How to sort the content items within this group. Overrides the page-level default. */
|
||||
contentOrder?: 'title' | 'natural';
|
||||
}
|
||||
>;
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,6 @@ export { EntityHeaderBlueprint } from './EntityHeaderBlueprint';
|
||||
export {
|
||||
defaultEntityContentGroups,
|
||||
defaultEntityContentGroupDefinitions,
|
||||
type EntityContentGroupDefinition,
|
||||
type EntityContentGroupDefinitions,
|
||||
} from './extensionData';
|
||||
export type { EntityCardType } from './extensionData';
|
||||
|
||||
@@ -1097,12 +1097,12 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
title: string;
|
||||
icon?: string | undefined;
|
||||
contentOrder?: 'alpha' | 'natural' | undefined;
|
||||
contentOrder?: 'title' | 'natural' | undefined;
|
||||
aliases?: string[] | undefined;
|
||||
}
|
||||
>[]
|
||||
| undefined;
|
||||
contentOrder: 'alpha' | 'natural';
|
||||
contentOrder: 'title' | 'natural';
|
||||
showNavItemIcons: boolean;
|
||||
path: string | undefined;
|
||||
title: string | undefined;
|
||||
@@ -1114,13 +1114,13 @@ const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
title: string;
|
||||
icon?: string | undefined;
|
||||
contentOrder?: 'alpha' | 'natural' | undefined;
|
||||
contentOrder?: 'title' | 'natural' | undefined;
|
||||
aliases?: string[] | undefined;
|
||||
}
|
||||
>[]
|
||||
| undefined;
|
||||
contentOrder?: 'title' | 'natural' | undefined;
|
||||
showNavItemIcons?: boolean | undefined;
|
||||
contentOrder?: 'alpha' | 'natural' | undefined;
|
||||
title?: string | undefined;
|
||||
path?: string | undefined;
|
||||
};
|
||||
|
||||
@@ -80,7 +80,7 @@ export interface EntityLayoutProps {
|
||||
*/
|
||||
parentEntityRelations?: string[];
|
||||
groupDefinitions: EntityContentGroupDefinitions;
|
||||
defaultContentOrder?: 'alpha' | 'natural';
|
||||
contentOrder?: 'title' | 'natural';
|
||||
showNavItemIcons?: boolean;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ export const EntityLayout = (props: EntityLayoutProps) => {
|
||||
NotFoundComponent,
|
||||
parentEntityRelations,
|
||||
groupDefinitions,
|
||||
defaultContentOrder,
|
||||
contentOrder,
|
||||
showNavItemIcons,
|
||||
} = props;
|
||||
const { kind } = useRouteRefParams(entityRouteRef);
|
||||
@@ -166,7 +166,7 @@ export const EntityLayout = (props: EntityLayoutProps) => {
|
||||
<EntityTabs
|
||||
routes={routes}
|
||||
groupDefinitions={groupDefinitions}
|
||||
defaultContentOrder={defaultContentOrder}
|
||||
contentOrder={contentOrder}
|
||||
showIcons={showNavItemIcons}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -72,12 +72,12 @@ export function useSelectedSubRoute(subRoutes: SubRoute[]): {
|
||||
type EntityTabsProps = {
|
||||
routes: SubRoute[];
|
||||
groupDefinitions: EntityContentGroupDefinitions;
|
||||
defaultContentOrder?: 'alpha' | 'natural';
|
||||
contentOrder?: 'title' | 'natural';
|
||||
showIcons?: boolean;
|
||||
};
|
||||
|
||||
export function EntityTabs(props: EntityTabsProps) {
|
||||
const { routes, groupDefinitions, defaultContentOrder, showIcons } = props;
|
||||
const { routes, groupDefinitions, contentOrder, showIcons } = props;
|
||||
|
||||
const { index, route, element } = useSelectedSubRoute(routes);
|
||||
|
||||
@@ -108,7 +108,7 @@ export function EntityTabs(props: EntityTabsProps) {
|
||||
selectedIndex={index}
|
||||
showIcons={showIcons}
|
||||
groupDefinitions={groupDefinitions}
|
||||
defaultContentOrder={defaultContentOrder}
|
||||
contentOrder={contentOrder}
|
||||
/>
|
||||
<EntityTabsPanel>
|
||||
<Helmet title={route?.title} />
|
||||
|
||||
@@ -78,11 +78,25 @@ type TabGroup = {
|
||||
type EntityTabsListProps = {
|
||||
tabs: Tab[];
|
||||
groupDefinitions: EntityContentGroupDefinitions;
|
||||
defaultContentOrder?: 'alpha' | 'natural';
|
||||
contentOrder?: 'title' | 'natural';
|
||||
showIcons?: boolean;
|
||||
selectedIndex?: number;
|
||||
};
|
||||
|
||||
function resolveGroupId(
|
||||
tabGroup: string | undefined,
|
||||
groupDefinitions: EntityContentGroupDefinitions,
|
||||
aliasToGroup: Record<string, string>,
|
||||
): string | undefined {
|
||||
if (!tabGroup) {
|
||||
return undefined;
|
||||
}
|
||||
if (groupDefinitions[tabGroup]) {
|
||||
return tabGroup;
|
||||
}
|
||||
return aliasToGroup[tabGroup];
|
||||
}
|
||||
|
||||
export function EntityTabsList(props: EntityTabsListProps) {
|
||||
const styles = useStyles();
|
||||
const { t } = useTranslationRef(catalogTranslationRef);
|
||||
@@ -92,7 +106,7 @@ export function EntityTabsList(props: EntityTabsListProps) {
|
||||
selectedIndex = 0,
|
||||
showIcons,
|
||||
groupDefinitions,
|
||||
defaultContentOrder = 'alpha',
|
||||
contentOrder = 'title',
|
||||
} = props;
|
||||
|
||||
const aliasToGroup = useMemo(
|
||||
@@ -108,11 +122,11 @@ export function EntityTabsList(props: EntityTabsListProps) {
|
||||
|
||||
const groups = useMemo(() => {
|
||||
const byKey = items.reduce((result, tab) => {
|
||||
const resolvedGroupId = tab.group
|
||||
? groupDefinitions[tab.group]
|
||||
? tab.group
|
||||
: aliasToGroup[tab.group]
|
||||
: undefined;
|
||||
const resolvedGroupId = resolveGroupId(
|
||||
tab.group,
|
||||
groupDefinitions,
|
||||
aliasToGroup,
|
||||
);
|
||||
const group = resolvedGroupId
|
||||
? groupDefinitions[resolvedGroupId]
|
||||
: undefined;
|
||||
@@ -143,8 +157,8 @@ export function EntityTabsList(props: EntityTabsListProps) {
|
||||
|
||||
for (const [id, tabGroup] of sorted) {
|
||||
const groupDef = groupDefinitions[id];
|
||||
const order = groupDef?.contentOrder ?? defaultContentOrder;
|
||||
if (order === 'alpha') {
|
||||
const order = groupDef?.contentOrder ?? contentOrder;
|
||||
if (order === 'title') {
|
||||
tabGroup.items.sort((a, b) =>
|
||||
a.label.localeCompare(b.label, undefined, { sensitivity: 'base' }),
|
||||
);
|
||||
@@ -152,7 +166,7 @@ export function EntityTabsList(props: EntityTabsListProps) {
|
||||
}
|
||||
|
||||
return sorted;
|
||||
}, [items, groupDefinitions, aliasToGroup, defaultContentOrder]);
|
||||
}, [items, groupDefinitions, aliasToGroup, contentOrder]);
|
||||
|
||||
const selectedItem = items[selectedIndex];
|
||||
return (
|
||||
|
||||
@@ -482,7 +482,7 @@ describe('Entity page', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Should sort content alphabetically by default', async () => {
|
||||
it('Should sort content by title by default', async () => {
|
||||
const tester = createExtensionTester(
|
||||
Object.assign({ namespace: 'catalog' }, catalogEntityPage),
|
||||
)
|
||||
@@ -564,7 +564,7 @@ describe('Entity page', () => {
|
||||
Object.assign({ namespace: 'catalog' }, catalogEntityPage),
|
||||
{
|
||||
config: {
|
||||
contentOrder: 'alpha',
|
||||
contentOrder: 'title',
|
||||
groups: [
|
||||
{
|
||||
documentation: {
|
||||
|
||||
@@ -110,13 +110,13 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({
|
||||
title: z.string(),
|
||||
icon: z.string().optional(),
|
||||
aliases: z.array(z.string()).optional(),
|
||||
contentOrder: z.enum(['alpha', 'natural']).optional(),
|
||||
contentOrder: z.enum(['title', 'natural']).optional(),
|
||||
}),
|
||||
),
|
||||
)
|
||||
.optional(),
|
||||
contentOrder: z =>
|
||||
z.enum(['alpha', 'natural']).optional().default('alpha'),
|
||||
z.enum(['title', 'natural']).optional().default('title'),
|
||||
showNavItemIcons: z => z.boolean().optional().default(false),
|
||||
},
|
||||
},
|
||||
@@ -178,7 +178,7 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({
|
||||
header={header}
|
||||
contextMenuItems={filteredMenuItems}
|
||||
groupDefinitions={groupDefinitions}
|
||||
defaultContentOrder={config.contentOrder}
|
||||
contentOrder={config.contentOrder}
|
||||
showNavItemIcons={config.showNavItemIcons}
|
||||
>
|
||||
{inputs.contents.map(output => (
|
||||
|
||||
Reference in New Issue
Block a user