Icu 13889 update tags column to action that opens hds flyout and add link to tags tab if more than 10 tags (#2355)
* refactor: 💡 update worker factory to include api_tags ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13889 * feat: 🎸 add tags flyout for workers table ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13889 * refactor: 💡 move test selector to const ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13889 * refactor: 💡 only show 'view more tags' if more than 10 tags ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13889 * test: 💍 update test name ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13889 * refactor: 💡 update how worker tags are shown in flyout ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13889 * refactor: 💡 add one more api tag for worker in mirage ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13889 * refactor: 💡 fix tagDisplayName signature ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13889 * refactor: 💡 update tag shortener styling ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-13889
This commit is contained in:
1 parent
f51124cfdb
commit
40ebbf8b09
7 files changed
+277
-19
No files matched your search
@@ -20,30 +20,54 @@ export default factory.extend({
|
||||
'delete',
|
||||
],
|
||||
type: (i) => types[i % types.length],
|
||||
canonical_tags: (i) => {
|
||||
if (i % 3 === 0) {
|
||||
return { os: ['ubuntu'] };
|
||||
}
|
||||
if (i % 2 === 0) {
|
||||
return {
|
||||
type: [faker.word.words(1), faker.word.words(1)],
|
||||
os: ['ubuntu'],
|
||||
env: ['dev', 'local'],
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
config_tags: (i) => {
|
||||
if (i % 3 === 0) {
|
||||
return { os: ['ubuntu'] };
|
||||
}
|
||||
if (i % 2 === 0) {
|
||||
return {
|
||||
type: [faker.word.words(1), faker.word.words(1)],
|
||||
os: ['ubuntu'],
|
||||
env: ['dev', 'local'],
|
||||
};
|
||||
}
|
||||
if (i % 2 === 0) {
|
||||
return { os: ['ubuntu'] };
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
api_tags: (i) => {
|
||||
if (i % 3 === 0) {
|
||||
return {
|
||||
type: [faker.word.words(1), faker.word.words(1)],
|
||||
os: ['z-os'],
|
||||
env: ['prod', 'qa'],
|
||||
sample: [faker.word.words(1), faker.word.words(1)],
|
||||
path: [faker.word.words(1)],
|
||||
};
|
||||
}
|
||||
if (i % 2 === 0) {
|
||||
return { os: ['z-os'] };
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
canonical_tags() {
|
||||
const configTags = this.config_tags || {};
|
||||
const apiTags = this.api_tags || {};
|
||||
const canonicalTags = {};
|
||||
|
||||
Object.keys(configTags).forEach((key) => {
|
||||
canonicalTags[key] = new Set(configTags[key]);
|
||||
});
|
||||
|
||||
Object.keys(apiTags).forEach((key) => {
|
||||
if (canonicalTags[key]) {
|
||||
apiTags[key].forEach((tag) => canonicalTags[key].add(tag));
|
||||
} else {
|
||||
canonicalTags[key] = new Set(apiTags[key]);
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(canonicalTags).forEach((key) => {
|
||||
canonicalTags[key] = Array.from(canonicalTags[key]);
|
||||
});
|
||||
|
||||
return canonicalTags;
|
||||
},
|
||||
});
|
||||
@@ -983,6 +983,13 @@ worker:
|
||||
ip_address: IP Address
|
||||
release_version: Release Version
|
||||
tag_count: '{tagCount, plural, =1 {# tag} other {# tags}}'
|
||||
flyout:
|
||||
title: Tags in {workerName}
|
||||
table:
|
||||
tag: Tag
|
||||
type: Type
|
||||
action:
|
||||
view_more: View more tags
|
||||
form:
|
||||
cluster_id:
|
||||
label: Boundary Cluster ID
|
||||
|
||||
@@ -7,6 +7,7 @@ import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { loading } from 'ember-loading';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { confirm } from 'core/decorators/confirm';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
|
||||
@@ -16,8 +17,39 @@ export default class ScopesScopeWorkersIndexController extends Controller {
|
||||
@service router;
|
||||
@service can;
|
||||
|
||||
// =attributes
|
||||
|
||||
@tracked selectedWorker;
|
||||
|
||||
/**
|
||||
* Get the first 10 tags of the selected worker.
|
||||
* @type {object[]}
|
||||
*/
|
||||
get selectedWorkerTags() {
|
||||
// only show the first 10 tags if there are more than 10
|
||||
return this.selectedWorker.allTags.slice(0, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the display name for a worker tag
|
||||
* @param {object} tag
|
||||
* @returns {string}
|
||||
*/
|
||||
tagDisplayName(tag) {
|
||||
return `${tag.key} = ${tag.value}`;
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Toggle the tags flyout to display or hide the tags of a worker.
|
||||
* @param {object} selectedWorker
|
||||
*/
|
||||
@action
|
||||
selectWorker(selectedWorker) {
|
||||
this.selectedWorker = selectedWorker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback changes on workers.
|
||||
* @param {WorkerModel} worker
|
||||
|
||||
@@ -68,6 +68,16 @@ export default class ScopesScopeWorkersIndexRoute extends Route {
|
||||
return workers;
|
||||
}
|
||||
|
||||
resetController(controller, isExiting) {
|
||||
// Clear selected worker when exiting route to prevent
|
||||
// the flyout from showing when returning to this route
|
||||
if (isExiting) {
|
||||
controller.setProperties({
|
||||
selectedWorker: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears filter selections.
|
||||
*/
|
||||
|
||||
@@ -1069,3 +1069,15 @@
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
// workers
|
||||
.view-more-tags {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.tag-shortener {
|
||||
display: inline-block;
|
||||
max-width: 310px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
@@ -124,12 +124,16 @@
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if B.data.tagCount}}
|
||||
<Hds::Badge
|
||||
<Hds::Button
|
||||
data-test-worker-tags-flyout-button={{B.data.id}}
|
||||
@icon='tag'
|
||||
@text={{t
|
||||
'resources.worker.table.tag_count'
|
||||
tagCount=B.data.tagCount
|
||||
}}
|
||||
@color='tertiary'
|
||||
@size='small'
|
||||
{{on 'click' (fn this.selectWorker B.data)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
@@ -145,6 +149,56 @@
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
|
||||
{{#if this.selectedWorker}}
|
||||
<Hds::Flyout
|
||||
data-test-worker-tags-flyout
|
||||
@onClose={{fn this.selectWorker null}}
|
||||
as |M|
|
||||
>
|
||||
<M.Header>
|
||||
{{t
|
||||
'resources.worker.flyout.title'
|
||||
workerName=this.selectedWorker.displayName
|
||||
}}
|
||||
</M.Header>
|
||||
<M.Body>
|
||||
<Hds::Table
|
||||
@model={{this.selectedWorkerTags}}
|
||||
@columns={{array
|
||||
(hash label=(t 'resources.worker.flyout.table.tag'))
|
||||
(hash label=(t 'resources.worker.flyout.table.type'))
|
||||
}}
|
||||
@valign='middle'
|
||||
>
|
||||
<:body as |B|>
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Hds::Text::Code class='tag-shortener'>
|
||||
{{this.tagDisplayName B.data}}
|
||||
</Hds::Text::Code>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<Hds::Badge @text={{B.data.type}} />
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
{{#if (gt this.selectedWorker.allTags.length 10)}}
|
||||
<div class='view-more-tags'>
|
||||
{{! TODO: Update route to worker.tags once route is added }}
|
||||
<Hds::Link::Standalone
|
||||
@icon='arrow-right'
|
||||
@iconPosition='trailing'
|
||||
@text={{t 'resources.worker.flyout.action.view_more'}}
|
||||
@route='scopes.scope.workers.worker'
|
||||
@model={{this.selectedWorker}}
|
||||
/>
|
||||
</div>
|
||||
{{/if}}
|
||||
</M.Body>
|
||||
</Hds::Flyout>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<Rose::Layout::Centered>
|
||||
<Rose::Message
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { visit } from '@ember/test-helpers';
|
||||
import { visit, click, currentURL } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
@@ -15,6 +15,15 @@ import {
|
||||
//invalidateSession,
|
||||
} from 'ember-simple-auth/test-support';
|
||||
|
||||
const WORKERS_FLYOUT = '[data-test-worker-tags-flyout]';
|
||||
const WORKERS_FLYOUT_DISMISS = '[data-test-worker-tags-flyout] div button';
|
||||
const WORKERS_FLYOUT_TABLE_BODY = '[data-test-worker-tags-flyout] tbody';
|
||||
const WORKERS_FLYOUT_TABLE_ROWS = '[data-test-worker-tags-flyout] tbody tr';
|
||||
const WORKERS_FLYOUT_VIEW_MORE_TAGS =
|
||||
'[data-test-worker-tags-flyout] .view-more-tags a';
|
||||
const WORKER_TAGS_BUTTON = (workerId) =>
|
||||
`[data-test-worker-tags-flyout-button="${workerId}"]`;
|
||||
|
||||
module('Acceptance | workers | list', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
@@ -26,17 +35,22 @@ module('Acceptance | workers | list', function (hooks) {
|
||||
scopes: {
|
||||
global: null,
|
||||
},
|
||||
worker: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
globalScope: null,
|
||||
workers: null,
|
||||
worker: null,
|
||||
};
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
instances.scopes.global = this.server.create('scope', { id: 'global' });
|
||||
const scope = instances.scopes.global;
|
||||
instances.worker = this.server.create('worker', { scopeId: scope.id });
|
||||
urls.globalScope = `/scopes/global/scopes`;
|
||||
urls.workers = `/scopes/global/workers`;
|
||||
urls.worker = `${urls.workers}/${instances.worker.id}`;
|
||||
authenticateSession({});
|
||||
featuresService = this.owner.lookup('service:features');
|
||||
});
|
||||
@@ -77,4 +91,109 @@ module('Acceptance | workers | list', function (hooks) {
|
||||
await visit(urls.globalScope);
|
||||
assert.dom(`[href="${urls.workers}"]`).isVisible();
|
||||
});
|
||||
|
||||
test('Users can open and close tags flyout for a specific worker', async function (assert) {
|
||||
featuresService.enable('byow');
|
||||
await visit(urls.workers);
|
||||
assert.dom(WORKERS_FLYOUT).isNotVisible();
|
||||
|
||||
await click(WORKER_TAGS_BUTTON(instances.worker.id));
|
||||
assert.dom(WORKERS_FLYOUT).isVisible();
|
||||
|
||||
await click(WORKERS_FLYOUT_DISMISS);
|
||||
assert.dom(WORKERS_FLYOUT).isNotVisible();
|
||||
});
|
||||
|
||||
test('Users can see worker tags in the tags flyout', async function (assert) {
|
||||
featuresService.enable('byow');
|
||||
await visit(urls.workers);
|
||||
await click(WORKER_TAGS_BUTTON(instances.worker.id));
|
||||
|
||||
assert.dom(WORKERS_FLYOUT_TABLE_BODY).includesText('os = z-os');
|
||||
});
|
||||
|
||||
test('Users can only see first 10 tags in the tags flyout', async function (assert) {
|
||||
featuresService.enable('byow');
|
||||
instances.worker.update({
|
||||
configTags: {
|
||||
tag1: ['value1', 'value2'],
|
||||
tag2: ['value3'],
|
||||
tag3: ['value4'],
|
||||
tag4: ['value5'],
|
||||
tag5: ['value6'],
|
||||
tag6: ['value7'],
|
||||
tag7: ['value8'],
|
||||
tag8: ['value9'],
|
||||
tag9: ['value10'],
|
||||
tag10: ['value11'],
|
||||
tag11: ['value12'],
|
||||
},
|
||||
});
|
||||
|
||||
await visit(urls.workers);
|
||||
await click(WORKER_TAGS_BUTTON(instances.worker.id));
|
||||
|
||||
assert.dom(WORKERS_FLYOUT_TABLE_ROWS).exists({ count: 10 });
|
||||
});
|
||||
|
||||
test('Users can click on "view more tags" if there are more than 10 tags', async function (assert) {
|
||||
featuresService.enable('byow');
|
||||
instances.worker.update({
|
||||
configTags: {
|
||||
tag1: ['value1', 'value2'],
|
||||
tag2: ['value3'],
|
||||
tag3: ['value4'],
|
||||
tag4: ['value5'],
|
||||
tag5: ['value6'],
|
||||
tag6: ['value7'],
|
||||
tag7: ['value8'],
|
||||
tag8: ['value9'],
|
||||
tag9: ['value10'],
|
||||
tag10: ['value11'],
|
||||
tag11: ['value12'],
|
||||
},
|
||||
});
|
||||
|
||||
await visit(urls.workers);
|
||||
await click(WORKER_TAGS_BUTTON(instances.worker.id));
|
||||
|
||||
assert.dom(WORKERS_FLYOUT_TABLE_ROWS).exists({ count: 10 });
|
||||
assert.dom(WORKERS_FLYOUT_VIEW_MORE_TAGS).isVisible();
|
||||
});
|
||||
|
||||
test('Users do not see "view more tags" if there are 10 or less tags', async function (assert) {
|
||||
featuresService.enable('byow');
|
||||
instances.worker.update({
|
||||
configTags: {
|
||||
tag1: ['value1', 'value2'],
|
||||
tag2: ['value3'],
|
||||
tag3: ['value4'],
|
||||
tag4: ['value5'],
|
||||
},
|
||||
apiTags: null,
|
||||
});
|
||||
|
||||
await visit(urls.workers);
|
||||
await click(WORKER_TAGS_BUTTON(instances.worker.id));
|
||||
|
||||
assert.dom(WORKERS_FLYOUT_TABLE_ROWS).exists({ count: 5 });
|
||||
assert.dom(WORKERS_FLYOUT_VIEW_MORE_TAGS).isNotVisible();
|
||||
});
|
||||
|
||||
test('Users do not see worker tags flyout when returning to workers list', async function (assert) {
|
||||
featuresService.enable('byow');
|
||||
await visit(urls.workers);
|
||||
await click(WORKER_TAGS_BUTTON(instances.worker.id));
|
||||
|
||||
assert.dom(WORKERS_FLYOUT).isVisible();
|
||||
|
||||
await click(WORKERS_FLYOUT_VIEW_MORE_TAGS);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.worker);
|
||||
|
||||
await click(`[href="${urls.workers}"]`);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.workers);
|
||||
assert.dom(WORKERS_FLYOUT).isNotVisible();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user