Better empty state copy

This commit is contained in:
Joshua Ogle 2018-11-03 11:21:49 -06:00 committed by Matthew Irish
parent 70a12dcde6
commit 1a5e6fdce9
16 changed files with 105 additions and 57 deletions

View file

@ -1,5 +1,6 @@
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
classNames: ['empty-state box is-sideless is-marginless is-fullwidth is-bottomless'] title: null,
classNames: ['empty-state box is-sideless is-marginless is-fullwidth is-bottomless'],
}); });

View file

@ -7,8 +7,12 @@ export default Component.extend({
items: null, items: null,
itemNoun: 'item', itemNoun: 'item',
emptyTitle: computed('itemNoun', function() {
let items = pluralize(this.get('itemNoun'));
return `No ${items} yet`;
}),
emptyMessage: computed('itemNoun', function() { emptyMessage: computed('itemNoun', function() {
let items = pluralize(this.get('itemNoun')); let items = pluralize(this.get('itemNoun'));
return `There are currently no ${items}`; return `Your ${items} will be listed here. Add your first ${this.get('itemNoun')} to get started.`;
}), })
}); });

View file

@ -5,7 +5,7 @@ import { assign } from '@ember/polyfills';
const DEFAULT_DISPLAY = { const DEFAULT_DISPLAY = {
searchPlaceholder: 'Filter secrets', searchPlaceholder: 'Filter secrets',
item: 'secret', item: 'secret',
create: 'Create secret', create: 'Add secret',
navigateTree: true, navigateTree: true,
editComponent: 'secret-edit', editComponent: 'secret-edit',
listItemPartial: 'partials/secret-list/item', listItemPartial: 'partials/secret-list/item',
@ -15,7 +15,7 @@ const SECRET_BACKENDS = {
displayName: 'AWS', displayName: 'AWS',
searchPlaceholder: 'Filter roles', searchPlaceholder: 'Filter roles',
item: 'role', item: 'role',
create: 'Create role', create: 'Add role',
navigateTree: false, navigateTree: false,
editComponent: 'role-aws-edit', editComponent: 'role-aws-edit',
listItemPartial: 'partials/secret-list/aws-role-item', listItemPartial: 'partials/secret-list/aws-role-item',
@ -30,7 +30,7 @@ const SECRET_BACKENDS = {
label: 'Roles', label: 'Roles',
searchPlaceholder: 'Filter roles', searchPlaceholder: 'Filter roles',
item: 'role', item: 'role',
create: 'Create role', create: 'Add role',
editComponent: 'role-pki-edit', editComponent: 'role-pki-edit',
}, },
{ {
@ -39,7 +39,7 @@ const SECRET_BACKENDS = {
label: 'Certificates', label: 'Certificates',
searchPlaceholder: 'Filter certificates', searchPlaceholder: 'Filter certificates',
item: 'certificates', item: 'certificates',
create: 'Create role', create: 'Add role',
tab: 'certs', tab: 'certs',
listItemPartial: 'partials/secret-list/pki-cert-item', listItemPartial: 'partials/secret-list/pki-cert-item',
editComponent: 'pki-cert-show', editComponent: 'pki-cert-show',
@ -50,7 +50,7 @@ const SECRET_BACKENDS = {
displayName: 'SSH', displayName: 'SSH',
searchPlaceholder: 'Filter roles', searchPlaceholder: 'Filter roles',
item: 'role', item: 'role',
create: 'Create role', create: 'Add role',
navigateTree: false, navigateTree: false,
editComponent: 'role-ssh-edit', editComponent: 'role-ssh-edit',
listItemPartial: 'partials/secret-list/ssh-role-item', listItemPartial: 'partials/secret-list/ssh-role-item',
@ -58,7 +58,7 @@ const SECRET_BACKENDS = {
transit: { transit: {
searchPlaceholder: 'Filter keys', searchPlaceholder: 'Filter keys',
item: 'key', item: 'key',
create: 'Create encryption key', create: 'Add encryption key',
navigateTree: false, navigateTree: false,
editComponent: 'transit-edit', editComponent: 'transit-edit',
listItemPartial: 'partials/secret-list/item', listItemPartial: 'partials/secret-list/item',

View file

@ -1,10 +1,10 @@
<div class="empty-state-content"> <div class="empty-state-content">
<h3 class="empty-state-title"> <h3 class="empty-state-title">
{{{title}}} {{title}}
</h3> </h3>
{{#if message}} {{#if message}}
<p class="empty-state-message"> <p class="empty-state-message">
{{{message}}} {{message}}
</p> </p>
{{/if}} {{/if}}
{{#if hasBlock}} {{#if hasBlock}}

View file

@ -20,7 +20,7 @@
<EmptyState <EmptyState
@title="No metadata for {{model.name}} yet" @title="No metadata for {{model.name}} yet"
@message="You can store custom data that you want to associate with a {{lowercase (humanize model.identityType)}}. Edit this {{lowercase (humanize model.identityType)}} to get started."> @message="You can store custom data that you want to associate with a {{lowercase (humanize model.identityType)}}. Edit this {{lowercase (humanize model.identityType)}} to get started.">
{{#link-to "vault.cluster.access.identity.aliases.edit" model.id tagName="button" class="link"}} {{#link-to "vault.cluster.access.identity.aliases.edit" model.id tagName="button" class="link"}}
Edit {{lowercase (humanize model.identityType)}} Edit {{lowercase (humanize model.identityType)}}
{{/link-to}} {{/link-to}}
<DocLink @href="https://learn.hashicorp.com/vault/identity-access-management/iam-identity"> <DocLink @href="https://learn.hashicorp.com/vault/identity-access-management/iam-identity">

View file

@ -29,6 +29,6 @@
{{/linked-block}} {{/linked-block}}
{{else}} {{else}}
<EmptyState <EmptyState
@title="There are no {{model.identityType}} aliases for {{model.name}}" @title="No {{model.identityType}} aliases for {{model.name}} yet"
/> />
{{/each}} {{/each}}

View file

@ -6,6 +6,7 @@
</div> </div>
{{else}} {{else}}
<EmptyState <EmptyState
@title={{emptyMessage}} @title={{emptyTitle}}
@message={{emptyMessage}}
/> />
{{/if}} {{/if}}

View file

@ -1,11 +1,20 @@
{{#if (and isV2 modelForData.destroyed)}} {{#if (and isV2 modelForData.destroyed)}}
<EmptyState <EmptyState
@title="Version {{modelForData.version}} of this secret has been permanently destroyed." @title="Version {{modelForData.version}} of this secret has been permanently destroyed"
/> @message="A version that has been permanently deleted cannot be restored. You can see other versions of this secret in the History menu.">
<DocLink @href="https://www.vaultproject.io/docs/secrets/kv/kv-v2.html">
Learn More
</DocLink>
</EmptyState>
{{else if (and isV2 modelForData.deleted)}} {{else if (and isV2 modelForData.deleted)}}
<EmptyState <EmptyState
@title="Version {{modelForData.version}} of this secret has been deleted." @title="Version {{modelForData.version}} of this secret has been deleted"
/> @message="A version that has been deleted but can be undeleted using the Version {{modelForData.version}} menu above.
You can also see other versions of this secret in the History menu.">
<DocLink @href="https://www.vaultproject.io/docs/secrets/kv/kv-v2.html">
Learn More
</DocLink>
</EmptyState>
{{else}} {{else}}
{{#if showAdvancedMode}} {{#if showAdvancedMode}}
{{json-editor {{json-editor

View file

@ -1,4 +1,4 @@
{{identity/entity-nav identityType=identityType}} {{identity/entity-nav identityType=identityType model=model}}
{{#if model.meta.total}} {{#if model.meta.total}}
{{#each model as |item|}} {{#each model as |item|}}
{{#linked-block {{#linked-block

View file

@ -10,32 +10,30 @@
{{model.message}} {{model.message}}
{{/unless}} {{/unless}}
<div class="box is-sideless is-fullwidth is-marginless"> {{#if (eq model.httpStatus 400)}}
{{#if (eq model.httpStatus 400)}} <AlertBanner
@type="danger"
@message="{{model.keyId}} is not a valid lease ID"
/>
{{else if (eq model.httpStatus 404)}}
<EmptyState
@title="No leases with that ID"
@message="Unable to find lease for the ID &quot;{{model.keyId}}&quot;. Try going back to the lookup and re-entering the ID."
>
{{#link-to "vault.cluster.access.leases" tagName="button" class="link"}}Back to lookup{{/link-to}}
</EmptyState>
{{else if (eq model.httpStatus 403)}}
<EmptyState
@title="You don't have access to a lease with that ID"
@message="If you think you've reached this page in error, please contact your administrator."
>
{{#link-to "vault.cluster.access.leases" tagName="button" class="link"}}Back to lookup{{/link-to}}
</EmptyState>
{{else}}
{{#each model.errors as |error|}}
<AlertBanner <AlertBanner
@type="danger" @type="danger"
@message="{{model.keyId}} is not a valid lease ID" @message={{error}}
/> />
{{else if (eq model.httpStatus 404)}} {{/each}}
<p class="message"> {{/if}}
Unable to find lease for the <code>id</code>: <code>{{model.keyId}}</code>. Try going back to the
{{#link-to "vault.cluster.access.leases"}}lookup{{/link-to}}
and re-entering the <code>id</code>.
</p>
{{else if (eq model.httpStatus 403)}}
<p class="message">
You don't have access to <code>{{model.keyId}}</code>. If you think you've reached this page in error, please contact your administrator.
</p>
{{else}}
{{#each model.errors as |error|}}
<p class="message">
{{error}}
</p>
{{/each}}
{{/if}}
</div>
<div class="field is-grouped box is-fullwidth is-bottomless">
{{#link-to "vault.cluster.access.leases" class="button"}}
Back
{{/link-to}}
</div>

View file

@ -7,7 +7,7 @@
</p.levelLeft> </p.levelLeft>
<p.levelRight> <p.levelRight>
{{#link-to 'vault.cluster.access.namespaces.create' class="button has-icon-right is-ghost is-compact"}} {{#link-to 'vault.cluster.access.namespaces.create' class="button has-icon-right is-ghost is-compact"}}
Create a namespace Add namespace
<ICon @glyph="chevron-right" @size=11 /> <ICon @glyph="chevron-right" @size=11 />
{{/link-to}} {{/link-to}}
</p.levelRight> </p.levelRight>

View file

@ -18,7 +18,7 @@
</div> </div>
{{else}} {{else}}
<EmptyState <EmptyState
@title="The secondary <code>{{model.config.id}}</code> does not currently have performance mount filtering configured" @title="Performance Mount Filtering is not configured for this secondary"
/> />
{{/if}} {{/if}}
<div class="field is-fullwidth box is-shadowless"> <div class="field is-fullwidth box is-shadowless">

View file

@ -48,7 +48,7 @@
{{else}} {{else}}
<EmptyState <EmptyState
@title="No known {{performanceMode}} secondary clusters associated with this cluster" @title="No known {{performanceMode}} secondary clusters associated with this cluster"
@message="A list of secondary clusters will be listed here. Add your first secondary cluster to get started."> @message="Associated secondary clusters will be listed here. Add your first secondary cluster to get started.">
{{#if model.canAddSecondary}} {{#if model.canAddSecondary}}
{{#link-to 'vault.cluster.replication.mode.secondaries.add' model.name replicationMode tagName="button" class="link" }} {{#link-to 'vault.cluster.replication.mode.secondaries.add' model.name replicationMode tagName="button" class="link" }}
Add secondary Add secondary

View file

@ -55,16 +55,34 @@
{{/if}} {{/if}}
{{else}} {{else}}
<div class="empty-state"> <div class="empty-state">
<div class="empty-state-title"> <div class="empty-state-content">
{{#if (eq baseKey.id '')}} {{#if (eq baseKey.id '')}}
There are currently no {{pluralize options.item}} in this backend. <div class="empty-state-title">
No {{pluralize options.item}} in this backend yet
</div>
<div class="empty-state-message">
Secrets in this backend will be listed here. Add a secret to get started.
</div>
<div class="empty-state-actions">
{{#secret-link
mode="create"
secret=''
queryParams=(query-params initialKey=(or filter baseKey.id))
class="link"
data-test-secret-create=true
}}
{{options.create}}
{{/secret-link}}
</div>
{{else}} {{else}}
{{#if filterIsFolder}} {{#if filterIsFolder}}
<div class="empty-state-title">
{{#if (eq filter baseKey.id)}} {{#if (eq filter baseKey.id)}}
There are no {{pluralize options.item}} under <code>{{or filter}}</code>. No {{pluralize options.item}} under <code>{{or filter}}</code>
{{else}} {{else}}
We couldn't find a folder matching <code>{{filter}}</code>. No folders matching <code>{{filter}}</code>
{{/if}} {{/if}}
</div>
{{/if}} {{/if}}
{{/if}} {{/if}}
</div> </div>

View file

@ -30,6 +30,6 @@
</div> </div>
{{else}} {{else}}
<EmptyState <EmptyState
@title="The token you are currently authenticated with does not have sufficient capabilities to seal this vault" @title="This token does not have sufficient capabilities to seal this vault"
/> />
{{/if}} {{/if}}

View file

@ -1,6 +1,6 @@
import { module, test } from 'qunit'; import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit'; import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers'; import { render, find } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile'; import hbs from 'htmlbars-inline-precompile';
module('Integration | Component | empty-state', function(hooks) { module('Integration | Component | empty-state', function(hooks) {
@ -16,11 +16,28 @@ module('Integration | Component | empty-state', function(hooks) {
// Template block usage: // Template block usage:
await render(hbs` await render(hbs`
{{#empty-state}} {{#empty-state
template block text title="Empty State Title"
message="This is the empty state message"
}}
Actions Link
{{/empty-state}} {{/empty-state}}
`); `);
assert.equal(this.element.textContent.trim(), 'template block text'); assert.equal(
find('.empty-state-title').textContent.trim(),
'Empty State Title',
'renders empty state title'
);
assert.equal(
find('.empty-state-message').textContent.trim(),
'This is the empty state message',
'renders empty state message'
);
assert.equal(
find('.empty-state-actions').textContent.trim(),
'Actions Link',
'renders empty state actions'
);
}); });
}); });