Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI: Use HDS::Toast for flash messages #25459

Merged
merged 9 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/25459.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:change
ui: flash messages render on right side of page
```
11 changes: 11 additions & 0 deletions ui/app/components/flash-toast.hbs
Original file line number Diff line number Diff line change
@@ -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>
Copy link

@didoo didoo Feb 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@hashishaw FYI the Description field of the Alert/Toast already breaking long words, so this could be simply:

<T.Description data-test-flash-message-body>{{@flash.message}}</T.Description>
screenshot_3524

</T.Description>
</Hds::Toast>
37 changes: 37 additions & 0 deletions ui/app/components/flash-toast.js
Original file line number Diff line number Diff line change
@@ -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':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this supposed to return 'warning'?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a shorthand for when type is warning or success, return the type

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always forget that switch cases allow you to have default values!

case 'danger':
return 'Error';
default:
return capitalize(this.args.flash.type);
}
}
}
File renamed without changes.
6 changes: 3 additions & 3 deletions ui/app/styles/components/global-flash.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
*/

.global-flash {
bottom: 0;
left: $spacing-12;
bottom: $spacing-32;
right: $spacing-24;
margin: 10px;
max-width: $drawer-width;
max-width: 360px;
position: fixed;
width: 95%;
z-index: 300;
Expand Down
19 changes: 1 addition & 18 deletions ui/app/templates/vault/cluster.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -75,24 +75,7 @@
<div class="global-flash">
{{#each this.flashMessages.queue as |flash|}}
<FlashMessage data-test-flash-message={{true}} @flash={{flash}} as |customComponent flash close|>
{{#if flash.componentName}}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see this setting used anywhere so I figured we can remove it

{{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}}
<FlashToast @flash={{flash}} @close={{close}} />
</FlashMessage>
{{/each}}
</div>
Expand Down
1 change: 0 additions & 1 deletion ui/lib/open-api-explorer/addon/routes/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.`;
this.flashMessages.warning(warning, {
sticky: true,
preformatted: true,
Copy link
Contributor Author

@hashishaw hashishaw Feb 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new component renders this correctly by default:
Screenshot 2024-02-15 at 13 59 29

});
}
}
58 changes: 58 additions & 0 deletions ui/tests/integration/components/flash-toast-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import { module, test } from 'qunit';
hashishaw marked this conversation as resolved.
Show resolved Hide resolved
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');
});
});
Loading