Skip to content

Commit

Permalink
Redesign: Improved notification message (Potentially breaking)
Browse files Browse the repository at this point in the history
* Better variables
* Slightly more readable colors
* Support `prefers-reduced-motion` and reduces motion in Ember tests by default
* Added proper ARIA role to notification
* Partially replaces #250
  • Loading branch information
pichfl committed Dec 15, 2021
1 parent f1ba75e commit e563265
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 92 deletions.
17 changes: 12 additions & 5 deletions addon/components/notification-message.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ export default class NotificationMessage extends Component {

@computed('notification.dismiss')
get dismissClass() {
return !this.notification.dismiss ? 'c-notification--in' : '';
return this.notification.dismiss
? 'ecn_notification-out'
: 'ecn_notification-in';
}

@computed('notification.onClick')
Expand Down Expand Up @@ -78,6 +80,7 @@ export default class NotificationMessage extends Component {
@action
handleOnClick(event) {
event.preventDefault();

this.notification.onClick?.(this.notification);
}

Expand All @@ -90,17 +93,21 @@ export default class NotificationMessage extends Component {

@action
handleMouseEnter() {
if (this.notification.autoClear) {
let notification = this.notification;

if (notification.autoClear) {
set(this, 'paused', true);
this.notifications.pauseAutoClear(this.notification);
this.notifications.pauseAutoClear(notification);
}
}

@action
handleMouseLeave() {
if (this.notification.autoClear) {
let notification = this.notification;

if (notification.autoClear) {
set(this, 'paused', false);
this.notifications.setupAutoClear(this.notification);
this.notifications.setupAutoClear(notification);
}
}
}
46 changes: 40 additions & 6 deletions addon/styles/components/notification-container.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,53 @@
--ecn-container-width: 80%;
--ecn-container-max-with: 400px;

/* Notifications */
--ecn-icon-width: 30px;
--ecn-icon-position: 10px;
--ecn-icon-color: rgba(255, 255, 255, 0.74);
--ecn-icon-color: inherit;
--ecn-icon-opacity: 0.74;
--ecn-icon-lighten-background: rgba(255, 255, 255, 0.2);
--ecn-countdown-lighten-background: rgba(255, 255, 255, 0.4);
--ecn-notification-max-height: 800px;
--ecn-notification-border-radius: 3px;
--ecn-notification-border-radius: 4px;

/* Colours */
--ecn-green: #64ce83;
--ecn-blue: #3ea2ff;
--ecn-orange: #ff7f48;
--ecn-red: #e74c3c;
--ecn-green: #22c55e;
--ecn-blue: #0ea5e9;
--ecn-orange: #f97316;
--ecn-red: #ef4444;
--ecn-white: #fff;

/* Types */
--ecn-success-background: var(--ecn-green);
--ecn-info-background: var(--ecn-blue);
--ecn-warning-background: var(--ecn-orange);
--ecn-error-background: var(--ecn-red);
--ecn-success-text: var(--ecn-white);
--ecn-info-text: var(--ecn-white);
--ecn-warning-text: var(--ecn-white);
--ecn-error-text: var(--ecn-white);

/* Animation */
--ecn-animation-in: notification-show 180ms 0ms
cubic-bezier(0.175, 0.885, 0.32, 1.27499);
--ecn-animation-out: notification-hide 250ms
cubic-bezier(0.33859, -0.42, 1, -0.22),
notification-out 250ms 250ms cubic-bezier(0.5, 0, 0, 1);
}

/* Prefers reduced motion */
@media (prefers-reduced-motion: reduce) {
:root {
--ecn-animation-in: notification-show 0.001ms;
--ecn-animation-out: notification-out 0.001ms;
}
}

/* Apply reduced motion in tests */
#ember-testing {
--ecn-animation-in: notification-show 0.001ms;
--ecn-animation-out: notification-out 0.001ms;
}

/* Keyframes */
Expand Down
121 changes: 68 additions & 53 deletions addon/styles/components/notification-message.css
Original file line number Diff line number Diff line change
@@ -1,69 +1,102 @@
/* Values */
.ember-cli-notifications-notification__container .c-notification {
display: flex;
align-items: stretch;
.ecn_notification {
position: relative;
overflow: hidden;
border-radius: var(--ecn-notification-border-radius);
border-bottom: 1rem;
color: white;
max-height: var(--ecn-notification-max-height);
animation: notification-hide 250ms cubic-bezier(.33859, -.42, 1, -.22), notification-shrink 250ms 250ms cubic-bezier(.5, 0, 0, 1);
color: white;
border-radius: var(--ecn-notification-border-radius);
box-shadow: 0 3px 12px -4px rgba(20,20,20,0.5), 0 2px 2px 0 rgba(20,20,20,0.08);
font-size: 1em;
line-height: 1.5em;
animation: var(--ecn-animation-out);
animation-fill-mode: forwards;
margin-bottom: var(--ecn-spacing-2);
}

.ember-cli-notifications-notification__container .c-notification--clickable {
cursor: pointer;
.ecn_notification + .ecn_notification {
margin-top: var(--ecn-spacing-2);
}

.ecn_notification-info {
background-color: var(--ecn-info-background);
color: var(--ecn-info-text);
}

.ecn_notification-success {
background-color: var(--ecn-success-background);
color: var(--ecn-success-text);
}

.ecn_notification-warning {
background-color: var(--ecn-warning-background);
color: var(--ecn-warning-text);
}

.ecn_notification-error {
background-color: var(--ecn-error-background);
color: var(--ecn-error-text);
}

.ember-cli-notifications-notification__container .c-notification--in {
animation: notification-show 180ms cubic-bezier(.175, .885, .32, 1.27499);
.ecn_notification.ecn_notification-in {
animation: var(--ecn-animation-in);
}

.ember-cli-notifications-notification__container .c-notification__content {
.ecn_notification_body {
display: flex;
align-items: stretch;
justify-content: stretch;
overflow: hidden;
}

.ecn_content {
flex: 1 1 auto;
min-width: 0;
min-height: 0;
justify-content: space-between;
padding: var(--ecn-spacing-1) var(--ecn-spacing-2);
word-break: break-word;
vertical-align: middle;
}

.ember-cli-notifications-notification__container .c-notification__content a {
color: #fff;
.ecn_content a {
color: currentColor;
text-decoration: underline;
font-weight: 700;
}

.ember-cli-notifications-notification__container .c-notification__icon {
padding: var(--ecn-spacing-1) 0;
text-align: center;
.ecn_icon {
display: flex;
flex-flow: column;
flex: none;
padding: var(--ecn-spacing-1) calc(var(--ecn-spacing-1) * 1.25);
opacity: var(--ecn-icon-opacity, 1);
}

.ecn_icon-type {
background-color: var(--ecn-icon-lighten-background);
width: var(--ecn-icon-width);
color: var(--ecn-icon-color);
color: var(--ecn-icon-color, inherit);
}

.ember-cli-notifications-notification__container .c-notification__svg {
width: 16px;
height: 16px;
vertical-align: text-top;
.ecn_icon svg {
display: block;
height: 1.5em;
}

.ember-cli-notifications-notification__container .c-notification__close {
margin-left: var(--ecn-spacing-2);
align-self: flex-start;
opacity: .74;
.ecn_icon-close {
margin: 0 0 0 var(--ecn-spacing-2);
padding: var(--ecn-spacing-1);
background: transparent;
color: inherit;
border: 0;
border-radius: 0;
font-size: inherit;
line-height: inherit;
appearance: none;
cursor: pointer;
transition: 0.2s opacity ease;
}

.ember-cli-notifications-notification__container .c-notification__close:hover,
.ember-cli-notifications-notification__container .c-notification__close:focus {
.ecn_icon-close:hover,
.ecn_icon-close:focus {
opacity: 1;
}

.ember-cli-notifications-notification__container .c-notification__countdown {
.ecn_countdown {
position: absolute;
bottom: 0;
left: 0;
Expand All @@ -72,21 +105,3 @@
height: 4px;
animation: notification-countdown linear 1;
}

/* Theme */
.ember-cli-notifications-notification__container .c-notification--info {
background-color: var(--ecn-blue);
}

.ember-cli-notifications-notification__container .c-notification--success {
background-color: var(--ecn-green);
}

.ember-cli-notifications-notification__container .c-notification--warning {
background-color: var(--ecn-orange);
}

.ember-cli-notifications-notification__container .c-notification--error {
background-color: var(--ecn-red);
}

59 changes: 33 additions & 26 deletions addon/templates/components/notification-message.hbs
Original file line number Diff line number Diff line change
@@ -1,42 +1,49 @@
{{! template-lint-disable no-invalid-interactive no-triple-curlies }}
{{! template-lint-disable no-invalid-interactive }}
<div
class="c-notification
{{this.dismissClass}}
class="ecn_notification
{{if this.validType (concat "ecn_notification-" this.validType) ""}}
{{this.clickableClass}}
{{if this.validType (concat "c-notification--" this.validType) ""}}
{{this.dismissClass}}
{{@notification.cssClasses}}"
data-test-notification-message={{@notification.type}}
role="alert"
{{on "mouseenter" this.handleMouseEnter}}
{{on "mouseleave" this.handleMouseLeave}}
>
<div class="c-notification__icon">
<div class="ecn_notification_body">
{{#if this.validType}}
{{#if this.isInfo}}
<EcnIconInfo class="c-notification__svg" />
{{else if this.isSuccess}}
<EcnIconSuccess class="c-notification__svg" />
{{else if this.isWarning}}
<EcnIconWarning class="c-notification__svg" />
{{else if this.isError}}
<EcnIconError class="c-notification__svg" />
{{/if}}
<div class="ecn_icon ecn_icon-type" data-test-notification-icon>
{{#if this.isInfo}}
<EcnIconInfo />
{{else if this.isSuccess}}
<EcnIconSuccess />
{{else if this.isWarning}}
<EcnIconWarning />
{{else if this.isError}}
<EcnIconError />
{{/if}}
</div>
{{/if}}
</div>
<div class="c-notification__content" {{on "click" this.handleOnClick}}>
{{this.message}}

<div
class="ecn_content"
{{on "click" this.handleOnClick}}
role={{if @notification.onClick "button"}}
data-test-notification-content
>
{{this.message}}
</div>

<div
class="c-notification__close"
class="ecn_icon ecn_icon-close"
{{on "click" this.removeNotification}}
title="Dismiss this notification"
>
<EcnIconClose class="c-notification__svg" />
<EcnIconClose />
</div>
</div>

{{#if @notification.autoClear}}
<div
class="c-notification__countdown"
style={{this.notificationClearDuration}}
></div>
{{/if}}
{{#if @notification.autoClear}}
<div class="ecn_countdown" style={{this.notificationClearDuration}}></div>
{{/if}}
</div>
</div>
4 changes: 2 additions & 2 deletions tests/integration/components/notification-message-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ module('Integration | Component | notification message', function (hooks) {

await settled();

await click('.c-notification__content');
await click('[data-test-notification-content][role="button"]');
});

test('clicking the notification close button does not call the callback defined on the notification message', async function (assert) {
Expand All @@ -37,7 +37,7 @@ module('Integration | Component | notification message', function (hooks) {
});

await settled();
await click('.c-notification__close');
await click('[data-test-notification-close]');
await settled();
});
});

0 comments on commit e563265

Please sign in to comment.