chore: 🤖 add notification component
This commit is contained in:
8 files changed
+308
No files matched your search
@@ -0,0 +1,38 @@
|
||||
import Component from '@ember/component';
|
||||
import layout from '../../templates/components/rose/notification';
|
||||
import { computed } from '@ember/object';
|
||||
|
||||
/**
|
||||
* An accessible alert notification component that displays an icon,
|
||||
* a heading, and optional block content. Notifications may be static or
|
||||
* dismissible. To make a notification dismissible, pass in a
|
||||
* `@dismiss` function.
|
||||
*/
|
||||
export default Component.extend({
|
||||
layout,
|
||||
tagName: '',
|
||||
|
||||
/**
|
||||
* Returns an icon name based on the notification style.
|
||||
* @type {string}
|
||||
*/
|
||||
@computed('style')
|
||||
get icon() {
|
||||
let icon = 'info-circle-fill';
|
||||
|
||||
switch(this.style) {
|
||||
case 'failure':
|
||||
icon = 'cancel-square-fill';
|
||||
break;
|
||||
case 'success':
|
||||
icon = 'check-circle-fill';
|
||||
break;
|
||||
case 'warning':
|
||||
icon = 'alert-triangle';
|
||||
break;
|
||||
}
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
import { text, boolean, select } from '@storybook/addon-knobs';
|
||||
|
||||
<Meta title="Rose|Notification" component="Notification" />
|
||||
|
||||
# Notification
|
||||
|
||||
Notifications come in four semantic styles: `info` (default), `success`,
|
||||
`warning`, and `failure`. Heading text is required. Additional block content
|
||||
is optional.
|
||||
|
||||
Notifications may be made dismissible by passing a `@dismiss` function into
|
||||
the component. Dismissible notifications _must_ also have `@dismissText`.
|
||||
This component does not handle dismissal though. Be sure your `@dismiss`
|
||||
handler takes appropriate action and hides the notification.
|
||||
|
||||
```htmlbars
|
||||
<Rose::Notification
|
||||
@style="info"
|
||||
@heading="Alert Notification"
|
||||
@dismiss={{dismissHandler}}
|
||||
@dismissText="Dismiss"
|
||||
>
|
||||
An error occurred.
|
||||
</Rose::Notification>
|
||||
```
|
||||
|
||||
<Preview>
|
||||
<Story name="Basic Notification">{{
|
||||
template: hbs`
|
||||
<Rose::Notification
|
||||
@style="info"
|
||||
@heading="Notification without dismissal"
|
||||
>
|
||||
Some useful information.
|
||||
</Rose::Notification>
|
||||
`,
|
||||
context: {}
|
||||
}}</Story>
|
||||
</Preview>
|
||||
|
||||
<Preview>
|
||||
<Story name="Basic Notifications with Dismissal" height="30rem">{{
|
||||
template: hbs`
|
||||
<Rose::Notification
|
||||
@style="info"
|
||||
@heading="Dismissible Notification"
|
||||
@dismiss={{dismiss}}
|
||||
@dismissText="Dismiss"
|
||||
>
|
||||
Some useful information.
|
||||
</Rose::Notification>
|
||||
<Rose::Notification
|
||||
@style="success"
|
||||
@heading="Success"
|
||||
@dismiss={{dismiss}}
|
||||
@dismissText="Dismiss"
|
||||
>
|
||||
Good job!
|
||||
</Rose::Notification>
|
||||
<Rose::Notification
|
||||
@style="warning"
|
||||
@heading="Warning"
|
||||
@dismiss={{dismiss}}
|
||||
@dismissText="Dismiss"
|
||||
>
|
||||
Careful now.
|
||||
</Rose::Notification>
|
||||
<Rose::Notification
|
||||
@style="failure"
|
||||
@heading="Error"
|
||||
@dismiss={{dismiss}}
|
||||
@dismissText="Dismiss"
|
||||
>
|
||||
An error occurred.
|
||||
</Rose::Notification>
|
||||
`,
|
||||
context: {
|
||||
dismiss: action('dismiss'),
|
||||
}
|
||||
}}</Story>
|
||||
</Preview>
|
||||
@@ -1,3 +1,4 @@
|
||||
@import 'button';
|
||||
@import 'form';
|
||||
@import 'header';
|
||||
@import 'notification';
|
||||
@@ -0,0 +1,105 @@
|
||||
@use '../variables/sizing';
|
||||
@use '../utilities/type';
|
||||
|
||||
.rose-notification {
|
||||
$s: sizing.rems(s); // 12
|
||||
$xs: sizing.rems(xs); // 8
|
||||
$xxs: sizing.rems(xxs); // 4
|
||||
$xxxs: sizing.rems(xxxs); // 2
|
||||
$xxxxs: sizing.rems(xxxxs); // 1
|
||||
|
||||
position: relative;
|
||||
padding: sizing.rems(m);
|
||||
margin-bottom: sizing.rems(m);
|
||||
border: sizing.rems(xxxxs) solid;
|
||||
border-radius: $xxxs;
|
||||
box-shadow: 0 $xs $s rgba(var(--stark-components), var(--opacity-3)),
|
||||
0 $xxs $xxs rgba(var(--stark-components), var(--opacity-4));
|
||||
|
||||
.rose-notification-icon {
|
||||
position: relative;
|
||||
top: $xxxxs;
|
||||
}
|
||||
|
||||
.rose-notification-header {
|
||||
@include type.type(m, semibold);
|
||||
}
|
||||
|
||||
.rose-notification-body {
|
||||
@include type.type(s);
|
||||
// hack alert, because the icons are still hard to work with
|
||||
margin-left: sizing.rems(m) + $xxs;
|
||||
}
|
||||
|
||||
.rose-notification-dismiss {
|
||||
position: absolute;
|
||||
color: currentColor;
|
||||
background-color: transparent;
|
||||
border-radius: $xxxs;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
|
||||
top: sizing.rems(s);
|
||||
right: sizing.rems(s);
|
||||
|
||||
.rose-icon {
|
||||
display: block;
|
||||
padding: $xxs;
|
||||
}
|
||||
|
||||
.rose-notification-dismiss-text {
|
||||
position: absolute;
|
||||
top: -1000px;
|
||||
left: -1000px;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(var(--stark-components), var(--opacity-3))
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: rgba(var(--stark-components), var(--opacity-2))
|
||||
}
|
||||
}
|
||||
|
||||
--icon-color: var(--info);
|
||||
--header-color: var(--info-starker-1);
|
||||
--border-color: var(--info-subtler-1);
|
||||
--background-color: var(--info-subtler-3);
|
||||
|
||||
border-color: var(--border-color);
|
||||
background-color: var(--background-color);
|
||||
|
||||
.rose-notification-icon {
|
||||
color: var(--icon-color);
|
||||
}
|
||||
|
||||
.rose-notification-header {
|
||||
color: var(--header-color);
|
||||
}
|
||||
|
||||
&.is-failure {
|
||||
--icon-color: var(--failure);
|
||||
--header-color: var(--failure);
|
||||
--border-color: var(--failure-subtler-1);
|
||||
--background-color: var(--failure-subtler-2);
|
||||
}
|
||||
|
||||
&.is-success {
|
||||
--icon-color: var(--success);
|
||||
--header-color: var(--success-starker-1);
|
||||
--border-color: var(--success-subtler-1);
|
||||
--background-color: var(--success-subtler-2);
|
||||
}
|
||||
|
||||
&.is-warning {
|
||||
--icon-color: var(--warning);
|
||||
--header-color: var(--warning-starker-1);
|
||||
--border-color: var(--warning-subtler-1);
|
||||
--background-color: var(--warning-subtler-2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<section
|
||||
...attributes
|
||||
class="rose-notification is-{{@style}}{{if @dismiss " is-dismissible"}}"
|
||||
role="alert"
|
||||
aria-live="{{if @dismiss "assertive"}}"
|
||||
aria-atomic="true"
|
||||
>
|
||||
|
||||
<header class="rose-notification-header">
|
||||
<Rose::Icon
|
||||
class="rose-notification-icon"
|
||||
@name={{this.icon}}
|
||||
@size="medium" />
|
||||
{{@heading}}
|
||||
</header>
|
||||
|
||||
<div class="rose-notification-body">
|
||||
{{yield}}
|
||||
</div>
|
||||
|
||||
{{#if @dismiss}}
|
||||
<button
|
||||
type="button"
|
||||
class="rose-notification-dismiss"
|
||||
title={{@dismissText}}
|
||||
{{on "click" @dismiss}}
|
||||
>
|
||||
<Rose::Icon
|
||||
@name="cancel-plain"
|
||||
@size="medium" />
|
||||
<span class="rose-notification-dismiss-text">
|
||||
{{@dismissText}}
|
||||
</span>
|
||||
</button>
|
||||
{{/if}}
|
||||
|
||||
</section>
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/notification';
|
||||
@@ -30,6 +30,15 @@
|
||||
|
||||
Dummy app index template
|
||||
|
||||
<Rose::Notification
|
||||
@style="failure"
|
||||
@heading="Error"
|
||||
@dismiss={{this.actionHandler}}
|
||||
@dismissText="Dismiss"
|
||||
>
|
||||
An error occurred.
|
||||
</Rose::Notification>
|
||||
|
||||
<h2>Icon</h2>
|
||||
<Rose::Icon @name="alert-circle-fill" />
|
||||
<Rose::Icon @name="alert-circle-fill" @size="medium" />
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render, find, click } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | rose/notification', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
await render(hbs`
|
||||
<Rose::Notification
|
||||
@style="info"
|
||||
@heading="Alert Notification"
|
||||
>
|
||||
An error occurred.
|
||||
</Rose::Notification>
|
||||
`);
|
||||
assert.ok(find('.rose-notification'));
|
||||
assert.equal(find('.rose-notification-body').textContent.trim(), 'An error occurred.');
|
||||
});
|
||||
|
||||
test('it has optional dismiss button which triggers a function', async function(assert) {
|
||||
assert.expect(1);
|
||||
this.dismiss = () => assert.ok(true, 'dismiss clicked');
|
||||
await render(hbs`
|
||||
<Rose::Notification
|
||||
@style="info"
|
||||
@heading="Alert Notification"
|
||||
@dismiss={{this.dismiss}} />
|
||||
`);
|
||||
await click('.rose-notification-dismiss');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user