From ea46b31d25badd73b35b3a421078071525d20aee Mon Sep 17 00:00:00 2001 From: Vault Automation Date: Tue, 2 Sep 2025 17:43:21 -0600 Subject: [PATCH] UI: Fix chart legend order (#8827) (#8856) * fix chart legend order and make sync clients last * reimplement using idx in chart legend Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Co-authored-by: claire bontempo --- ui/app/components/clients/counts-card.hbs | 6 +-- ui/app/components/clients/running-total.ts | 11 ++-- ui/app/styles/core/charts.scss | 54 ++++++++++--------- .../clients/counts/overview-test.js | 23 ++++++-- .../components/clients/running-total-test.js | 13 +++-- 5 files changed, 66 insertions(+), 41 deletions(-) diff --git a/ui/app/components/clients/counts-card.hbs b/ui/app/components/clients/counts-card.hbs index df3d7a7a46..776340bdc0 100644 --- a/ui/app/components/clients/counts-card.hbs +++ b/ui/app/components/clients/counts-card.hbs @@ -35,10 +35,10 @@ Data visualizations render in in a flex row with a 1/3-width left element and a {{#if @legend}}
- {{#each @legend as |l|}} + {{#each @legend as |l idx|}}
- - {{capitalize l.label}} + + {{l.label}}
{{/each}}
diff --git a/ui/app/components/clients/running-total.ts b/ui/app/components/clients/running-total.ts index c930b1d9bc..8a9b9f4c0d 100644 --- a/ui/app/components/clients/running-total.ts +++ b/ui/app/components/clients/running-total.ts @@ -36,12 +36,13 @@ export default class RunningTotal extends Component { get chartLegend() { if (this.showStacked) { return [ - { key: 'entity_clients', label: 'entity clients' }, - { key: 'non_entity_clients', label: 'non-entity clients' }, - ...(this.flags.secretsSyncIsActivated ? [{ key: 'secret_syncs', label: 'secret sync clients' }] : []), - { key: 'acme_clients', label: 'acme clients' }, + { key: 'entity_clients', label: 'Entity clients' }, + { key: 'non_entity_clients', label: 'Non-entity clients' }, + { key: 'acme_clients', label: 'ACME clients' }, + // MUST BE LAST because conditionally renders and legend color mapping for stacked bars will be off otherwise + ...(this.flags.secretsSyncIsActivated ? [{ key: 'secret_syncs', label: 'Secret sync clients' }] : []), ]; } - return [{ key: 'new_clients', label: 'new clients' }]; + return [{ key: 'new_clients', label: 'New clients' }]; } } diff --git a/ui/app/styles/core/charts.scss b/ui/app/styles/core/charts.scss index 88db198796..55f616fa64 100644 --- a/ui/app/styles/core/charts.scss +++ b/ui/app/styles/core/charts.scss @@ -7,11 +7,12 @@ */ // LEGEND STYLING (positioning is in chart-container.scss) -$blue-500: #1c345f; -$secret_syncs: #6cc5b0; -$acme_clients: #ff725c; -$entity_clients: #4269d0; -$non_entity_clients: #efb117; +$single: #1c345f; +// stacked bar chart color scheme +$first: #4269d0; +$second: #efb117; +$third: #ff725c; +$fourth: #6cc5b0; .legend-container { .dots { @@ -20,20 +21,22 @@ $non_entity_clients: #efb117; border-radius: 50%; display: inline-block; } - .legend-new_clients { - background-color: $blue-500; + .legend-dot-new_clients { + background-color: $single; } - .legend-entity_clients { - background-color: $entity_clients; + // numbers are indices because chart legend is iterated over to ensure + // legend colors match the correct stacked-bar-# class below + .legend-dot-0 { + background-color: $first; } - .legend-non_entity_clients { - background-color: $non_entity_clients; + .legend-dot-1 { + background-color: $second; } - .legend-secret_syncs { - background-color: $secret_syncs; + .legend-dot-2 { + background-color: $third; } - .legend-acme_clients { - background-color: $acme_clients; + .legend-dot-3 { + background-color: $fourth; } } @@ -85,7 +88,7 @@ $non_entity_clients: #efb117; } .lineal-chart-bar { - fill: var(--token-color-palette-blue-500); + fill: var(--token-color-palette-single); } .lineal-axis { @@ -98,20 +101,21 @@ $non_entity_clients: #efb117; } } -// @colorScale arg for Lineal::VBars is "stacked-bar", indices are added by lineal +// @colorScale arg for Lineal::VBars is "stacked-bar", numbers are added by lineal (not 0-indexed) .stacked-bar-1 { - color: $entity_clients; - fill: $entity_clients; + color: $first; + fill: $first; } .stacked-bar-2 { - color: $non_entity_clients; - fill: $non_entity_clients; + color: $second; + fill: $second; } .stacked-bar-3 { - color: $secret_syncs; - fill: $secret_syncs; + color: $third; + fill: $third; } +// MUST BE LAST because conditionally renders and legend color mapping for stacked bars will be off otherwise .stacked-bar-4 { - color: $acme_clients; - fill: $acme_clients; + color: $fourth; + fill: $fourth; } diff --git a/ui/tests/acceptance/clients/counts/overview-test.js b/ui/tests/acceptance/clients/counts/overview-test.js index f0485c1335..4b5c8e82c8 100644 --- a/ui/tests/acceptance/clients/counts/overview-test.js +++ b/ui/tests/acceptance/clients/counts/overview-test.js @@ -42,7 +42,7 @@ module('Acceptance | clients | overview', function (hooks) { assert.dom(CLIENT_COUNT.statTextValue('Secret sync')).doesNotExist(); assert.dom(CLIENT_COUNT.statTextValue('Entity')).exists('other stats are still visible'); await click(GENERAL.inputByAttr('toggle view')); - assert.dom(CHARTS.legend).hasText('Entity clients Non-entity clients Acme clients'); + assert.dom(CHARTS.legend).hasText('Entity clients Non-entity clients ACME clients'); }); // These tests use the clientsHandler which dynamically generates activity data, used for asserting date querying, etc @@ -277,7 +277,12 @@ module('Acceptance | clients | overview', function (hooks) { await visit('/vault/clients/counts/overview'); assert.dom(CLIENT_COUNT.statTextValue('Secret sync')).exists('shows secret sync data on overview'); await click(GENERAL.inputByAttr('toggle view')); - assert.dom(CHARTS.legend).hasText('Entity clients Non-entity clients Secret sync clients Acme clients'); + assert + .dom(CHARTS.legend) + .hasText( + 'Entity clients Non-entity clients ACME clients Secret sync clients', + 'it renders legend in order that matches the stacked bar data' + ); }); test('it should hide secrets sync stats when feature is NOT activated', async function (assert) { @@ -295,7 +300,12 @@ module('Acceptance | clients | overview', function (hooks) { .doesNotExist('stat is hidden because feature is not activated'); assert.dom(CLIENT_COUNT.statTextValue('Entity')).exists('other stats are still visible'); await click(GENERAL.inputByAttr('toggle view')); - assert.dom(CHARTS.legend).hasText('Entity clients Non-entity clients Acme clients'); + assert + .dom(CHARTS.legend) + .hasText( + 'Entity clients Non-entity clients ACME clients', + 'it renders legend in order that matches the stacked bar data and does not include secret sync' + ); }); test('it should show secrets sync stats for HVD managed clusters', async function (assert) { @@ -306,7 +316,12 @@ module('Acceptance | clients | overview', function (hooks) { await visit('/vault/clients/counts/overview'); assert.dom(CLIENT_COUNT.statTextValue('Secret sync')).exists(); await click(GENERAL.inputByAttr('toggle view')); - assert.dom(CHARTS.legend).hasText('Entity clients Non-entity clients Secret sync clients Acme clients'); + assert + .dom(CHARTS.legend) + .hasText( + 'Entity clients Non-entity clients ACME clients Secret sync clients', + 'it renders legend in order that matches the stacked bar data' + ); }); }); }); diff --git a/ui/tests/integration/components/clients/running-total-test.js b/ui/tests/integration/components/clients/running-total-test.js index 6bf423b647..4fbdacf6d8 100644 --- a/ui/tests/integration/components/clients/running-total-test.js +++ b/ui/tests/integration/components/clients/running-total-test.js @@ -101,14 +101,19 @@ module('Integration | Component | clients/running-total', function (hooks) { .dom(CLIENT_COUNT.card('Client usage trends for selected billing period')) .exists('running total component renders'); assert.dom(CHARTS.chart('Client usage by month')).exists('bar chart renders'); - assert.dom(CHARTS.legend).hasText('Entity clients Non-entity clients Secret sync clients Acme clients'); + assert + .dom(CHARTS.legend) + .hasText( + 'Entity clients Non-entity clients ACME clients Secret sync clients', + 'it renders legend in order that matches the stacked bar data and secret sync clients is last' + ); // assert each legend item is correct const expectedLegend = [ { label: 'Entity clients', color: 'rgb(66, 105, 208)' }, { label: 'Non-entity clients', color: 'rgb(239, 177, 23)' }, + { label: 'ACME clients', color: 'rgb(255, 114, 92)' }, { label: 'Secret sync clients', color: 'rgb(108, 197, 176)' }, - { label: 'Acme clients', color: 'rgb(255, 114, 92)' }, ]; findAll('.legend-item').forEach((e, i) => { @@ -172,13 +177,13 @@ module('Integration | Component | clients/running-total', function (hooks) { await click(GENERAL.inputByAttr('toggle view')); assert .dom(CHARTS.legend) - .hasText('Entity clients Non-entity clients Acme clients', 'legend does not include sync clients'); + .hasText('Entity clients Non-entity clients ACME clients', 'legend does not include sync clients'); // assert each legend item is correct const expectedLegend = [ { label: 'Entity clients', color: 'rgb(66, 105, 208)' }, { label: 'Non-entity clients', color: 'rgb(239, 177, 23)' }, - { label: 'Acme clients', color: 'rgb(255, 114, 92)' }, + { label: 'ACME clients', color: 'rgb(255, 114, 92)' }, ]; findAll('.legend-item').forEach((e, i) => {