Skip to content

Commit

Permalink
ui: New Empty States (#7940)
Browse files Browse the repository at this point in the history
* ui: CSS and component changes to the <EmptyState /> component

* ui: Reset the auth-form component back to its initial state

Moving forwards we are going to have the auth-form on the page all the
time, even when logged in (for relogging in purposes). This means the
auth-form will not always be removed from the DOM when you log in.

This sets the form back to its idle state before calling onsubmit

* ui: Make a public api for modal-dialog with a single close method

* ui : Move cache reset somewhere that makes more sense, + single refresh

1. Centralize cache resetting elsewhere, for now the store makes most
sense, although I would prefer the Repository class, so using the store
is temporary
2. We only need to refresh on login once, unless we have a differing
nspace

* ui: Ensure visibilitychange events are cleaned up

* ui: Only cache DataSource data if we have any, + only clear the cache

* ui: Add the modal login dialog to both unauth and auth views

This means we can 'relogin' when already logged in

* ui: Add new empty states

* ui: CSS Tweaks

* Remove marketing grays
  • Loading branch information
johncowen committed May 27, 2020
1 parent 137e5e1 commit a947b06
Show file tree
Hide file tree
Showing 23 changed files with 297 additions and 94 deletions.
2 changes: 1 addition & 1 deletion ui-v2/app/components/auth-form/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
@nspace={{or value.Namespace nspace}}
@type={{if value.Name 'oidc' 'secret'}}
@value={{if value.Name value.Name value}}
@onchange={{action onsubmit}}
@onchange={{queue (action dispatch "RESET") (action onsubmit)}}
@onerror={{queue (action (mut error) value="error.errors.firstObject") (action dispatch "ERROR")}}
/>
</State>
Expand Down
21 changes: 17 additions & 4 deletions ui-v2/app/components/empty-state/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,24 @@
{{yield}}
{{/yield-slot}}
</header>
<p>
{{#yield-slot name="body"}}
{{#yield-slot name="body"}}
<div>
{{yield}}
{{/yield-slot}}
</p>
{{#if (and (env 'CONSUL_ACLS_ENABLED') allowLogin)}}
<label for="login-toggle">
<DataSource
@src="settings://consul:token"
@onchange={{action (mut token) value="data"}}
/>
{{#if token.AccessorID}}
Log in with a different token
{{else}}
Log in
{{/if}}
</label>
{{/if}}
</div>
{{/yield-slot}}
{{#yield-slot name="actions"}}
<ul>
{{yield}}
Expand Down
21 changes: 19 additions & 2 deletions ui-v2/app/components/hashicorp-consul/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,15 @@
<AuthDialog
@dc={{dc.Name}}
@nspace={{nspace.Name}}
@onchange={{action onchange}} as |authDialog components|
@onchange={{action "reauthorize"}} as |authDialog components|
>
{{#let components.AuthForm components.AuthProfile as |AuthForm AuthProfile|}}
<BlockSlot @name="unauthorized">
<label tabindex="0" for="login-toggle" onkeypress={{action 'keypressClick'}}>
<span>Log in</span>
</label>
<ModalDialog @name="login-toggle" @onclose={{action 'close'}} @onopen={{action 'open'}}>
<ModalDialog @name="login-toggle" @onclose={{action 'close'}} @onopen={{action 'open'}} as |api|>
<Ref @target={{this}} @name="modal" @value={{api}} />
<BlockSlot @name="header">
<h2>Log in to Consul</h2>
</BlockSlot>
Expand All @@ -151,6 +152,22 @@
</ModalDialog>
</BlockSlot>
<BlockSlot @name="authorized">
<ModalDialog @name="login-toggle" @onclose={{action 'close'}} @onopen={{action 'open'}} as |api|>
<Ref @target={{this}} @name="modal" @value={{api}} />
<BlockSlot @name="header">
<h2>Log in with a different token</h2>
</BlockSlot>
<BlockSlot @name="body">
<AuthForm as |api|>
<Ref @target={{this}} @name="authForm" @value={{api}} />
</AuthForm>
</BlockSlot>
<BlockSlot @name="actions" as |close|>
<button type="button" onclick={{action close}}>
Continue without logging in
</button>
</BlockSlot>
</ModalDialog>
<PopoverMenu @position="right">
<BlockSlot @name="trigger">
Logout
Expand Down
4 changes: 4 additions & 0 deletions ui-v2/app/components/hashicorp-consul/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export default Component.extend({
close: function() {
this.authForm.reset();
},
reauthorize: function(e) {
this.modal.close();
this.onchange(e);
},
change: function(e) {
const win = this.dom.viewport();
const $root = this.dom.root();
Expand Down
18 changes: 15 additions & 3 deletions ui-v2/app/components/modal-dialog/index.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,25 @@
<div>
<header>
<label for="modal_close">Close</label>
<YieldSlot @name="header">{{yield}}</YieldSlot>
<YieldSlot @name="header">
{{yield (hash
close=(action "close")
)}}
</YieldSlot>
</header>
<div>
<YieldSlot @name="body">{{yield}}</YieldSlot>
<YieldSlot @name="body">
{{yield (hash
close=(action "close")
)}}
</YieldSlot>
</div>
<footer>
<YieldSlot @name="actions" @params={{block-params (action "close")}}>{{yield}}</YieldSlot>
<YieldSlot @name="actions" @params={{block-params (action "close")}}>
{{yield (hash
close=(action "close")
)}}
</YieldSlot>
</footer>
</div>
</div>
Expand Down
34 changes: 16 additions & 18 deletions ui-v2/app/controllers/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ import transitionable from 'consul-ui/utils/routing/transitionable';

export default Controller.extend({
router: service('router'),
http: service('repository/type/event-source'),
dataSource: service('data-source/service'),
client: service('client/http'),
store: service('store'),
feedback: service('feedback'),
actions: {
Expand All @@ -23,12 +20,11 @@ export default Controller.extend({
// used for the feedback service.
this.feedback.execute(
() => {
// TODO: Centralize this elsewhere
this.client.abort();
this.http.resetCache();
this.dataSource.resetCache();
this.store.init();

// TODO: Currently we clear cache from the ember-data store
// ideally this would be a static method of the abstract Repository class
// once we move to proper classes for services take another look at this.
this.store.clear();
//
const params = {};
if (e.data) {
const token = e.data;
Expand All @@ -42,22 +38,24 @@ export default Controller.extend({
}
}
}
const container = getOwner(this);
const routeName = this.router.currentRoute.name;
const route = getOwner(this).lookup(`route:${routeName}`);
const router = this.router;
const route = container.lookup(`route:${routeName}`);
// Refresh the application route
return getOwner(this)
return container
.lookup('route:application')
.refresh()
.promise.then(() => {
// We use transitionable here as refresh doesn't work if you are on an error page
// which is highly likely to happen here (403s)
if (routeName !== router.currentRouteName || typeof params.nspace !== 'undefined') {
.promise.then(res => {
// Use transitionable if we need to change a section of the URL
if (
routeName !== this.router.currentRouteName ||
typeof params.nspace !== 'undefined'
) {
return route.transitionTo(
...transitionable(router.currentRoute, params, getOwner(this))
...transitionable(this.router.currentRoute, params, container)
);
} else {
return route.refresh();
return res;
}
});
},
Expand Down
32 changes: 22 additions & 10 deletions ui-v2/app/services/client/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,19 +69,27 @@ export default Service.extend({
settings: service('settings'),
init: function() {
this._super(...arguments);
this._listeners = this.dom.listeners();
const maxConnections = env('CONSUL_HTTP_MAX_CONNECTIONS');
set(this, 'connections', getObjectPool(dispose, maxConnections));
if (typeof maxConnections !== 'undefined') {
set(this, 'maxConnections', maxConnections);
const doc = this.dom.document();
// when the user hides the tab, abort all connections
doc.addEventListener('visibilitychange', e => {
if (e.target.hidden) {
this.connections.purge();
}
this._listeners.add(this.dom.document(), {
visibilitychange: e => {
if (e.target.hidden) {
this.connections.purge();
}
},
});
}
},
willDestroy: function() {
this._listeners.remove();
this.connections.purge();
set(this, 'connections', undefined);
this._super(...arguments);
},
url: function() {
return url(...arguments);
},
Expand Down Expand Up @@ -235,14 +243,18 @@ export default Service.extend({
this.connections.purge();
},
whenAvailable: function(e) {
const doc = this.dom.document();
// if we are using a connection limited protocol and the user has hidden the tab (hidden browser/tab switch)
// any aborted errors should restart
const doc = this.dom.document();
if (typeof this.maxConnections !== 'undefined' && doc.hidden) {
return new Promise(function(resolve) {
doc.addEventListener('visibilitychange', function listen(event) {
doc.removeEventListener('visibilitychange', listen);
resolve(e);
return new Promise(resolve => {
const remove = this._listeners.add(doc, {
visibilitychange: function(event) {
remove();
// we resolve with the event that comes from
// whenAvailable not visibilitychange
resolve(e);
},
});
});
}
Expand Down
26 changes: 13 additions & 13 deletions ui-v2/app/services/data-source/service.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,17 @@ export default Service.extend({

init: function() {
this._super(...arguments);
if (cache === null) {
this.resetCache();
}
cache = new Map();
sources = new Map();
usage = new MultiMap(Set);
this._listeners = this.dom.listeners();
},
resetCache: function() {
Object.entries(sources || {}).forEach(function([key, item]) {
item.close();
});
cache = new Map();
sources = new Map();
usage = new MultiMap(Set);
},
willDestroy: function() {
this._listeners.remove();
Object.entries(sources || {}).forEach(function([key, item]) {
sources.forEach(function(item) {
item.close();
});
cache = null;
Expand Down Expand Up @@ -61,10 +56,15 @@ export default Service.extend({
close: e => {
const source = e.target;
source.removeEventListener('close', close);
cache.set(uri, {
currentEvent: source.getCurrentEvent(),
cursor: source.configuration.cursor,
});
const event = source.getCurrentEvent();
const cursor = source.configuration.cursor;
// only cache data if we have any
if (typeof event !== 'undefined' && typeof cursor !== 'undefined') {
cache.set(uri, {
currentEvent: source.getCurrentEvent(),
cursor: source.configuration.cursor,
});
}
// the data is cached delete the EventSource
sources.delete(uri);
},
Expand Down
15 changes: 15 additions & 0 deletions ui-v2/app/services/store.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
import Store from 'ember-data/store';
import { inject as service } from '@ember/service';

export default Store.extend({
// TODO: This should eventually go on a static method
// of the abstract Repository class
http: service('repository/type/event-source'),
dataSource: service('data-source/service'),
client: service('client/http'),
clear: function() {
// Aborting the client will close all open http type sources
this.client.abort();
// once they are closed clear their caches
this.http.resetCache();
this.dataSource.resetCache();
this.init();
},
//
// TODO: These only exist for ACLs, should probably make sure they fail
// nicely if you aren't on ACLs for good DX
// cloning immediately refreshes the view
Expand Down
11 changes: 1 addition & 10 deletions ui-v2/app/styles/base/color/base-variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,7 @@ $cyan-600: #009fd9;
$cyan-700: #0077a3;
$cyan-800: #005574;
$cyan-900: #003346;
$gray-1: #191a1c;
$gray-2: #323538;
$gray-3: #4c4f54;
$gray-4: #656a70;
$gray-5: #7f858d;
$gray-6: #9a9ea5;
$gray-7: #b4b8bc;
$gray-8: #d0d2d5;
$gray-9: #ebecee;
$gray-10: #f3f4f6;
$gray-010: #fbfbfc;
$gray-050: #f7f8fa;
$gray-100: #ebeef2;
$gray-200: #dce0e6;
Expand Down
3 changes: 3 additions & 0 deletions ui-v2/app/styles/components/empty-state/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
%empty-state > ul > li {
@extend %with-popover-menu;
}
%empty-state label {
@extend %primary-button;
}
15 changes: 14 additions & 1 deletion ui-v2/app/styles/components/empty-state/layout.scss
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
%empty-state,
%empty-state > div {
display: flex;
flex-direction: column;
}
%empty-state-header {
padding: 0;
margin: 0;
}
%empty-state {
width: 320px;
margin-top: 0 !important;
padding-bottom: 2.8em;
}
%empty-state > * {
width: 370px;
margin: 0 auto;
}
%empty-state label {
margin: 0 auto !important;
}
%empty-state-header {
margin-bottom: -3px;
}
%empty-state header {
margin-top: 1.8em;
margin-bottom: 0.5em;
}
%empty-state > ul {
Expand Down
17 changes: 11 additions & 6 deletions ui-v2/app/styles/components/empty-state/skin.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
%empty-state {
color: $gray-500;
background-color: $gray-010;
}
%empty-state > ul {
border-color: $gray-300;
Expand Down Expand Up @@ -34,12 +35,16 @@
%empty-state[class*='status-5'] header::before {
@extend %with-alert-circle-outline-mask;
}
%empty-state .docs-link > *::before {
@extend %with-docs-mask, %as-pseudo;
%empty-state li[class*='-link'] > *::after {
@extend %as-pseudo;
margin-left: 5px;
}
%empty-state .docs-link > *::after {
@extend %with-docs-mask;
}
%empty-state .back-link > *::before {
@extend %with-chevron-left-mask, %as-pseudo;
%empty-state .back-link > *::after {
@extend %with-chevron-left-mask;
}
%empty-state .learn-link > *::before {
@extend %with-learn-mask, %as-pseudo;
%empty-state .learn-link > *::after {
@extend %with-learn-mask;
}
2 changes: 1 addition & 1 deletion ui-v2/app/templates/dc/acls/-authorization.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<EmptyState class="status-403">
<EmptyState class="status-403" @allowLogin={{true}}>
<BlockSlot @name="header">
<h2>You are not authorized</h2>
</BlockSlot>
Expand Down

0 comments on commit a947b06

Please sign in to comment.