Merge pull request #3305 from hashicorp/f-ui-empty-states

UI empty states
This commit is contained in:
Michael Lange
2017-10-03 10:08:08 -07:00
committed by GitHub
11 changed files with 182 additions and 11 deletions

View File

@@ -12,7 +12,7 @@ export default Component.extend({
// Used to throttle sets to searchTerm
debounce: 150,
classNames: ['field', 'has-addons'],
classNames: ['search-box', 'field', 'has-addons'],
actions: {
setSearchTerm(e) {

View File

@@ -0,0 +1,12 @@
export function initialize() {
const application = arguments[1] || arguments[0];
// Provides the acl token service to all templates
application.inject('controller', 'token', 'service:token');
application.inject('component', 'token', 'service:token');
}
export default {
name: 'app-token',
initialize,
};

View File

@@ -1,6 +1,7 @@
@import "./components/badge";
@import "./components/boxed-section";
@import "./components/breadcrumbs";
@import "./components/empty-message";
@import "./components/gutter";
@import "./components/inline-definitions";
@import "./components/job-diff";

View File

@@ -0,0 +1,21 @@
.empty-message {
padding: 1.5rem;
background: $white-ter;
border-radius: $radius;
.empty-message-headline {
font-size: $size-3;
color: $grey;
text-align: center;
}
.empty-message-body {
padding: 0 20%;
text-align: center;
color: $grey;
strong {
color: $grey;
}
}
}

View File

@@ -3,9 +3,11 @@
{{/global-header}}
{{#gutter-menu class="page-body"}}
<section class="section">
<div class="content">
<div>{{search-box searchTerm=(mut searchTerm) placeholder="Search jobs..."}}</div>
</div>
{{#if model.length}}
<div class="content">
<div>{{search-box searchTerm=(mut searchTerm) placeholder="Search jobs..."}}</div>
</div>
{{/if}}
{{#list-pagination
source=sortedJobs
size=pageSize
@@ -37,6 +39,18 @@
<ul class="pagination-list"></ul>
</nav>
</div>
{{else}}
<div class="empty-message">
{{#if (eq model.length 0)}}
<h3 class="empty-message-headline">No Jobs</h3>
<p class="empty-message-body">
There are currently no visible jobs in the cluster. It could be that the cluster is empty. It could also mean {{#link-to "settings.tokens"}}you don't have access to see any jobs{{/link-to}}.
</p>
{{else if searchTerm}}
<h3 class="empty-message-headline">No Matches</h3>
<p class="empty-message-body">No jobs match the term <strong>{{searchTerm}}</strong></p>
{{/if}}
</div>
{{/list-pagination}}
</section>
{{/gutter-menu}}

View File

@@ -90,6 +90,13 @@
{{#p.last class="pagination-link"}} &gt;| {{/p.last}}
</ul>
</nav>
{{else}}
<div class="boxed-section-body">
<div class="empty-message">
<h3 class="empty-message-headline">No Matches</h3>
<p class="empty-message-body">No allocations match the term <strong>{{searchTerm}}</strong></p>
</div>
</div>
{{/list-pagination}}
</div>
</div>

View File

@@ -3,9 +3,11 @@
{{/global-header}}
{{#gutter-menu class="page-body"}}
<section class="section">
<div class="content">
<div>{{search-box searchTerm=(mut searchTerm) placeholder="Search nodes..."}}</div>
</div>
{{#if nodes.length}}
<div class="content">
<div>{{search-box searchTerm=(mut searchTerm) placeholder="Search nodes..."}}</div>
</div>
{{/if}}
{{#list-pagination
source=sortedNodes
size=pageSize
@@ -38,6 +40,18 @@
<ul class="pagination-list"></ul>
</nav>
</div>
{{else}}
<div class="empty-message">
{{#if (eq nodes.length 0)}}
<h3 class="empty-message-headline">No Clients</h3>
<p class="empty-message-body">
There are currently no visible nodes in the cluster. This could mean that the cluster is bootstrapped with no clients. It could also mean {{#link-to "settings.tokens"}}you don't have access to see any clients{{/link-to}}.
</p>
{{else if searchTerm}}
<h3 class="empty-message-headline">No Matches</h3>
<p class="empty-message-body">No clients match the term <strong>{{searchTerm}}</strong></p>
{{/if}}
</div>
{{/list-pagination}}
</section>
{{/gutter-menu}}

View File

@@ -1,6 +1,6 @@
<div class="page-layout">
{{#global-header class="page-header"}}
Nodes
Servers
{{/global-header}}
{{#gutter-menu class="page-body"}}
<section class="section">
@@ -35,6 +35,17 @@
<ul class="pagination-list"></ul>
</nav>
</div>
{{else}}
<div class="empty-message">
<h3 class="empty-message-headline">Invalid Permissions</h3>
<p class="empty-message-body">
{{#if token.secret}}
Your ACL token does not grant access to see servers.
{{else}}
You have no ACL token set. {{#link-to "settings.tokens"}}Provide a token{{/link-to}} with the appropriate permissions to see servers.
{{/if}}
</p>
</div>
{{/list-pagination}}
{{outlet}}
</section>

View File

@@ -1,5 +1,5 @@
import Ember from 'ember';
import { click, findAll, currentURL, visit } from 'ember-native-dom-helpers';
import { click, find, findAll, currentURL, visit, fillIn } from 'ember-native-dom-helpers';
import { test } from 'qunit';
import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance';
@@ -80,3 +80,30 @@ test('each job row should link to the corresponding job', function(assert) {
assert.equal(currentURL(), `/jobs/${job.id}`);
});
});
test('when there are no jobs, there is an empty message', function(assert) {
visit('/jobs');
andThen(() => {
assert.ok(find('.empty-message'));
assert.equal(find('.empty-message-headline').textContent, 'No Jobs');
});
});
test('when there are jobs, but no matches for a search result, there is an empty message', function(
assert
) {
server.create('job', { name: 'cat 1' });
server.create('job', { name: 'cat 2' });
visit('/jobs');
andThen(() => {
fillIn('.search-box input', 'dog');
});
andThen(() => {
assert.ok(find('.empty-message'));
assert.equal(find('.empty-message-headline').textContent, 'No Matches');
});
});

View File

@@ -1,5 +1,5 @@
import Ember from 'ember';
import { click, findAll, currentURL, visit } from 'ember-native-dom-helpers';
import { click, find, findAll, currentURL, visit } from 'ember-native-dom-helpers';
import { test } from 'qunit';
import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance';
import { findLeader } from '../../mirage/config';
@@ -75,6 +75,35 @@ test('each client should link to the client detail page', function(assert) {
});
});
test('when there are no clients, there is an empty message', function(assert) {
server.createList('agent', 1);
visit('/nodes');
andThen(() => {
assert.ok(find('.empty-message'));
assert.equal(find('.empty-message-headline').textContent, 'No Clients');
});
});
test('when there are clients, but no matches for a search term, there is an empty message', function(
assert
) {
server.createList('agent', 1);
server.create('node', { name: 'node' });
visit('/nodes');
andThen(() => {
fillIn('.search-box input', 'client');
});
andThen(() => {
assert.ok(find('.empty-message'));
assert.equal(find('.empty-message-headline').textContent, 'No Matches');
});
});
test('/servers should list all servers', function(assert) {
const agentsCount = 10;
const pageSize = 8;
@@ -141,3 +170,24 @@ test('each server should link to the server detail page', function(assert) {
assert.equal(currentURL(), `/servers/${agent.name}`);
});
});
test('when the API returns no agents, show an empty message', function(assert) {
minimumSetup();
// Override the members handler to act as if server-side permissions
// are preventing a qualified response.
server.pretender.get('/v1/agent/members', () => [
200,
{},
JSON.stringify({
Members: [],
}),
]);
visit('/servers');
andThen(() => {
assert.ok(find('.empty-message'));
assert.equal(find('.empty-message-headline').textContent, 'Invalid Permissions');
});
});

View File

@@ -1,5 +1,5 @@
import Ember from 'ember';
import { click, findAll, currentURL, visit } from 'ember-native-dom-helpers';
import { click, find, findAll, fillIn, currentURL, visit } from 'ember-native-dom-helpers';
import { test } from 'qunit';
import moduleForAcceptance from 'nomad-ui/tests/helpers/module-for-acceptance';
@@ -41,6 +41,11 @@ moduleForAcceptance('Acceptance | task group detail', {
taskGroup: taskGroups[1].name,
});
// Set a static name to make the search test deterministic
server.db.allocations.forEach(alloc => {
alloc.name = 'aaaaa';
});
visit(`/jobs/${job.id}/${taskGroup.name}`);
},
});
@@ -212,3 +217,12 @@ test('each allocation should show stats about the allocation, retrieved directly
`Requests ${nodeStatsUrl}`
);
});
test('when the allocation search has no matches, there is an empty message', function(assert) {
fillIn('.search-box input', 'zzzzzz');
andThen(() => {
assert.ok(find('.allocations .empty-message'));
assert.equal(find('.allocations .empty-message-headline').textContent, 'No Matches');
});
});