mirror of https://github.com/hashicorp/consul
ui: Notifications re-organization/re-style (#11577)
- Moves where they appear up to the <App /> component. - Instead of a <Notification /> wrapping component to move whatever you use for a notification up to where they need to appear (via ember-cli-flash), we now use a {{notification}} modifier now we have modifiers. - Global notifications/flashes are no longer special styles of their own. You just use the {{notification}} modifier to hoist whatever component/element you want up to the top of the page. This means we can re-use our existing <Notice /> component for all our global UI notifications (this is the user visible change here)pull/11697/head
parent
3820e09a47
commit
3f131dcf34
@ -0,0 +1,3 @@
|
||||
```release-note:improvement
|
||||
ui: Update global notification styling
|
||||
```
|
@ -0,0 +1,19 @@
|
||||
<div
|
||||
class="app-notification"
|
||||
...attributes
|
||||
{{style
|
||||
(array
|
||||
(array 'opacity' '1')
|
||||
(array 'transition-delay' (concat @delay 'ms'))
|
||||
)
|
||||
}}
|
||||
{{style
|
||||
(array
|
||||
(array 'opacity' (if @sticky '1' '0'))
|
||||
)
|
||||
delay=0
|
||||
}}
|
||||
>
|
||||
{{yield}}
|
||||
</div>
|
||||
|
@ -1,35 +0,0 @@
|
||||
---
|
||||
class: css
|
||||
state: needs-love
|
||||
---
|
||||
# flash-message
|
||||
|
||||
CSS component for styling our flash messages
|
||||
|
||||
```hbs preview-template
|
||||
<div class="flash-message">
|
||||
<p
|
||||
role="alert"
|
||||
class={{or this.type 'success'}}
|
||||
>
|
||||
<strong>
|
||||
{{capitalize (or this.type 'success')}}!
|
||||
</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<figure>
|
||||
<figcaption>Provide a widget to change the <code>class</code></figcaption>
|
||||
|
||||
<select
|
||||
onchange={{action (mut this.type) value="target.value"}}
|
||||
>
|
||||
<option>success</option>
|
||||
<option>warning</option>
|
||||
<option>error</option>
|
||||
<option>exists</option>
|
||||
</select>
|
||||
|
||||
</figure>
|
||||
```
|
||||
|
@ -1,19 +0,0 @@
|
||||
@import './skin';
|
||||
@import './layout';
|
||||
.flash-message {
|
||||
@extend %flash-message;
|
||||
}
|
||||
%flash-message.exiting {
|
||||
@extend %blink-in-fade-out;
|
||||
}
|
||||
/* This is for the flash message that appears */
|
||||
/* when you save an intention that already exists */
|
||||
/* once we have refactored app-view with data-source with nicer */
|
||||
/* flash message usage we should be able to remove this */
|
||||
%flash-message p.exists strong::before {
|
||||
@extend %with-cancel-square-fill-mask;
|
||||
color: rgb(var(--tone-red-500));
|
||||
}
|
||||
%flash-message p.exists {
|
||||
@extend %frame-red-500;
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
%flash-message {
|
||||
display: flex;
|
||||
position: relative;
|
||||
z-index: 6;
|
||||
justify-content: center;
|
||||
margin: 0 15%;
|
||||
}
|
||||
%flash-message p {
|
||||
top: -46px;
|
||||
position: absolute;
|
||||
padding: 9px 15px;
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
%flash-message p {
|
||||
border-width: 1px;
|
||||
border-radius: var(--decor-radius-100);
|
||||
}
|
||||
%flash-message p strong::before {
|
||||
@extend %as-pseudo;
|
||||
}
|
||||
%flash-message p.success strong::before {
|
||||
@extend %with-check-circle-fill-mask;
|
||||
color: rgb(var(--tone-green-500));
|
||||
}
|
||||
%flash-message p.warning strong::before {
|
||||
@extend %with-alert-triangle-mask;
|
||||
color: rgb(var(--tone-orange-500));
|
||||
}
|
||||
%flash-message p.error strong::before {
|
||||
@extend %with-cancel-square-fill-mask;
|
||||
color: rgb(var(--tone-red-500));
|
||||
}
|
||||
%flash-message p.success {
|
||||
@extend %frame-green-500;
|
||||
}
|
||||
%flash-message p.warning {
|
||||
@extend %frame-yellow-500;
|
||||
}
|
||||
%flash-message p.error {
|
||||
@extend %frame-red-500;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
<div id={{guid}}>
|
||||
{{yield}}
|
||||
</div>
|
@ -1,40 +0,0 @@
|
||||
import Component from '@ember/component';
|
||||
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
notify: service('flashMessages'),
|
||||
dom: service('dom'),
|
||||
oncomplete: function() {},
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
this.guid = this.dom.guid(this);
|
||||
},
|
||||
didInsertElement: function() {
|
||||
const $el = this.dom.element(`#${this.guid}`);
|
||||
const options = {
|
||||
timeout: 6000,
|
||||
extendedTimeout: 300,
|
||||
dom: $el.innerHTML,
|
||||
};
|
||||
if (this.sticky) {
|
||||
options.sticky = true;
|
||||
}
|
||||
$el.remove();
|
||||
this.notify.clearMessages();
|
||||
if (typeof this.after === 'function') {
|
||||
Promise.resolve(this.after())
|
||||
.catch(e => {
|
||||
if (e.name !== 'TransitionAborted') {
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
this.notify.add(options);
|
||||
});
|
||||
} else {
|
||||
this.notify.add(options);
|
||||
}
|
||||
},
|
||||
});
|
@ -0,0 +1,37 @@
|
||||
import Modifier from 'ember-modifier';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class NotificationModifier extends Modifier {
|
||||
@service('flashMessages') notify;
|
||||
|
||||
didInstall() {
|
||||
this.element.setAttribute('role', 'alert');
|
||||
this.element.dataset['notification'] = null;
|
||||
const options = {
|
||||
timeout: 6000,
|
||||
extendedTimeout: 300,
|
||||
...this.args.named,
|
||||
};
|
||||
options.dom = this.element.outerHTML;
|
||||
this.element.remove();
|
||||
this.notify.clearMessages();
|
||||
if (typeof options.after === 'function') {
|
||||
Promise.resolve().then(_ => options.after())
|
||||
.catch(e => {
|
||||
if (e.name !== 'TransitionAborted') {
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
.then(res => {
|
||||
this.notify.add(options);
|
||||
});
|
||||
} else {
|
||||
this.notify.add(options);
|
||||
}
|
||||
}
|
||||
willDestroy() {
|
||||
if(this.args.named.sticky) {
|
||||
this.notify.clearMessages();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
# notification
|
||||
|
||||
Consul UIs notification modifier is used to 'hoist' DOM elements into the
|
||||
global notification area for the UI. The most common usage will be something
|
||||
like the below:
|
||||
|
||||
```hbs preview-template
|
||||
<figure>
|
||||
<figcaption>Attach a Warning notice to the top of the app ^^^</figcaption>
|
||||
|
||||
<Notice
|
||||
class="notification-update"
|
||||
@type="warning"
|
||||
{{notification
|
||||
sticky=true
|
||||
}}
|
||||
as |notice|>
|
||||
<notice.Header>
|
||||
<strong>Warning!</strong>
|
||||
</notice.Header>
|
||||
<notice.Body>
|
||||
<p>
|
||||
This service has been deregistered and no longer exists in the catalog.
|
||||
</p>
|
||||
</notice.Body>
|
||||
</Notice>
|
||||
|
||||
</figure>
|
||||
```
|
||||
|
||||
Currently this is backed by `ember-cli-flash` and the named options are
|
||||
currently options that can be accepted by `ember-cli-flash`. Be aware that this
|
||||
is likely to change in the future.
|
||||
|
@ -0,0 +1,52 @@
|
||||
import Modifier from 'ember-modifier';
|
||||
import { assert } from '@ember/debug';
|
||||
|
||||
export default class StyleModifier extends Modifier {
|
||||
setStyles(newStyles = []) {
|
||||
const rulesToRemove = this._oldStyles || new Set();
|
||||
if(!Array.isArray(newStyles)) {
|
||||
newStyles = Object.entries(newStyles)
|
||||
}
|
||||
newStyles.forEach(([property, value]) => {
|
||||
assert(
|
||||
`Your given value for property '${property}' is ${value} (${typeof value}). Accepted types are string and undefined. Please change accordingly.`,
|
||||
typeof value === 'undefined' || typeof value === 'string'
|
||||
);
|
||||
|
||||
// priority must be specified as separate argument
|
||||
// value must not contain "!important"
|
||||
let priority = '';
|
||||
if (value.length > 0 && value.includes('!important')) {
|
||||
priority = 'important';
|
||||
value = value.replace('!important', '');
|
||||
}
|
||||
|
||||
// update CSSOM
|
||||
this.element.style.setProperty(property, value, priority);
|
||||
|
||||
// should not remove rules that have been updated in this cycle
|
||||
rulesToRemove.delete(property);
|
||||
});
|
||||
|
||||
// remove rules that were present in last cycle but aren't present in this one
|
||||
rulesToRemove.forEach((rule) => this.element.style.removeProperty(rule));
|
||||
|
||||
// cache styles that in this rendering cycle for the next one
|
||||
this._oldStyles = new Set(newStyles.map((e) => e[0]));
|
||||
}
|
||||
|
||||
didReceiveArguments() {
|
||||
if(typeof this.args.named.delay !== 'undefined') {
|
||||
setTimeout(
|
||||
_ => {
|
||||
if(typeof this !== this.args.positional[0]) {
|
||||
this.setStyles(this.args.positional[0]);
|
||||
}
|
||||
},
|
||||
this.args.named.delay
|
||||
)
|
||||
} else {
|
||||
this.setStyles(this.args.positional[0]);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue