mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 03:27:54 +00:00
Ui/transform info table row roles (#9942)
* add dynamic segement to list-root so that you can sepecify the tab you want to go to * create new info-table-item-array component to handle array items passed into a info-table * do not underline links if they are in an info-table-row confirmed with design * implement the InfoTableItemArray component * amend wildcard helper to take in regular string * setup the logic and more logic * fix routing to roles issue * test for new component * change data-test-mode to count * handle case when wildcardCount is 0
This commit is contained in:
@@ -95,7 +95,7 @@ export default TransformBase.extend({
|
||||
const newModelTransformations = this.get('model.transformations');
|
||||
|
||||
if (!this.initialTransformations) {
|
||||
this.handleUpdatedTransformations(
|
||||
this.handleUpdateTransformations(
|
||||
newModelTransformations.map(t => ({
|
||||
id: t,
|
||||
action: 'ADD',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { helper as buildHelper } from '@ember/component/helper';
|
||||
|
||||
export function isWildcardString([string]) {
|
||||
export function isWildcardString(string) {
|
||||
if (!string) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,10 @@
|
||||
.icon-false {
|
||||
color: $ui-gray-300;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.info-table-row:not(.is-mobile) .column {
|
||||
|
||||
@@ -92,6 +92,14 @@
|
||||
{{#each model.attrs as |attr|}}
|
||||
{{#if (eq attr.type "object")}}
|
||||
{{info-table-row label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) value=(stringify (get model attr.name))}}
|
||||
{{else if (eq attr.type "array")}}
|
||||
{{info-table-row
|
||||
label=(capitalize (or attr.options.label (humanize (dasherize attr.name))))
|
||||
value=(get model attr.name)
|
||||
type=attr.type
|
||||
isLink=(eq attr.name 'transformations')
|
||||
viewAll="transformations"
|
||||
}}
|
||||
{{else}}
|
||||
{{info-table-row label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) value=(get model attr.name)}}
|
||||
{{/if}}
|
||||
|
||||
@@ -2,8 +2,24 @@
|
||||
{{#each model.transformFieldAttrs as |attr|}}
|
||||
{{#if (eq attr.type "object")}}
|
||||
{{info-table-row label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) value=(stringify (get model attr.name))}}
|
||||
{{else if (eq attr.type "array")}}
|
||||
{{info-table-row
|
||||
label=(capitalize (or attr.options.label (humanize (dasherize attr.name))))
|
||||
value=(get model attr.name)
|
||||
type=attr.type
|
||||
isLink=(eq attr.name 'allowed_roles')
|
||||
queryParam="role"
|
||||
modelType="transform/role"
|
||||
wildcardLabel=attr.options.wildcardLabel
|
||||
viewAll="roles"
|
||||
backend=model.backend
|
||||
}}
|
||||
{{else}}
|
||||
{{info-table-row label=(capitalize (or attr.options.label (humanize (dasherize attr.name)))) value=(get model attr.name) type=attr.type}}
|
||||
{{info-table-row
|
||||
label=(capitalize (or attr.options.label (humanize (dasherize attr.name))))
|
||||
value=(get model attr.name)
|
||||
type=attr.type
|
||||
}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</div>
|
||||
|
||||
70
ui/lib/core/addon/components/info-table-item-array.js
Normal file
70
ui/lib/core/addon/components/info-table-item-array.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import { computed } from '@ember/object';
|
||||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { task } from 'ember-concurrency';
|
||||
import layout from '../templates/components/info-table-item-array';
|
||||
import { isWildcardString } from 'vault/helpers/is-wildcard-string';
|
||||
|
||||
/**
|
||||
* @module InfoTableItemArray
|
||||
* The `InfoTableItemArray` component handles arrays in the info-table-row component.
|
||||
* If an array has more than 10 items, then only 5 are displayed and a count of total items is displayed next to the five.
|
||||
* If a isLink is true than a link can be set for the use to click on the specific array item
|
||||
* If a wildcard is a potential variable in the string of an item, then you can use the modelType and wildcardLabel parameters to
|
||||
* return a wildcard count similar to what is done in the searchSelect component.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <InfoTableItemArray @displayArray={{['test-1','test-2','test-3']}} @isLink={{true}} @modelType="transform/role"/ @queryParam="role" @backend="transform" viewAll="roles">
|
||||
* ```
|
||||
*
|
||||
* @param displayArray=null {array} - This array of data to be displayed. If there are more than 10 items in the array only five will show and a count of the other number in the array will show.
|
||||
* @param [isLink=true] {Boolean} - Indicates if the item should contain a link-to component. Only setup for arrays, but this could be changed if needed.
|
||||
* @param [modelType=null] {string} - Tells what model you want data for the allOptions to be returned from. Used in conjunction with the the isLink.
|
||||
* @param [wildcardLabel] {String} - when you want the component to return a count on the model for options returned when using a wildcard you must provide a label of the count e.g. role. Should be singular.
|
||||
* @param [queryParam] {String} - If you want to specific a tab for the View All XX to display to. Ex: role
|
||||
* @param [backend] {String} - To specify which backend to point the link to.
|
||||
* @param [viewAll] {String} - Specify the word at the end of the link View all xx.
|
||||
*/
|
||||
export default Component.extend({
|
||||
layout,
|
||||
'data-test-info-table-item-array': true,
|
||||
allOptions: null,
|
||||
displayArray: null,
|
||||
wildcardInDisplayArray: false,
|
||||
store: service(),
|
||||
displayArrayAmended: computed('displayArray', function() {
|
||||
let { displayArray } = this;
|
||||
if (displayArray.length >= 10) {
|
||||
// if array greater than 10 in length only display the first 5
|
||||
displayArray = displayArray.slice(0, 5);
|
||||
}
|
||||
|
||||
return displayArray;
|
||||
}),
|
||||
|
||||
checkWildcardInArray: task(function*() {
|
||||
let filteredArray = yield this.displayArray.filter(item => isWildcardString(item));
|
||||
this.set('wildcardInDisplayArray', filteredArray.length > 0 ? true : false);
|
||||
}).on('didInsertElement'),
|
||||
|
||||
fetchOptions: task(function*() {
|
||||
if (this.isLink && this.modelType) {
|
||||
let queryOptions = {};
|
||||
|
||||
if (this.backend) {
|
||||
queryOptions = { backend: this.backend };
|
||||
}
|
||||
|
||||
let options = yield this.store.query(this.modelType, queryOptions);
|
||||
this.formatOptions(options);
|
||||
}
|
||||
}).on('didInsertElement'),
|
||||
|
||||
formatOptions: function(options) {
|
||||
let allOptions = options.toArray().map(option => {
|
||||
return option.id;
|
||||
});
|
||||
this.set('allOptions', allOptions);
|
||||
},
|
||||
});
|
||||
@@ -19,7 +19,13 @@ import layout from '../templates/components/info-table-row';
|
||||
* @param helperText=null {string} - Text to describe the value displayed beneath the label.
|
||||
* @param alwaysRender=false {Boolean} - Indicates if the component content should be always be rendered. When false, the value of `value` will be used to determine if the component should render.
|
||||
* @param [type=array] {string} - The type of value being passed in. This is used for when you want to trim an array. For example, if you have an array value that can equal length 15+ this will trim to show 5 and count how many more are there
|
||||
* @param [isLink=true] {Boolean} - Passed through to InfoTableItemArray. Indicates if the item should contain a link-to component. Only setup for arrays, but this could be changed if needed.
|
||||
* @param [modelType=null] {string} - Passed through to InfoTableItemArray. Tells what model you want data for the allOptions to be returned from. Used in conjunction with the the isLink.
|
||||
* @param [queryParam] {String} - Passed through to InfoTableItemArray. If you want to specific a tab for the View All XX to display to. Ex: role
|
||||
* @param [backend] {String} - Passed through to InfoTableItemArray. To specify secrets backend to point link to Ex: transformation
|
||||
* @param [viewAll] {String} - Passed through to InfoTableItemArray. Specify the word at the end of the link View all xx.
|
||||
*/
|
||||
|
||||
export default Component.extend({
|
||||
layout,
|
||||
'data-test-component': 'info-table-row',
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
{{#if isLink}}
|
||||
{{#each displayArrayAmended as |name|}}
|
||||
{{#if (is-wildcard-string name)}}
|
||||
{{#let (filter-wildcard name allOptions) as | wildcardCount |}}
|
||||
<span>{{name}}</span>
|
||||
<span class="tag is-light has-text-grey-dark" data-test-count={{wildcardCount}}>
|
||||
includes {{if wildcardCount wildcardCount 0}} {{if (eq wildcardCount 1) wildcardLabel (pluralize wildcardLabel)}}
|
||||
</span>
|
||||
{{#if (eq displayArrayAmended.lastObject name)}}
|
||||
{{#link-to "vault.cluster.secrets.backend.list-root" (query-params tab=queryParam)}}
|
||||
<span data-test-view-all={{viewAll}}>View all {{viewAll}}</span>
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{else}}
|
||||
{{#link-to "vault.cluster.secrets.backend.show" (if queryParam (concat queryParam "/" name) name)}}
|
||||
<span>{{name}}</span>
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
{{#if (or (and (not-eq name displayArrayAmended.lastObject) wildcardInDisplayArray) (not-eq name displayArrayAmended.lastObject))}}
|
||||
,
|
||||
{{/if}}
|
||||
{{#if (and (eq name displayArrayAmended.lastObject) (gte displayArray.length 10)) }}
|
||||
<span data-test-and={{dec 5 displayArray.length}}> and {{dec 5 displayArray.length}} others. </span>
|
||||
{{/if}}
|
||||
{{#if (and (eq name displayArrayAmended.lastObject) (gte displayArray.length 10)) }}
|
||||
{{#link-to "vault.cluster.secrets.backend.list-root" (query-params tab=queryParam)}}
|
||||
<span data-test-view-all={{viewAll}}>View all {{viewAll}} </span>
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
{{else}}
|
||||
<code class="is-word-break has-text-black" data-test-row-value="{{label}}">{{if (gte displayArray.length 10) (concat displayArray ", and " (dec 5 displayArray.length) " more.") displayArray}}</code>
|
||||
{{/if}}
|
||||
|
||||
@@ -30,7 +30,16 @@
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if (eq type 'array')}}
|
||||
<code class="is-word-break has-text-black" data-test-row-value="{{label}}">{{if (gte value.length 10) (concat (take 5 value) ", and " (dec 5 value.length) " more.") value}}</code>
|
||||
<InfoTableItemArray
|
||||
@backend={{backend}}
|
||||
@displayArray={{value}}
|
||||
@isLink={{isLink}}
|
||||
@label={{label}}
|
||||
@modelType={{modelType}}
|
||||
@queryParam={{queryParam}}
|
||||
@viewAll={{viewAll}}
|
||||
@wildcardLabel={{wildcardLabel}}
|
||||
/>
|
||||
{{else}}
|
||||
<code class="is-word-break has-text-black" data-test-row-value="{{label}}">{{value}}</code>
|
||||
{{/if}}
|
||||
|
||||
@@ -53,10 +53,10 @@
|
||||
{{else}}
|
||||
<div>{{selected.id}}
|
||||
{{#if wildcardLabel}}
|
||||
{{#if (is-wildcard-string selected)}}
|
||||
{{#if (is-wildcard-string selected.id)}}
|
||||
{{#let (filter-wildcard selected allOptions) as | wildcardCount |}}
|
||||
<span class="tag is-light has-text-grey-dark" data-test-mode={{wildcardCount}}>
|
||||
includes {{wildcardCount}} {{if (eq wildcardCount 1) wildcardLabel (pluralize wildcardLabel)}}
|
||||
<span class="tag is-light has-text-grey-dark" data-test-count={{wildcardCount}}>
|
||||
includes {{if wildcardCount wildcardCount 0}} {{if (eq wildcardCount 1) wildcardLabel (pluralize wildcardLabel)}}
|
||||
</span>
|
||||
{{/let}}
|
||||
{{/if}}
|
||||
|
||||
1
ui/lib/core/app/components/info-table-item-array.js
Normal file
1
ui/lib/core/app/components/info-table-item-array.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from 'core/components/info-table-item-array';
|
||||
127
ui/tests/integration/components/info-table-item-array-test.js
Normal file
127
ui/tests/integration/components/info-table-item-array-test.js
Normal file
@@ -0,0 +1,127 @@
|
||||
import { module, test } from 'qunit';
|
||||
import Service from '@ember/service';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { run } from '@ember/runloop';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
const DISPLAY_ARRAY = ['role-1', 'role-2', 'role-3', 'role-4', 'role-5'];
|
||||
|
||||
const storeService = Service.extend({
|
||||
query() {
|
||||
return new Promise(resolve => {
|
||||
resolve([
|
||||
{ id: 'role-1' },
|
||||
{ id: 'role-2' },
|
||||
{ id: 'role-3' },
|
||||
{ id: 'role-4' },
|
||||
{ id: 'role-5' },
|
||||
{ id: 'role-6' },
|
||||
]);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
module('Integration | Component | InfoTableItemArray', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
this.set('displayArray', DISPLAY_ARRAY);
|
||||
this.set('isLink', true);
|
||||
this.set('modelType', 'transform/role');
|
||||
this.set('queryParam', 'role');
|
||||
this.set('backend', 'transform');
|
||||
this.set('wildcardLabel', 'role');
|
||||
this.set('viewAll', 'roles');
|
||||
run(() => {
|
||||
this.owner.unregister('service:store');
|
||||
this.owner.register('service:store', storeService);
|
||||
});
|
||||
});
|
||||
|
||||
hooks.afterEach(function() {
|
||||
this.owner.unregister('service:store');
|
||||
});
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
await render(hbs`<InfoTableItemArray
|
||||
@displayArray={{displayArray}}
|
||||
/>`);
|
||||
|
||||
assert.dom('[data-test-info-table-item-array]').exists();
|
||||
let noLinkString = document.querySelector('code').textContent;
|
||||
|
||||
assert.equal(
|
||||
noLinkString.length,
|
||||
DISPLAY_ARRAY.toString().length,
|
||||
'renders a string of the array if isLink is not provided'
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders links if isLink is true', async function(assert) {
|
||||
await render(hbs`<InfoTableItemArray
|
||||
@displayArray={{displayArray}}
|
||||
@isLink={{isLink}}
|
||||
@modelType={{modelType}}
|
||||
@queryParam={{queryParam}}
|
||||
@backend={{backend}}
|
||||
/>`);
|
||||
assert.equal(
|
||||
document.querySelectorAll('a > span').length,
|
||||
DISPLAY_ARRAY.length,
|
||||
'renders each item in array with link'
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders a badge and view all if wildcard in display array && < 10', async function(assert) {
|
||||
const displayArrayWithWildcard = ['role-1', 'role-2', 'role-3', 'r*'];
|
||||
this.set('displayArrayWithWildcard', displayArrayWithWildcard);
|
||||
await render(hbs`<InfoTableItemArray
|
||||
@displayArray={{displayArrayWithWildcard}}
|
||||
@isLink={{isLink}}
|
||||
@modelType={{modelType}}
|
||||
@queryParam={{queryParam}}
|
||||
@backend={{backend}}
|
||||
@viewAll={{viewAll}}
|
||||
/>`);
|
||||
|
||||
assert.equal(
|
||||
document.querySelectorAll('a > span').length,
|
||||
DISPLAY_ARRAY.length - 1,
|
||||
'renders each item in array with link'
|
||||
);
|
||||
// 6 here comes from the six roles setup in the store service.
|
||||
assert.dom('[data-test-count="6"]').exists('correctly counts with wildcard filter and shows the count');
|
||||
assert.dom('[data-test-view-all="roles"]').exists({ count: 1 }, 'renders 1 view all roles');
|
||||
});
|
||||
|
||||
test('it renders a badge and view all if wildcard in display array && >= 10', async function(assert) {
|
||||
const displayArrayWithWildcard = [
|
||||
'role-1',
|
||||
'role-2',
|
||||
'role-3',
|
||||
'r*',
|
||||
'role-4',
|
||||
'role-5',
|
||||
'role-6',
|
||||
'role-7',
|
||||
'role-8',
|
||||
'role-9',
|
||||
'role-10',
|
||||
];
|
||||
this.set('displayArrayWithWildcard', displayArrayWithWildcard);
|
||||
await render(hbs`<InfoTableItemArray
|
||||
@displayArray={{displayArrayWithWildcard}}
|
||||
@isLink={{isLink}}
|
||||
@modelType={{modelType}}
|
||||
@queryParam={{queryParam}}
|
||||
@backend={{backend}}
|
||||
@viewAll={{viewAll}}
|
||||
/>`);
|
||||
const numberCutOffTruncatedArray = displayArrayWithWildcard.length - 5;
|
||||
assert.equal(document.querySelectorAll('a > span').length, 5, 'renders truncated array of five');
|
||||
assert
|
||||
.dom(`[data-test-and="${numberCutOffTruncatedArray}"]`)
|
||||
.exists('correctly counts with wildcard filter and shows the count');
|
||||
});
|
||||
});
|
||||
@@ -104,7 +104,7 @@ module('Integration | Component | search select', function(hooks) {
|
||||
await clickTrigger();
|
||||
await typeInSearch('*bar*');
|
||||
await component.selectOption();
|
||||
assert.dom('[data-test-mode="2"]').exists('correctly counts with wildcard filter and shows the count');
|
||||
assert.dom('[data-test-count="2"]').exists('correctly counts with wildcard filter and shows the count');
|
||||
});
|
||||
|
||||
test('it behaves correctly if new items not allowed', async function(assert) {
|
||||
|
||||
@@ -4,37 +4,37 @@ import { module, test } from 'qunit';
|
||||
module('Unit | Helpers | is-wildcard-string', function() {
|
||||
test('it returns true if regular string with wildcard', function(assert) {
|
||||
let string = 'foom#*eep';
|
||||
let result = isWildcardString([string]);
|
||||
let result = isWildcardString(string);
|
||||
assert.equal(result, true);
|
||||
});
|
||||
|
||||
test('it returns false if no wildcard', function(assert) {
|
||||
let string = 'foo.bar';
|
||||
let result = isWildcardString([string]);
|
||||
let result = isWildcardString(string);
|
||||
assert.equal(result, false);
|
||||
});
|
||||
|
||||
test('it returns true if string with id as in searchSelect selected has wildcard', function(assert) {
|
||||
let string = { id: 'foo.bar*baz' };
|
||||
let result = isWildcardString([string]);
|
||||
let result = isWildcardString(string);
|
||||
assert.equal(result, true);
|
||||
});
|
||||
|
||||
test('it returns true if string object has name and no id', function(assert) {
|
||||
let string = { name: 'foo.bar*baz' };
|
||||
let result = isWildcardString([string]);
|
||||
let result = isWildcardString(string);
|
||||
assert.equal(result, true);
|
||||
});
|
||||
|
||||
test('it returns true if string object has name and id with at least one wildcard', function(assert) {
|
||||
let string = { id: '7*', name: 'seven' };
|
||||
let result = isWildcardString([string]);
|
||||
let result = isWildcardString(string);
|
||||
assert.equal(result, true);
|
||||
});
|
||||
|
||||
test('it returns true if string object has name and id with wildcard in name not id', function(assert) {
|
||||
let string = { id: '7', name: 'sev*n' };
|
||||
let result = isWildcardString([string]);
|
||||
let result = isWildcardString(string);
|
||||
assert.equal(result, true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user