Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to specify nav section ordering by plugins #7275

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
122 changes: 122 additions & 0 deletions frontend/__tests__/components/nav/perspective-nav.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { getSortedNavItems } from '@console/internal/components/nav/perspective-nav';
import { LoadedExtension, NavItem, NavSection, SeparatorNavItem } from '@console/plugin-sdk/src';

const mockNavItems: LoadedExtension<NavSection | NavItem | SeparatorNavItem>[] = [
{
type: 'Nav/Section',
properties: {
id: 'test1',
},
pluginID: 'test-plugin-id',
pluginName: 'test-plugin-name',
uid: 'test-plugin-uid',
},
{
type: 'Nav/Section',
properties: {
id: 'test2',
},
pluginID: 'test-plugin-id',
pluginName: 'test-plugin-name',
uid: 'test-plugin-uid',
},
{
type: 'Nav/Section',
properties: {
id: 'test3',
},
pluginID: 'test-plugin-id',
pluginName: 'test-plugin-name',
uid: 'test-plugin-uid',
},
{
type: 'Nav/Section',
properties: {
id: 'test4',
},
pluginID: 'test-plugin-id',
pluginName: 'test-plugin-name',
uid: 'test-plugin-uid',
},
{
type: 'Nav/Section',
properties: {
id: 'test5',
},
pluginID: 'test-plugin-id',
pluginName: 'test-plugin-name',
uid: 'test-plugin-uid',
},
{
type: 'Nav/Section',
properties: {
id: 'test6',
},
pluginID: 'test-plugin-id',
pluginName: 'test-plugin-name',
uid: 'test-plugin-uid',
},
{
type: 'NavItem/Separator',
properties: {
id: 'test7',
componentProps: {
testID: 'test-sep',
},
},
pluginID: 'test-plugin-id',
pluginName: 'test-plugin-name',
uid: 'test-plugin-uid',
},
];

