mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-03 12:07:54 +00:00
UI: Use HDS::Toast for flash messages (#25459)
* Move global-flash to HDS-specified area * Add flash-toast component * use flash toast for flash messages * Use spacing vars * Remove unnecessary key * Cleanup + tests * Remove nondeterministic build warning * add changelog * I wish this was automatic
This commit is contained in:
3
changelog/25459.txt
Normal file
3
changelog/25459.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:change
|
||||||
|
ui: flash messages render on right side of page
|
||||||
|
```
|
||||||
11
ui/app/components/flash-toast.hbs
Normal file
11
ui/app/components/flash-toast.hbs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{{!
|
||||||
|
Copyright (c) HashiCorp, Inc.
|
||||||
|
SPDX-License-Identifier: BUSL-1.1
|
||||||
|
~}}
|
||||||
|
|
||||||
|
<Hds::Toast @color={{this.color}} @onDismiss={{@close}} class="has-bottom-margin-m" data-test-flash-toast as |T|>
|
||||||
|
<T.Title data-test-flash-toast-title>{{this.title}}</T.Title>
|
||||||
|
<T.Description data-test-flash-message-body>
|
||||||
|
<p class="is-word-break">{{@flash.message}}</p>
|
||||||
|
</T.Description>
|
||||||
|
</Hds::Toast>
|
||||||
37
ui/app/components/flash-toast.js
Normal file
37
ui/app/components/flash-toast.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: BUSL-1.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { capitalize } from '@ember/string';
|
||||||
|
import Component from '@glimmer/component';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlashToast components are used to translate flash messages into toast notifications.
|
||||||
|
* Flash object passed should have a `type` and `message` property at minimum.
|
||||||
|
*/
|
||||||
|
export default class FlashToastComponent extends Component {
|
||||||
|
get color() {
|
||||||
|
switch (this.args.flash.type) {
|
||||||
|
case 'info':
|
||||||
|
return 'highlight';
|
||||||
|
case 'danger':
|
||||||
|
return 'critical';
|
||||||
|
case 'warning':
|
||||||
|
case 'success':
|
||||||
|
return this.args.flash.type;
|
||||||
|
default:
|
||||||
|
return 'neutral';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get title() {
|
||||||
|
if (this.args.title) return this.args.title;
|
||||||
|
switch (this.args.flash.type) {
|
||||||
|
case 'danger':
|
||||||
|
return 'Error';
|
||||||
|
default:
|
||||||
|
return capitalize(this.args.flash.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,10 +4,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
.global-flash {
|
.global-flash {
|
||||||
bottom: 0;
|
bottom: $spacing-32;
|
||||||
left: $spacing-12;
|
right: $spacing-24;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
max-width: $drawer-width;
|
max-width: 360px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
width: 95%;
|
width: 95%;
|
||||||
z-index: 300;
|
z-index: 300;
|
||||||
|
|||||||
@@ -75,24 +75,7 @@
|
|||||||
<div class="global-flash">
|
<div class="global-flash">
|
||||||
{{#each this.flashMessages.queue as |flash|}}
|
{{#each this.flashMessages.queue as |flash|}}
|
||||||
<FlashMessage data-test-flash-message={{true}} @flash={{flash}} as |customComponent flash close|>
|
<FlashMessage data-test-flash-message={{true}} @flash={{flash}} as |customComponent flash close|>
|
||||||
{{#if flash.componentName}}
|
<FlashToast @flash={{flash}} @close={{close}} />
|
||||||
{{component flash.componentName content=flash.content}}
|
|
||||||
{{else}}
|
|
||||||
{{#let (hash info="highlight" success="success" danger="critical" warning="warning") as |color|}}
|
|
||||||
<Hds::Alert @type="inline" @color={{get color flash.type}} class="has-bottom-margin-s" @onDismiss={{close}} as |A|>
|
|
||||||
{{#let (hash info="Info" success="Success" danger="Error" warning="Warning") as |title|}}
|
|
||||||
<A.Title class="alert-title">{{get title flash.type}}</A.Title>
|
|
||||||
{{/let}}
|
|
||||||
<A.Description data-test-flash-message-body>
|
|
||||||
{{#if flash.preformatted}}
|
|
||||||
<p class="is-word-break">{{flash.message}}</p>
|
|
||||||
{{else}}
|
|
||||||
{{flash.message}}
|
|
||||||
{{/if}}
|
|
||||||
</A.Description>
|
|
||||||
</Hds::Alert>
|
|
||||||
{{/let}}
|
|
||||||
{{/if}}
|
|
||||||
</FlashMessage>
|
</FlashMessage>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ IF YOUR TOKEN HAS THE PROPER CAPABILITIES, THIS WILL CREATE AND DELETE ITEMS ON
|
|||||||
Your token will also be shown on the screen in the example curl command output.`;
|
Your token will also be shown on the screen in the example curl command output.`;
|
||||||
this.flashMessages.warning(warning, {
|
this.flashMessages.warning(warning, {
|
||||||
sticky: true,
|
sticky: true,
|
||||||
preformatted: true,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
58
ui/tests/integration/components/flash-toast-test.js
Normal file
58
ui/tests/integration/components/flash-toast-test.js
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) HashiCorp, Inc.
|
||||||
|
* SPDX-License-Identifier: BUSL-1.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { module, test } from 'qunit';
|
||||||
|
import { setupRenderingTest } from 'vault/tests/helpers';
|
||||||
|
import { click, find, render } from '@ember/test-helpers';
|
||||||
|
import { hbs } from 'ember-cli-htmlbars';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
module('Integration | Component | flash-toast', function (hooks) {
|
||||||
|
setupRenderingTest(hooks);
|
||||||
|
|
||||||
|
hooks.beforeEach(function () {
|
||||||
|
this.flash = {
|
||||||
|
type: 'info',
|
||||||
|
message: 'The bare minimum flash message',
|
||||||
|
};
|
||||||
|
this.closeSpy = sinon.spy();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders', async function (assert) {
|
||||||
|
await render(hbs`<FlashToast @flash={{this.flash}} @close={{this.closeSpy}} />`);
|
||||||
|
|
||||||
|
assert.dom('[data-test-flash-message-body]').hasText('The bare minimum flash message');
|
||||||
|
assert.dom('[data-test-flash-toast]').hasClass('hds-alert--color-highlight');
|
||||||
|
await click('button');
|
||||||
|
assert.ok(this.closeSpy.calledOnce, 'close action was called');
|
||||||
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
{ type: 'info', title: 'Info', color: 'hds-alert--color-highlight' },
|
||||||
|
{ type: 'success', title: 'Success', color: 'hds-alert--color-success' },
|
||||||
|
{ type: 'warning', title: 'Warning', color: 'hds-alert--color-warning' },
|
||||||
|
{ type: 'danger', title: 'Error', color: 'hds-alert--color-critical' },
|
||||||
|
{ type: 'foobar', title: 'Foobar', color: 'hds-alert--color-neutral' },
|
||||||
|
].forEach(({ type, title, color }) => {
|
||||||
|
test(`it has correct title and color for type: ${type}`, async function (assert) {
|
||||||
|
this.flash.type = type;
|
||||||
|
await render(hbs`<FlashToast @flash={{this.flash}} @close={{this.closeSpy}} />`);
|
||||||
|
|
||||||
|
assert.dom('[data-test-flash-toast-title]').hasText(title, 'title is correct');
|
||||||
|
assert.dom('[data-test-flash-toast]').hasClass(color, 'color is correct');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('it renders messages with whitespaces correctly', async function (assert) {
|
||||||
|
this.flash.message = `multi-
|
||||||
|
|
||||||
|
line msg`;
|
||||||
|
|
||||||
|
await render(hbs`<FlashToast @flash={{this.flash}} @close={{this.closeSpy}} />`);
|
||||||
|
const dom = find('[data-test-flash-message-body]');
|
||||||
|
const lineHeight = 20;
|
||||||
|
assert.true(dom.clientHeight > lineHeight, 'renders message on multiple lines');
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user