From fc7ded2a4662ee3ee368fdab02777f45ccfc0a76 Mon Sep 17 00:00:00 2001 From: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Date: Thu, 28 Sep 2023 11:24:40 -0700 Subject: [PATCH] UI: Part 1 - hds adoption replace (#23363) * replace policy-form modal * replace clients/attribution modal * clients/config modal * scope form odal * remove button type * include toolbar to match other example templates * rotate credentials modal * add toolbar button class for hds buttons * transformation-edit modal * add back test selector * add route arg to button! * update link status * fix link-status tests * remove prevent default * update db tests * update tests * use page alert for hcp link status banner * fix scopy button selector * fix sidebar test * change to neutral banner --- ui/app/components/database-connection.js | 8 +- ui/app/components/policy-form.hbs | 122 ++++++++---------- ui/app/styles/components/sidebar.scss | 26 ---- ui/app/styles/core/buttons.scss | 16 +++ .../components/clients/attribution.hbs | 86 ++++++------ .../templates/components/clients/config.hbs | 69 ++++------ .../components/database-connection.hbs | 55 ++++---- ui/app/templates/components/link-status.hbs | 83 ++++++------ .../templates/components/oidc/scope-form.hbs | 75 +++++------ .../components/transformation-edit.hbs | 64 +++++---- .../core/addon/components/policy-example.hbs | 9 +- .../components/search-select-with-modal.hbs | 2 + .../secrets/backend/database/secret-test.js | 4 +- .../components/clients/attribution-test.js | 13 +- .../components/clients/config-test.js | 14 +- .../components/link-status-test.js | 29 +++-- .../components/oidc/scope-form-test.js | 22 +--- .../search-select-with-modal-test.js | 4 +- .../components/sidebar/frame-test.js | 2 +- 19 files changed, 315 insertions(+), 388 deletions(-) diff --git a/ui/app/components/database-connection.js b/ui/app/components/database-connection.js index 420b83f1f360..060a7702b2fa 100644 --- a/ui/app/components/database-connection.js +++ b/ui/app/components/database-connection.js @@ -73,15 +73,15 @@ export default class DatabaseConnectionEdit extends Component { } @action - continueWithoutRotate(evt) { - evt.preventDefault(); + continueWithoutRotate() { + this.showSaveModal = false; const { name } = this.args.model; this.transitionToRoute(SHOW_ROUTE, name); } @action - continueWithRotate(evt) { - evt.preventDefault(); + continueWithRotate() { + this.showSaveModal = false; const { backend, name } = this.args.model; this.rotateCredentials(backend, name) .then(() => { diff --git a/ui/app/components/policy-form.hbs b/ui/app/components/policy-form.hbs index ba38262433c1..db18e2c0f86d 100644 --- a/ui/app/components/policy-form.hbs +++ b/ui/app/components/policy-form.hbs @@ -23,11 +23,24 @@ {{/if}}
- {{#if @model.isNew}} - - - -
+ + + {{#if @renderPolicyExampleModal}} + {{! only true in policy create and edit routes }} + + + + {{/if}} + +
+ {{#if @model.isNew}}
-
-
- {{#if this.showFileUpload}} - - {{else}} - - {{/if}} + {{else}} + {{! EDITING - no file upload toggle}} + + {{/if}} +
+
+ {{#if this.showFileUpload}} + {{else}} - {{! EDITING - no file upload toggle}} You can use Alt+Tab (Option+Tab on MacOS) in the code editor to skip to the next field. - {{! Only renders button (and modal) if not already in the "create policy" modal }} - {{#if @renderPolicyExampleModal}} - - See - . - - {{! Only renders more information if already in the "create policy" modal }} - {{else}} -

- More information about - {{uppercase @model.policyType}} - policies can be found - - here. - -

- {{/if}}
{{#each @model.additionalAttrs as |attr|}} @@ -128,26 +110,26 @@ -{{! SAMPLE POLICY MODAL. Only renders modal if not already in create policy modal }} -{{#if @renderPolicyExampleModal}} - - - - + + Example + {{uppercase @model.policyType}} + Policy + + + + + + + + {{/if}} \ No newline at end of file diff --git a/ui/app/styles/components/sidebar.scss b/ui/app/styles/components/sidebar.scss index b34184ef42ca..ee7b7a6116af 100644 --- a/ui/app/styles/components/sidebar.scss +++ b/ui/app/styles/components/sidebar.scss @@ -24,29 +24,3 @@ color: $black; } } - -.link-status { - height: 40px; - display: flex; - justify-content: center; - align-items: center; - font-size: $size-7; - font-weight: $font-weight-semibold; - - &.connected { - background-color: var(--token-color-surface-action); - color: var(--token-color-foreground-action-active); - - a { - color: var(--token-color-foreground-action-active); - } - } - &.warning { - background-color: var(--token-color-surface-warning); - color: var(--token-color-palette-amber-300); - - a { - color: var(--token-color-palette-amber-300); - } - } -} diff --git a/ui/app/styles/core/buttons.scss b/ui/app/styles/core/buttons.scss index 8b8790314314..f64e55c04077 100644 --- a/ui/app/styles/core/buttons.scss +++ b/ui/app/styles/core/buttons.scss @@ -332,6 +332,7 @@ a.button.disabled { } } +// TODO HDS adoption cleanup: audit styles with design and see what to keep/remove once buttons are fully HDS // Existing class on component, modifying to match existing UI Structure buttons .hds-copy-button { font-weight: $font-weight-semibold; @@ -386,3 +387,18 @@ a.button.disabled { } } } + +// Existing class on component, modifying to match existing UI Structure buttons +.hds-button { + &.toolbar-button { + font-weight: $font-weight-semibold; + background: none; + border: none; + box-shadow: none; + &:hover:not(.disabled) { + background-color: $ui-gray-100; + border: 0; + color: $blue; + } + } +} diff --git a/ui/app/templates/components/clients/attribution.hbs b/ui/app/templates/components/clients/attribution.hbs index 4faa78f4fafe..7cea4fe50ecb 100644 --- a/ui/app/templates/components/clients/attribution.hbs +++ b/ui/app/templates/components/clients/attribution.hbs @@ -15,14 +15,12 @@
{{#if this.hasCsvData}} - + /> {{/if}}
@@ -91,41 +89,41 @@ {{! MODAL FOR CSV DOWNLOAD }} - - -
- - - {{#if @upgradeExplanation}} -
- - Your data contains an upgrade. - - Learn more here. - - -

{{@upgradeExplanation}}

-
- {{/if}} -
-
\ No newline at end of file +{{#if this.showCSVDownloadModal}} + + + Export attribution data + + +

+ This export will include the namespace path, authentication method path, and the associated total, entity, and + non-entity clients for the below + {{if this.formattedEndDate "date range" "month"}}. +

+

SELECTED DATE {{if this.formattedEndDate " RANGE"}}

+

+ {{this.formattedStartDate}} + {{if this.formattedEndDate "-"}} + {{this.formattedEndDate}}

+
+ + + + + + {{#if @upgradeExplanation}} +
+ + Your data contains an upgrade. + + Learn more here. + + +

{{@upgradeExplanation}}

+
+ {{/if}} +
+
+{{/if}} \ No newline at end of file diff --git a/ui/app/templates/components/clients/config.hbs b/ui/app/templates/components/clients/config.hbs index 3473ba3c1881..088f68ea2bba 100644 --- a/ui/app/templates/components/clients/config.hbs +++ b/ui/app/templates/components/clients/config.hbs @@ -47,47 +47,34 @@ - - -
- - -
-
+ {{#if this.modalOpen}} + + + {{this.modalTitle}} + + + {{#if (eq @model.enabled "On")}} +

+ Vault will start tracking data starting from today’s date, + {{date-format (now) "MMMM d, yyyy"}}. If you’ve previously enabled usage tracking, that historical data will + still be available to you. +

+ {{else}} +

+ Turning usage tracking off means that all data for the current month will be deleted. You will still be able to + query previous months. +

+

Are you sure?

+ {{/if}} +
+ + + + + + +
+ {{/if}} {{else}}
{{#each this.infoRows as |item|}} diff --git a/ui/app/templates/components/database-connection.hbs b/ui/app/templates/components/database-connection.hbs index 85f16b7c74b4..dae1eb023a5c 100644 --- a/ui/app/templates/components/database-connection.hbs +++ b/ui/app/templates/components/database-connection.hbs @@ -352,35 +352,32 @@ {{/each}} {{/if}} - - -
- - -
-
+{{#if this.showSaveModal}} + + + Rotate your root credentials? + + +

+ It’s best practice to rotate the root credential immediately after the initial configuration of each database. Once + rotated, + only Vault knows the new root password. +

+

Would you like to rotate your new credentials? You can also do this later.

+
+ + + + + + +
+{{/if}} - -

+ + {{#if (eq this.state "connected")}} This self-managed Vault is linked to @@ -19,49 +18,41 @@ for more information. {{/if}} -

-
+ + {{/if}} - - -
- -
-
\ No newline at end of file + +
+

Error

+ {{#if this.error}} + + {{this.error}} + + {{else}} +

+ Not available +

+ {{/if}} +
+
+

Additional information

+

Check the logs for more information

+
+ + + + + +{{/if}} \ No newline at end of file diff --git a/ui/app/templates/components/oidc/scope-form.hbs b/ui/app/templates/components/oidc/scope-form.hbs index 51e72319196e..d55a292d400b 100644 --- a/ui/app/templates/components/oidc/scope-form.hbs +++ b/ui/app/templates/components/oidc/scope-form.hbs @@ -29,6 +29,15 @@ Scope + + +
@@ -41,15 +50,8 @@ {{/each}}

- You can use Alt+Tab (Option+Tab on MacOS) in the code editor to skip to the next field. See - . + You can use Alt+Tab (Option+Tab on MacOS) in the code editor to skip to the next field. Click 'How to write JSON + template for scopes' to view an example.

@@ -78,40 +80,25 @@ {{/if}} - - - - \ No newline at end of file + +

+ The full list of template parameters can be found + + here. + +

+ + + + + +{{/if}} \ No newline at end of file diff --git a/ui/app/templates/components/transformation-edit.hbs b/ui/app/templates/components/transformation-edit.hbs index ba67b866c764..0a26fc653657 100644 --- a/ui/app/templates/components/transformation-edit.hbs +++ b/ui/app/templates/components/transformation-edit.hbs @@ -53,14 +53,13 @@ {{/if}} {{#if this.capabilities.canUpdate}} {{#if (gt this.model.allowed_roles.length 0)}} - + /> {{else}} Edit transformation @@ -96,30 +95,27 @@ - - -
- - Confirm - - -
-
\ No newline at end of file +{{#if this.isEditModalActive}} + + + Edit transformation + + +

+ You’re editing a transformation that is in use by at least one role. Editing it may mean that encode and decode + operations stop working. Are you sure? +

+
+ + + + + + +
+{{/if}} \ No newline at end of file diff --git a/ui/lib/core/addon/components/policy-example.hbs b/ui/lib/core/addon/components/policy-example.hbs index 89e5fed21451..be7761c3b0f7 100644 --- a/ui/lib/core/addon/components/policy-example.hbs +++ b/ui/lib/core/addon/components/policy-example.hbs @@ -42,7 +42,14 @@

{{/if}}
- +

More information about diff --git a/ui/lib/core/addon/components/search-select-with-modal.hbs b/ui/lib/core/addon/components/search-select-with-modal.hbs index e3fe952a6d22..f5b7d239e832 100644 --- a/ui/lib/core/addon/components/search-select-with-modal.hbs +++ b/ui/lib/core/addon/components/search-select-with-modal.hbs @@ -94,6 +94,8 @@ @isActive={{this.showModal}} @type="info" @showCloseButton={{false}} + {{! temporary class until Hds::Modal is fully adopted so dynamic components with copy buttons work }} + class="hds-modal" >

`); await click('[data-test-attribution-export-button]'); - assert.dom('.modal.is-active .title').hasText('Export attribution data', 'modal appears to export csv'); - assert.dom('.modal.is-active').includesText('June 2022 - December 2022'); + assert + .dom('[data-test-export-modal-title]') + .hasText('Export attribution data', 'modal appears to export csv'); + assert.dom('[ data-test-export-date-range]').includesText('June 2022 - December 2022'); }); }); diff --git a/ui/tests/integration/components/clients/config-test.js b/ui/tests/integration/components/clients/config-test.js index 9d81bda34e03..482ecbaf4e93 100644 --- a/ui/tests/integration/components/clients/config-test.js +++ b/ui/tests/integration/components/clients/config-test.js @@ -52,7 +52,7 @@ module('Integration | Component | client count config', function (hooks) { }); test('it should function in edit mode when reporting is disabled', async function (assert) { - assert.expect(13); + assert.expect(12); this.server.put('/sys/internal/counters/config', (schema, req) => { const { enabled, retention_months } = JSON.parse(req.requestBody); @@ -64,7 +64,6 @@ module('Integration | Component | client count config', function (hooks) { this.createModel('disable'); await render(hbs` - `); @@ -86,9 +85,8 @@ module('Integration | Component | client count config', function (hooks) { await fillIn('[data-test-input="retentionMonths"]', 24); await click('[data-test-clients-config-save]'); - assert.dom('.modal.is-active').exists('Modal renders'); assert - .dom('[data-test-modal-title] span') + .dom('[data-test-clients-config-modal="title"]') .hasText('Turn usage tracking on?', 'Correct modal title renders'); assert.dom('[data-test-clients-config-modal="on"]').exists('Correct modal description block renders'); @@ -100,14 +98,14 @@ module('Integration | Component | client count config', function (hooks) { await click('[data-test-input="enabled"]'); await click('[data-test-clients-config-save]'); - assert.dom('.modal.is-active').exists('Modal renders'); + assert.dom('[data-test-clients-config-modal]').exists('Modal renders'); assert - .dom('[data-test-modal-title] span') + .dom('[data-test-clients-config-modal="title"]') .hasText('Turn usage tracking off?', 'Correct modal title renders'); assert.dom('[data-test-clients-config-modal="off"]').exists('Correct modal description block renders'); await click('[data-test-clients-config-modal="cancel"]'); - assert.dom('.modal.is-active').doesNotExist('Modal is hidden on cancel'); + assert.dom('[data-test-clients-config-modal]').doesNotExist('Modal is hidden on cancel'); }); test('it should function in edit mode when reporting is enabled', async function (assert) { @@ -123,7 +121,6 @@ module('Integration | Component | client count config', function (hooks) { this.createModel('enable', true, 24); await render(hbs` - `); @@ -162,7 +159,6 @@ module('Integration | Component | client count config', function (hooks) { this.createModel(); await render(hbs` - `); await fillIn('[data-test-input="retentionMonths"]', 24); diff --git a/ui/tests/integration/components/link-status-test.js b/ui/tests/integration/components/link-status-test.js index a97a98157ba9..a0852cdd0b88 100644 --- a/ui/tests/integration/components/link-status-test.js +++ b/ui/tests/integration/components/link-status-test.js @@ -10,6 +10,14 @@ import { hbs } from 'ember-cli-htmlbars'; import { setupMirage } from 'ember-cli-mirage/test-support'; import { statuses } from '../../../mirage/handlers/hcp-link'; +const SELECTORS = { + modalOpen: '[data-test-link-status] button', + modalClose: '[data-test-icon="x"]', + bannerConnected: '.hds-alert [data-test-icon="info"]', + bannerWarning: '.hds-alert [data-test-icon="alert-triangle"]', + banner: '[data-test-link-status]', +}; + module('Integration | Component | link-status', function (hooks) { setupRenderingTest(hooks); setupMirage(hooks); @@ -22,31 +30,28 @@ module('Integration | Component | link-status', function (hooks) { test('it does not render banner when status is not present', async function (assert) { await render(hbs` - `); - assert.dom('.link-status').doesNotExist('Banner is hidden for missing status message'); + assert.dom(SELECTORS.banner).doesNotExist('Banner is hidden for missing status message'); }); test('it does not render banner in oss version', async function (assert) { this.owner.lookup('service:version').set('version', '1.13.0'); await render(hbs` - `); - assert.dom('.link-status').doesNotExist('Banner is hidden in oss'); + assert.dom(SELECTORS.banner).doesNotExist('Banner is hidden in oss'); }); test('it renders connected status', async function (assert) { await render(hbs` - `); - assert.dom('.link-status').hasClass('connected', 'Correct banner class renders for connected state'); + assert.dom(SELECTORS.bannerConnected).exists('Success banner renders for connected state'); assert .dom('[data-test-link-status]') .hasText('This self-managed Vault is linked to HCP.', 'Banner copy renders for connected state'); @@ -58,11 +63,10 @@ module('Integration | Component | link-status', function (hooks) { test('it should render error states', async function (assert) { // disconnected error await render(hbs` - `); - assert.dom('.link-status').hasClass('warning', 'Correct banner class renders for error state'); + assert.dom(SELECTORS.bannerWarning).exists('Warning banner renders for error state'); assert .dom('[data-test-link-status]') .hasText( @@ -70,7 +74,7 @@ module('Integration | Component | link-status', function (hooks) { 'Banner copy renders for error state' ); - await click('[data-test-link-status] button'); + await click(SELECTORS.modalOpen); assert .dom('[data-test-link-status-timestamp]') .hasText('2022-09-21T11:25:02.196835-07:00', 'Timestamp renders'); @@ -78,20 +82,23 @@ module('Integration | Component | link-status', function (hooks) { .dom('[data-test-link-status-error]') .hasText('unable to establish a connection with HCP', 'Error renders'); + await click(SELECTORS.modalClose); // connecting error await render(hbs` - `); + await click(SELECTORS.modalOpen); assert .dom('[data-test-link-status-error]') .hasText('principal does not have the permission to register as a provider', 'Error renders'); + await click(SELECTORS.modalClose); // this shouldn't happen but placeholders should render if disconnected/connecting status is returned without timestamp and/or error await render(hbs` - `); + await click(SELECTORS.modalOpen); + assert.dom('[data-test-link-status-timestamp]').hasText('Not available', 'Timestamp placeholder renders'); assert.dom('[data-test-link-status-error]').hasText('Not available', 'Error placeholder renders'); }); diff --git a/ui/tests/integration/components/oidc/scope-form-test.js b/ui/tests/integration/components/oidc/scope-form-test.js index 36619bb582b6..dc7fc2491d88 100644 --- a/ui/tests/integration/components/oidc/scope-form-test.js +++ b/ui/tests/integration/components/oidc/scope-form-test.js @@ -35,7 +35,6 @@ module('Integration | Component | oidc/scope-form', function (hooks) { @onCancel={{this.onCancel}} @onSave={{this.onSave}} /> - `); assert.dom('[data-test-oidc-scope-title]').hasText('Create Scope', 'Form title renders'); @@ -87,7 +86,6 @@ module('Integration | Component | oidc/scope-form', function (hooks) { @onCancel={{this.onCancel}} @onSave={{this.onSave}} /> - `); assert.dom('[data-test-oidc-scope-title]').hasText('Edit Scope', 'Form title renders'); @@ -122,7 +120,6 @@ module('Integration | Component | oidc/scope-form', function (hooks) { @onCancel={{this.onCancel}} @onSave={{this.onSave}} /> - `); await click(SELECTORS.scopeCancelButton); @@ -154,8 +151,8 @@ module('Integration | Component | oidc/scope-form', function (hooks) { }); test('it should show example template modal', async function (assert) { - assert.expect(8); - + assert.expect(5); + const MODAL = (e) => `[data-test-scope-modal="${e}"]`; this.model = this.store.createRecord('oidc/scope'); // formatting here is purposeful so that it matches formatting in the template modal @@ -174,23 +171,17 @@ module('Integration | Component | oidc/scope-form', function (hooks) { @onCancel={{this.onCancel}} @onSave={{this.onSave}} /> - `); - assert.dom('[data-test-modal-div]').doesNotHaveClass('is-active', 'Modal is hidden'); await click('[data-test-oidc-scope-example]'); - assert.dom('[data-test-modal-div]').hasClass('is-active', 'Modal is shown'); - assert.dom('[data-test-modal-title]').hasText('Scope template', 'Modal title renders'); - assert - .dom('[data-test-modal-text]') - .hasText('Example of a JSON template for scopes:', 'Modal text renders'); - assert.dom('[data-test-copy-button]').exists('Modal copy button renders'); + assert.dom(MODAL('title')).hasText('Scope template', 'Modal title renders'); + assert.dom(MODAL('text')).hasText('Example of a JSON template for scopes:', 'Modal text renders'); assert - .dom('[data-test-modal-div] [data-test-copy-button]') + .dom('#scope-template-modal [data-test-copy-button]') .hasAttribute('data-clipboard-text', exampleTemplate, 'Modal copy button copies the example template'); assert.dom('.cm-string').hasText('"username"', 'Example template json renders'); await click('[data-test-close-modal]'); - assert.dom('[data-test-modal-div]').doesNotHaveClass('is-active', 'Modal is hidden'); + assert.dom('.hds#scope-template-modal').doesNotExist('Modal is hidden'); }); test('it should render error alerts when API returns an error', async function (assert) { @@ -203,7 +194,6 @@ module('Integration | Component | oidc/scope-form', function (hooks) { @onCancel={{this.onCancel}} @onSave={{this.onSave}} /> - `); await fillIn('[data-test-input="name"]', 'test-scope'); await click(SELECTORS.scopeSaveButton); diff --git a/ui/tests/integration/components/search-select-with-modal-test.js b/ui/tests/integration/components/search-select-with-modal-test.js index 2675cd75e110..e0393d41cfe1 100644 --- a/ui/tests/integration/components/search-select-with-modal-test.js +++ b/ui/tests/integration/components/search-select-with-modal-test.js @@ -146,7 +146,9 @@ module('Integration | Component | search select with modal', function (hooks) { ); await component.selectOption(); - assert.dom('[data-test-modal-div]').hasAttribute('class', 'modal is-info is-active', 'modal is active'); + assert + .dom('[data-test-modal-div]') + .hasAttribute('class', 'modal is-info is-active hds-modal', 'modal is active'); assert.dom('[data-test-empty-state-title]').hasText('No policy type selected'); assert.ok(this.onChange.notCalled, 'onChange is not called'); }); diff --git a/ui/tests/integration/components/sidebar/frame-test.js b/ui/tests/integration/components/sidebar/frame-test.js index cafb90b2af64..e310988a3f6f 100644 --- a/ui/tests/integration/components/sidebar/frame-test.js +++ b/ui/tests/integration/components/sidebar/frame-test.js @@ -37,7 +37,7 @@ module('Integration | Component | sidebar-frame', function (hooks) { `); - assert.dom('.link-status').exists('Link status component renders'); + assert.dom('[data-test-link-status]').exists('Link status component renders'); assert.dom('[data-test-component="console/ui-panel"]').exists('Console UI panel renders'); assert.dom('.page-container').exists('Block yields for app content'); });