diff --git a/.changelog/11820.txt b/.changelog/11820.txt new file mode 100644 index 00000000000..bb9dad9e57a --- /dev/null +++ b/.changelog/11820.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: add links to legend items in allocation-summary +``` diff --git a/ui/app/components/allocation-status-bar.js b/ui/app/components/allocation-status-bar.js index 803884b6dc0..dc65be51690 100644 --- a/ui/app/components/allocation-status-bar.js +++ b/ui/app/components/allocation-status-bar.js @@ -5,11 +5,24 @@ export default class AllocationStatusBar extends DistributionBar { layoutName = 'components/distribution-bar'; allocationContainer = null; + job = null; 'data-test-allocation-status-bar' = true; + generateLegendLink(job, status) { + if (!job || status === 'queued') return null; + + return { + queryParams: { + status: JSON.stringify([status]), + namespace: job.belongsTo('namespace').id(), + }, + }; + } + @computed( - 'allocationContainer.{queuedAllocs,completeAllocs,failedAllocs,runningAllocs,startingAllocs}' + 'allocationContainer.{queuedAllocs,completeAllocs,failedAllocs,runningAllocs,startingAllocs}', + 'job.namespace' ) get data() { if (!this.allocationContainer) { @@ -25,21 +38,43 @@ export default class AllocationStatusBar extends DistributionBar { 'lostAllocs' ); return [ - { label: 'Queued', value: allocs.queuedAllocs, className: 'queued' }, + { + label: 'Queued', + value: allocs.queuedAllocs, + className: 'queued', + legendLink: this.generateLegendLink(this.job, 'queued'), + }, { label: 'Starting', value: allocs.startingAllocs, className: 'starting', layers: 2, + legendLink: this.generateLegendLink(this.job, 'starting'), + }, + { + label: 'Running', + value: allocs.runningAllocs, + className: 'running', + legendLink: this.generateLegendLink(this.job, 'running'), }, - { label: 'Running', value: allocs.runningAllocs, className: 'running' }, { label: 'Complete', value: allocs.completeAllocs, className: 'complete', + legendLink: this.generateLegendLink(this.job, 'complete'), + }, + { + label: 'Failed', + value: allocs.failedAllocs, + className: 'failed', + legendLink: this.generateLegendLink(this.job, 'failed'), + }, + { + label: 'Lost', + value: allocs.lostAllocs, + className: 'lost', + legendLink: this.generateLegendLink(this.job, 'lost'), }, - { label: 'Failed', value: allocs.failedAllocs, className: 'failed' }, - { label: 'Lost', value: allocs.lostAllocs, className: 'lost' }, ]; } } diff --git a/ui/app/styles/charts/distribution-bar.scss b/ui/app/styles/charts/distribution-bar.scss index 507f8bdb93f..42439b28c15 100644 --- a/ui/app/styles/charts/distribution-bar.scss +++ b/ui/app/styles/charts/distribution-bar.scss @@ -59,7 +59,6 @@ display: block; background-color: transparent; transition: background-color 0.1s ease-in-out; - border: 1px solid $grey-blue; padding: 0.25em 0.75em; margin: 0.25em; border-radius: $radius; @@ -98,8 +97,6 @@ &.is-clickable { a { display: block; - text-decoration: none; - color: inherit; } &:not(.is-empty) { @@ -111,7 +108,6 @@ &.is-empty { color: darken($grey-blue, 20%); - border: none; .label { color: darken($grey-blue, 20%); diff --git a/ui/app/templates/components/job-page/parts/job-client-status-summary.hbs b/ui/app/templates/components/job-page/parts/job-client-status-summary.hbs index 09eee819a2e..64fbff2f517 100644 --- a/ui/app/templates/components/job-page/parts/job-client-status-summary.hbs +++ b/ui/app/templates/components/job-page/parts/job-client-status-summary.hbs @@ -18,7 +18,10 @@ {{this.jobClientStatus.totalNodes}} {{/if}} - + {{x-icon "info-circle-outline" class="is-faded"}} @@ -46,9 +49,20 @@ >
    {{#each chart.data as |datum index|}} -
  1. +
  2. {{#if (gt datum.value 0)}} - + {{else}} @@ -60,4 +74,4 @@ {{/if}} - + \ No newline at end of file diff --git a/ui/app/templates/components/job-page/parts/summary-legend-item.hbs b/ui/app/templates/components/job-page/parts/summary-legend-item.hbs index ab7997fbe6c..d5bd3492235 100644 --- a/ui/app/templates/components/job-page/parts/summary-legend-item.hbs +++ b/ui/app/templates/components/job-page/parts/summary-legend-item.hbs @@ -1,12 +1,18 @@
    - + - {{@datum.value}} - {{@datum.label}} + + {{@datum.value}} + + + {{@datum.label}} + {{#if @datum.help}} {{x-icon "info-circle-outline" class="is-faded"}} {{/if}} -
    + \ No newline at end of file diff --git a/ui/app/templates/components/job-page/parts/summary.hbs b/ui/app/templates/components/job-page/parts/summary.hbs index 9b4ed85090f..ff0a14e5745 100644 --- a/ui/app/templates/components/job-page/parts/summary.hbs +++ b/ui/app/templates/components/job-page/parts/summary.hbs @@ -40,26 +40,58 @@ - {{#component - (if a.item.hasChildren "children-status-bar" "allocation-status-bar") - allocationContainer=a.item.summary - job=a.item.summary - onSliceClick=this.onSliceClick - class="split-view" as |chart| - }} -
      - {{#each chart.data as |datum index|}} -
    1. +
        + {{#each chart.data as |datum index|}} +
      1. - -
      2. - {{/each}} -
      - {{/component}} + {{if (eq datum.value 0) "is-empty"}}" + > + +
    2. + {{/each}} +
    + + {{else}} + +
      + {{#each chart.data as |datum index|}} +
    1. + {{#if (and (gt datum.value 0) datum.legendLink)}} + + + + {{else}} + + {{/if}} +
    2. + {{/each}} +
    +
    + {{/if}}
    \ No newline at end of file diff --git a/ui/tests/helpers/module-for-job.js b/ui/tests/helpers/module-for-job.js index 50ce97eee7a..08522ea5c4f 100644 --- a/ui/tests/helpers/module-for-job.js +++ b/ui/tests/helpers/module-for-job.js @@ -93,8 +93,14 @@ export default function moduleForJob(title, context, jobFactory, additionalTests if (context === 'allocations') { test('allocations for the job are shown in the overview', async function(assert) { - assert.ok(JobDetail.allocationsSummary, 'Allocations are shown in the summary section'); - assert.notOk(JobDetail.childrenSummary, 'Children are not shown in the summary section'); + assert.ok( + JobDetail.allocationsSummary.isPresent, + 'Allocations are shown in the summary section' + ); + assert.ok( + JobDetail.childrenSummary.isHidden, + 'Children are not shown in the summary section' + ); }); test('clicking in an allocation row navigates to that allocation', async function(assert) { @@ -109,13 +115,47 @@ export default function moduleForJob(title, context, jobFactory, additionalTests 'Allocation row links to allocation detail' ); }); + + test('clicking legend item navigates to a pre-filtered allocations table', async function(assert) { + const legendItem = JobDetail.allocationsSummary.legend.clickableItems[1]; + const status = legendItem.label; + await legendItem.click(); + + const encodedStatus = encodeURIComponent(JSON.stringify([status])); + const expectedURL = new URL( + urlWithNamespace(`/jobs/${job.name}/clients?status=${encodedStatus}`, job.namespace), + window.location + ); + const gotURL = new URL(currentURL(), window.location); + assert.deepEqual(gotURL.path, expectedURL.path); + assert.deepEqual(gotURL.searchParams, expectedURL.searchParams); + }); + + test('clicking in a slice takes you to a pre-filtered allocations table', async function(assert) { + const slice = JobDetail.allocationsSummary.slices[1]; + const status = slice.label; + await slice.click(); + + const encodedStatus = encodeURIComponent(JSON.stringify([status])); + const expectedURL = new URL( + urlWithNamespace(`/jobs/${job.name}/allocations?status=${encodedStatus}`, job.namespace), + window.location + ); + const gotURL = new URL(currentURL(), window.location); + assert.deepEqual(gotURL.pathname, expectedURL.pathname); + + // Sort and compare URL query params. + gotURL.searchParams.sort(); + expectedURL.searchParams.sort(); + assert.equal(gotURL.searchParams.toString(), expectedURL.searchParams.toString()); + }); } if (context === 'children') { test('children for the job are shown in the overview', async function(assert) { - assert.ok(JobDetail.childrenSummary, 'Children are shown in the summary section'); - assert.notOk( - JobDetail.allocationsSummary, + assert.ok(JobDetail.childrenSummary.isPresent, 'Children are shown in the summary section'); + assert.ok( + JobDetail.allocationsSummary.isHidden, 'Allocations are not shown in the summary section' ); }); diff --git a/ui/tests/pages/components/job-client-status-bar.js b/ui/tests/pages/components/job-client-status-bar.js index 3cd753159be..e29d841852e 100644 --- a/ui/tests/pages/components/job-client-status-bar.js +++ b/ui/tests/pages/components/job-client-status-bar.js @@ -12,11 +12,11 @@ export default scope => ({ scope: '.legend', items: collection('li', { - label: attribute('data-test-legent-label'), + label: attribute('data-test-legend-label'), }), clickableItems: collection('li.is-clickable', { - label: attribute('data-test-legent-label'), + label: attribute('data-test-legend-label'), click: clickable('a'), }), }, diff --git a/ui/tests/pages/jobs/detail.js b/ui/tests/pages/jobs/detail.js index d077e8c8630..e82fc6f836b 100644 --- a/ui/tests/pages/jobs/detail.js +++ b/ui/tests/pages/jobs/detail.js @@ -82,10 +82,10 @@ export default create({ tooltip: attribute('aria-label'), }, }, - - childrenSummary: isPresent('[data-test-job-summary] [data-test-children-status-bar]'), - allocationsSummary: isPresent('[data-test-job-summary] [data-test-allocation-status-bar]'), - + childrenSummary: jobClientStatusBar('[data-test-job-summary] [data-test-children-status-bar]'), + allocationsSummary: jobClientStatusBar( + '[data-test-job-summary] [data-test-allocation-status-bar]' + ), ...allocations(), viewAllAllocations: text('[data-test-view-all-allocations]'),