describe('perspective-nav insertPositionedItems', () => {
it('should order items that are not positioned', () => {
const sortedItems = getSortedNavItems(mockNavItems);
expect(sortedItems.map((i) => i.properties.id)).toEqual(
mockNavItems.map((i) => i.properties.id),
);
});

it('should order items that are positioned', () => {
mockNavItems[0].properties.insertAfter = 'test2';
let sortedItems = getSortedNavItems(mockNavItems);
expect(sortedItems.map((i) => i.properties.id).indexOf('test1')).toBe(1);

delete mockNavItems[0].properties.insertAfter;
mockNavItems[0].properties.insertBefore = 'test5';
sortedItems = getSortedNavItems(mockNavItems);
expect(sortedItems.map((i) => i.properties.id).indexOf('test1')).toBe(3);

delete mockNavItems[0].properties.insertBefore;
mockNavItems[0].properties.insertAfter = ['x', 'y', 'test3', 'z'];
sortedItems = getSortedNavItems(mockNavItems);
expect(sortedItems.map((i) => i.properties.id).indexOf('test1')).toBe(2);

delete mockNavItems[0].properties.insertAfter;
mockNavItems[0].properties.insertBefore = ['x', 'y', 'test3', 'z'];
sortedItems = getSortedNavItems(mockNavItems);
expect(sortedItems.map((i) => i.properties.id).indexOf('test1')).toBe(1);

// Before takes precedence
mockNavItems[0].properties.insertAfter = 'test6';
sortedItems = getSortedNavItems(mockNavItems);
expect(sortedItems.map((i) => i.properties.id).indexOf('test1')).toBe(1);
});

it('should order items that are positioned on positioned items', () => {
delete mockNavItems[0].properties.insertBefore;
mockNavItems[0].properties.insertAfter = 'test6';
mockNavItems[5].properties.insertAfter = 'test4';
let sortedItems = getSortedNavItems(mockNavItems);
expect(sortedItems.map((i) => i.properties.id).indexOf('test6')).toBe(3);
expect(sortedItems.map((i) => i.properties.id).indexOf('test1')).toBe(4);
expect(sortedItems.map((i) => i.properties.id).indexOf('test7')).toBe(6);

mockNavItems[6].properties.insertBefore = 'test1';
sortedItems = getSortedNavItems(mockNavItems);
expect(sortedItems.map((i) => i.properties.id).indexOf('test6')).toBe(3);
expect(sortedItems.map((i) => i.properties.id).indexOf('test1')).toBe(5);
expect(sortedItems.map((i) => i.properties.id).indexOf('test7')).toBe(4);
});
});
4 changes: 2 additions & 2 deletions frontend/packages/console-app/src/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ const plugin: Plugin<ConsumedExtensions> = [
{
type: 'NavItem/ResourceCluster',
properties: {
id: 'storage',
section: 'Storage',
id: 'volumesnapshots',
section: 'storage',
componentProps: {
name: 'Volume Snapshot Contents',
resource: referenceForModel(VolumeSnapshotContentModel),
Expand Down
26 changes: 21 additions & 5 deletions frontend/packages/console-demo-plugin/src/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
ResourceListPage,
ResourceDetailsPage,
Perspective,
NavSection,
YAMLTemplate,
RoutePage,
DashboardsOverviewHealthPrometheusSubsystem,
Expand Down Expand Up @@ -43,6 +44,7 @@ type ConsumedExtensions =
| ResourceListPage
| ResourceDetailsPage
| Perspective
| NavSection
| YAMLTemplate
| RoutePage
| DashboardsOverviewHealthPrometheusSubsystem
Expand Down Expand Up @@ -73,10 +75,17 @@ const plugin: Plugin<ConsumedExtensions> = [
},
},
{
type: 'NavItem/Href',
type: 'Nav/Section',
properties: {
id: 'home',
section: 'Home',
name: 'Home',
},
},
{
type: 'NavItem/Href',
properties: {
id: 'testhreflink',
section: 'home',
componentProps: {
name: 'Test Href Link',
href: '/test',
Expand All @@ -90,7 +99,7 @@ const plugin: Plugin<ConsumedExtensions> = [
type: 'NavItem/ResourceNS',
properties: {
id: 'testresourcens',
section: 'Home',
section: 'home',
componentProps: {
name: 'Test ResourceNS Link',
resource: 'pods',
Expand All @@ -104,7 +113,7 @@ const plugin: Plugin<ConsumedExtensions> = [
type: 'NavItem/ResourceCluster',
properties: {
id: 'testresourcecluster',
section: 'Home',
section: 'home',
componentProps: {
name: 'Test ResourceCluster Link',
resource: 'projects',
Expand Down Expand Up @@ -191,12 +200,19 @@ const plugin: Plugin<ConsumedExtensions> = [
},
},
},
{
type: 'Nav/Section',
properties: {
id: 'advanced',
name: 'Advanced',
},
},
{
type: 'NavItem/ResourceCluster',
properties: {
id: 'testprojects',
perspective: 'test',
section: 'Advanced',
section: 'advanced',
componentProps: {
name: 'Test Projects',
resource: 'projects',
Expand Down
1 change: 1 addition & 0 deletions frontend/packages/console-plugin-sdk/src/typings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export * from './features';
export * from './kebab-actions';
export * from './models';
export * from './nav-items';
export * from './nav-section';
export * from './overview';
export * from './pages';
export * from './perspectives';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ namespace ExtensionProperties {
id: string;
/** Perspective id to which this item belongs to. If not specified, use the default perspective. */
perspective?: string;
/** Nav section to which this item belongs to.If not specified, render item as top-level link. */
/** Nav section to which this item belongs to. If not specified, render item as top-level link. */
section?: string;
/** Nav group to which this item belongs to. Add items to a grouping with a separator above */
group?: string;
/** Props to pass to the corresponding `NavLink` component. */
componentProps: Pick<NavLinkProps, 'name' | 'startsWith' | 'testID' | 'data-tour-id'>;
/*
Expand Down
24 changes: 24 additions & 0 deletions frontend/packages/console-plugin-sdk/src/typings/nav-section.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Extension } from './base';

namespace ExtensionProperties {
export interface NavSection {
/** Section id, should be unique for all sections (and top level items) */
id: string;
/** Perspective id to which this section belongs to. If not specified, use the default perspective. */
perspective?: string;
/** Title for the section, if none only a separator will be shown above the section */
name?: string;
/** Nav section before which this section should be placed. For arrays, first one found in order is used */
insertBefore?: string | string[];
/** Nav section after which this section should be placed (before takes precedence). For arrays, first one found in order is used */
insertAfter?: string | string[];
}
}

export interface NavSection extends Extension<ExtensionProperties.NavSection> {
type: 'Nav/Section';
}

export const isNavSection = (e: Extension): e is NavSection => {
return e.type === 'Nav/Section';
};
2 changes: 1 addition & 1 deletion frontend/packages/container-security/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ const plugin: Plugin<ConsumedExtensions> = [
properties: {
id: 'imagevulnerabilities',
perspective: 'admin',
section: 'Administration',
section: 'administration',
insertBefore: 'customresourcedefinitions',
componentProps: {
name: 'Image Vulnerabilities',
Expand Down
32 changes: 24 additions & 8 deletions frontend/packages/dev-console/src/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
ModelDefinition,
ModelFeatureFlag,
KebabActions,
NavSection,
HrefNavItem,
ResourceNSNavItem,
ResourceClusterNavItem,
Expand Down Expand Up @@ -67,6 +68,7 @@ import { CatalogConsumedExtensions, catalogPlugin } from './components/catalog/c
type ConsumedExtensions =
| ModelDefinition
| ModelFeatureFlag
| NavSection
| HrefNavItem
| ResourceClusterNavItem
| ResourceNSNavItem
Expand Down Expand Up @@ -107,12 +109,26 @@ const plugin: Plugin<ConsumedExtensions> = [
flag: FLAG_OPENSHIFT_GITOPS,
},
},
{
type: 'Nav/Section',
properties: {
id: 'top',
perspective: 'dev',
},
},
{
type: 'Nav/Section',
properties: {
id: 'resources',
perspective: 'dev',
},
},
{
type: 'NavItem/Href',
properties: {
id: 'add',
perspective: 'dev',
group: 'top',
section: 'top',
componentProps: {
name: '+Add',
href: '/add',
Expand All @@ -125,7 +141,7 @@ const plugin: Plugin<ConsumedExtensions> = [
properties: {
id: 'topology',
perspective: 'dev',
group: 'top',
section: 'top',
componentProps: {
name: 'Topology',
href: '/topology',
Expand All @@ -141,7 +157,7 @@ const plugin: Plugin<ConsumedExtensions> = [
properties: {
id: 'monitoring',
perspective: 'dev',
group: 'top',
section: 'top',
componentProps: {
name: 'Monitoring',
href: '/dev-monitoring',
Expand All @@ -158,7 +174,7 @@ const plugin: Plugin<ConsumedExtensions> = [
properties: {
id: 'search',
perspective: 'dev',
group: 'top',
section: 'top',
componentProps: {
name: 'Search',
href: '/search',
Expand All @@ -172,7 +188,7 @@ const plugin: Plugin<ConsumedExtensions> = [
properties: {
id: 'builds',
perspective: 'dev',
group: 'resources',
section: 'resources',
componentProps: {
name: 'Builds',
resource: 'buildconfigs',
Expand All @@ -188,7 +204,7 @@ const plugin: Plugin<ConsumedExtensions> = [
properties: {
id: 'applicationstages',
perspective: 'dev',
group: 'resources',
section: 'resources',
componentProps: {
name: 'Application Stages',
href: '/applicationstages',
Expand All @@ -204,7 +220,7 @@ const plugin: Plugin<ConsumedExtensions> = [
properties: {
id: 'helm',
perspective: 'dev',
group: 'resources',
section: 'resources',
componentProps: {
name: 'Helm',
href: '/helm-releases',
Expand All @@ -220,7 +236,7 @@ const plugin: Plugin<ConsumedExtensions> = [
properties: {
id: 'project',
perspective: 'dev',
group: 'resources',
section: 'resources',
componentProps: {
name: 'Project',
href: '/project-details',
Expand Down