Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions shell/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ export interface ProductOptions {
*/
ifHaveType?: string | RegExp;

/**
* Hide the product if the type is present (opposite of ifHaveType)
*/
ifNotHaveType?: string | RegExp;

/**
* The vuex store that this product should use by default i.e. 'management'
*/
Expand Down
166 changes: 164 additions & 2 deletions shell/store/__tests__/type-map.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,169 @@ describe('type-map', () => {
});
});
});

describe('activeProducts', () => {
// Basic schemas for product filtering tests
const productSchemas = {
myType: {
id: 'mytype',
_id: 'mytype',
type: SCHEMA,
_group: 'mygroup',
},
anotherType: {
id: 'anothertype',
_id: 'anothertype',
type: SCHEMA,
_group: 'anothergroup',
},
};

const createProductState = (products) => ({
products,
schemaGeneration: 1,
});

const createProductRootGetters = (moduleSchemas = [], moduleName = 'cluster') => ({
'prefs/get': () => false,
[`${ moduleName }/all`]: (resource) => {
if (resource === SCHEMA) {
return moduleSchemas;
}

return [];
},
});

describe('ifHaveType', () => {
it('should show product when matching type exists', () => {
const state = createProductState([{
name: 'test-product',
inStore: 'cluster',
ifHaveType: 'mytype',
}]);
const rootGetters = createProductRootGetters([productSchemas.myType]);

const active = getters.activeProducts(state, {}, {}, rootGetters);

expect(active).toHaveLength(1);
expect(active[0].name).toBe('test-product');
});

it('should hide product when matching type does not exist', () => {
const state = createProductState([{
name: 'test-product',
inStore: 'cluster',
ifHaveType: 'missingtype',
}]);
const rootGetters = createProductRootGetters([productSchemas.myType]);

const active = getters.activeProducts(state, {}, {}, rootGetters);

expect(active).toHaveLength(0);
});
});

describe('ifNotHaveType', () => {
it('should show product when matching type does NOT exist', () => {
const state = createProductState([{
name: 'test-product',
inStore: 'cluster',
ifNotHaveType: 'missingtype',
}]);
const rootGetters = createProductRootGetters([productSchemas.myType]);

const active = getters.activeProducts(state, {}, {}, rootGetters);

expect(active).toHaveLength(1);
expect(active[0].name).toBe('test-product');
});

it('should hide product when matching type exists', () => {
const state = createProductState([{
name: 'test-product',
inStore: 'cluster',
ifNotHaveType: 'mytype',
}]);
const rootGetters = createProductRootGetters([productSchemas.myType]);

const active = getters.activeProducts(state, {}, {}, rootGetters);

expect(active).toHaveLength(0);
});

it('should support regex pattern in ifNotHaveType', () => {
const state = createProductState([{
name: 'test-product',
inStore: 'cluster',
ifNotHaveType: 'my.*',
}]);
const rootGetters = createProductRootGetters([productSchemas.myType]);

const active = getters.activeProducts(state, {}, {}, rootGetters);

expect(active).toHaveLength(0);
});

it('should show product when regex pattern does not match any type', () => {
const state = createProductState([{
name: 'test-product',
inStore: 'cluster',
ifNotHaveType: 'nomatch.*',
}]);
const rootGetters = createProductRootGetters([productSchemas.myType]);

const active = getters.activeProducts(state, {}, {}, rootGetters);

expect(active).toHaveLength(1);
expect(active[0].name).toBe('test-product');
});
});

describe('combined ifHaveType and ifNotHaveType', () => {
it('should show product when ifHaveType matches and ifNotHaveType does not match', () => {
const state = createProductState([{
name: 'test-product',
inStore: 'cluster',
ifHaveType: 'mytype',
ifNotHaveType: 'missingtype',
}]);
const rootGetters = createProductRootGetters([productSchemas.myType]);

const active = getters.activeProducts(state, {}, {}, rootGetters);

expect(active).toHaveLength(1);
expect(active[0].name).toBe('test-product');
});

it('should hide product when ifHaveType matches but ifNotHaveType also matches', () => {
const state = createProductState([{
name: 'test-product',
inStore: 'cluster',
ifHaveType: 'mytype',
ifNotHaveType: 'anothertype',
}]);
const rootGetters = createProductRootGetters([productSchemas.myType, productSchemas.anotherType]);

const active = getters.activeProducts(state, {}, {}, rootGetters);

expect(active).toHaveLength(0);
});

it('should hide product when ifHaveType does not match', () => {
const state = createProductState([{
name: 'test-product',
inStore: 'cluster',
ifHaveType: 'missingtype',
ifNotHaveType: 'anothermissingtype',
}]);
const rootGetters = createProductRootGetters([productSchemas.myType]);

const active = getters.activeProducts(state, {}, {}, rootGetters);

expect(active).toHaveLength(0);
});
});
});
});
});

// getTree - Remove ignored schemas, not-applicable to ns filter
11 changes: 10 additions & 1 deletion shell/store/type-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
// ifHave, -- Show this product only if the given capability is available
// ifHaveGroup, -- Show this product only if the given group exists in the store [inStore]
// ifHaveType, -- Show this product only if the given type exists in the store [inStore], This can also be specified as an object { type: TYPE, store: 'management' } if the type isn't in the current [inStore]
// ifNotHaveType, -- Hide this product if the given type exists in the store [inStore] (opposite of ifHaveType)
// ifHaveVerb, -- In combination with ifHaveTYpe, show it only if the type also has this collectionMethod
// inStore, -- Which store to look at for if* above and the left-nav, defaults to "cluster"
// rootProduct, -- Optional root (parent) product - if set, used to optimize navigation when product changes stays within root product
Expand Down Expand Up @@ -238,7 +239,7 @@ export function DSL(store, product, module = 'type-map') {
};

// Convert strings to regex's - we do this once here for efficiency
for ( const k of ['ifHaveGroup', 'ifHaveType'] ) {
for ( const k of ['ifHaveGroup', 'ifHaveType', 'ifNotHaveType'] ) {
if ( opt[k] ) {
if (Array.isArray(opt[k])) {
opt[k] = opt[k].map((r) => regexToString(ensureRegex(r)));
Expand Down Expand Up @@ -1419,6 +1420,14 @@ export const getters = {
}
}

if ( p.ifNotHaveType ) {
const haveIds = knownTypes[module].filter((t) => t.match(stringToRegex(p.ifNotHaveType)) );

if ( haveIds.length ) {
return false;
}
}

if ( p.ifHaveGroup && !knownGroups[module].find((t) => t.match(stringToRegex(p.ifHaveGroup)) ) ) {
return false;
}
Expand Down
Loading