From 40ebbf8b09d6af19478adf15bd1d5fc85bc08d3a Mon Sep 17 00:00:00 2001 From: Cameron Perera Date: Tue, 25 Jun 2024 08:46:25 -0500 Subject: [PATCH] Icu 13889 update tags column to action that opens hds flyout and add link to tags tab if more than 10 tags (#2355) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- addons/api/mirage/factories/worker.js | 58 ++++++--- addons/core/translations/resources/en-us.yaml | 7 + .../controllers/scopes/scope/workers/index.js | 32 +++++ .../app/routes/scopes/scope/workers/index.js | 10 ++ ui/admin/app/styles/app.scss | 12 ++ .../templates/scopes/scope/workers/index.hbs | 56 +++++++- .../tests/acceptance/workers/list-test.js | 121 +++++++++++++++++- 7 files changed, 277 insertions(+), 19 deletions(-) diff --git a/addons/api/mirage/factories/worker.js b/addons/api/mirage/factories/worker.js index 730948e33..3fcdce27b 100644 --- a/addons/api/mirage/factories/worker.js +++ b/addons/api/mirage/factories/worker.js @@ -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; + }, }); diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index c23c7df39..f972197c0 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -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 diff --git a/ui/admin/app/controllers/scopes/scope/workers/index.js b/ui/admin/app/controllers/scopes/scope/workers/index.js index 2570308f7..0619700d8 100644 --- a/ui/admin/app/controllers/scopes/scope/workers/index.js +++ b/ui/admin/app/controllers/scopes/scope/workers/index.js @@ -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 diff --git a/ui/admin/app/routes/scopes/scope/workers/index.js b/ui/admin/app/routes/scopes/scope/workers/index.js index 401f573de..9b0f77965 100644 --- a/ui/admin/app/routes/scopes/scope/workers/index.js +++ b/ui/admin/app/routes/scopes/scope/workers/index.js @@ -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. */ diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss index 7aedc1d8f..d5fdb924d 100644 --- a/ui/admin/app/styles/app.scss +++ b/ui/admin/app/styles/app.scss @@ -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; +} diff --git a/ui/admin/app/templates/scopes/scope/workers/index.hbs b/ui/admin/app/templates/scopes/scope/workers/index.hbs index 0e12ec593..c33f3ec3c 100644 --- a/ui/admin/app/templates/scopes/scope/workers/index.hbs +++ b/ui/admin/app/templates/scopes/scope/workers/index.hbs @@ -124,12 +124,16 @@ {{#if B.data.tagCount}} - {{/if}} @@ -145,6 +149,56 @@ + + {{#if this.selectedWorker}} + + + {{t + 'resources.worker.flyout.title' + workerName=this.selectedWorker.displayName + }} + + + + <:body as |B|> + + + + {{this.tagDisplayName B.data}} + + + + + + + + + {{#if (gt this.selectedWorker.allTags.length 10)}} +
+ {{! TODO: Update route to worker.tags once route is added }} + +
+ {{/if}} +
+
+ {{/if}} {{else}} + `[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(); + }); });