UI: [VAULT-19560] Add empty states and tests (#22690)

This commit is contained in:
Kianna
2023-08-31 12:12:20 -07:00
committed by GitHub
parent bbffe93bbb
commit 1dfd57e26d
6 changed files with 192 additions and 136 deletions

View File

@@ -101,13 +101,13 @@ export default class DashboardQuickActionsCard extends Component {
}
get filteredSecretEngines() {
return this.args.secretsEngines.filter(
return this.args.secretsEngines?.filter(
(engine) => (engine.type === 'kv' && engine.version == 2) || QUICK_ACTION_ENGINES.includes(engine.type)
);
}
get mountOptions() {
return this.filteredSecretEngines.map((engine) => {
return this.filteredSecretEngines?.map((engine) => {
const { id, type } = engine;
return { name: id, type, id };

View File

@@ -18,14 +18,10 @@ import Component from '@glimmer/component';
export default class DashboardSecretsEnginesCard extends Component {
get filteredSecretsEngines() {
const filteredEngines = this.args.secretsEngines.filter(
(secretEngine) => secretEngine.shouldIncludeInList
);
return filteredEngines;
return this.args.secretsEngines?.filter((secretEngine) => secretEngine.shouldIncludeInList);
}
get firstFiveSecretsEngines() {
return this.filteredSecretsEngines.slice(0, 5);
return this.filteredSecretsEngines?.slice(0, 5);
}
}

View File

@@ -1,74 +1,87 @@
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l" data-test-card="quick-actions">
<h3 class="title is-4">Quick actions</h3>
<div class="has-top-margin-m has-bottom-margin-m">
<h4 class="title is-marginless is-6">Secrets engines</h4>
<p class="is-size-8 has-top-margin-xxs has-bottom-margin-s has-text-grey">Supported engines include databases, KV version
2, and PKI.</p>
<SearchSelect
@id="secrets-engines-select"
@options={{this.mountOptions}}
@selectLimit="1"
@disallowNewItems={{true}}
@fallbackComponent="input-search"
@onChange={{this.handleSearchEngineSelect}}
@placeholder="Type to select a mount"
@displayInherit={{true}}
@shouldRenderName={{true}}
@passObject={{true}}
@objectKeys={{array "type" "version"}}
class="is-marginless"
data-test-secrets-engines-select
/>
</div>
{{#if this.selectedEngine}}
<h4 class="title is-6">Action</h4>
<Select
@name="action-select"
@options={{this.actionOptions}}
@isFullwidth={{true}}
@selectedValue={{this.selectedAction}}
@onChange={{this.setSelectedAction}}
@noDefault={{true}}
/>
{{#if this.searchSelectParams.model}}
<h4 class="title is-6" data-test-search-select-params-title>{{this.searchSelectParams.title}}</h4>
{{#if this.filteredSecretEngines}}
<div class="has-top-margin-m has-bottom-margin-m">
<h4 class="title is-marginless is-6">Secrets engines</h4>
<p class="is-size-8 has-top-margin-xxs has-bottom-margin-s has-text-grey">Supported engines include databases, KV
version 2, and PKI.</p>
<SearchSelect
class="is-flex-grow-1"
@id="secrets-engines-select"
@options={{this.mountOptions}}
@selectLimit="1"
@models={{array this.searchSelectParams.model}}
@placeholder={{this.searchSelectParams.placeholder}}
@disallowNewItems={{true}}
@onChange={{this.handleActionSelect}}
@fallbackComponent="input-search"
@disabled={{not this.searchSelectParams.model}}
@nameKey={{this.searchSelectParams.nameKey}}
@queryObject={{this.searchSelectParams.queryObject}}
@objectKeys={{this.searchSelectParams.objectKeys}}
@onChange={{this.handleSearchEngineSelect}}
@placeholder="Type to select a mount"
@displayInherit={{true}}
@shouldRenderName={{true}}
@passObject={{true}}
@shouldRenderName={{this.searchSelectParams.nameKey}}
data-test-param-select
@objectKeys={{array "type" "version"}}
class="is-marginless"
data-test-secrets-engines-select
/>
</div>
{{#if this.selectedEngine}}
<h4 class="title is-6">Action</h4>
<Select
@name="action-select"
@options={{this.actionOptions}}
@isFullwidth={{true}}
@selectedValue={{this.selectedAction}}
@onChange={{this.setSelectedAction}}
@noDefault={{true}}
/>
<div>
<button
type="button"
class="button is-primary has-top-margin-m"
disabled={{(not (and this.selectedAction this.selectedEngine this.paramValue))}}
{{on "click" this.navigateToPage}}
data-test-button={{this.searchSelectParams.buttonText}}
>
{{this.searchSelectParams.buttonText}}
</button>
</div>
{{#if this.searchSelectParams.model}}
<h4 class="title is-6" data-test-search-select-params-title>{{this.searchSelectParams.title}}</h4>
<SearchSelect
class="is-flex-grow-1"
@selectLimit="1"
@models={{array this.searchSelectParams.model}}
@placeholder={{this.searchSelectParams.placeholder}}
@disallowNewItems={{true}}
@onChange={{this.handleActionSelect}}
@fallbackComponent="input-search"
@disabled={{not this.searchSelectParams.model}}
@nameKey={{this.searchSelectParams.nameKey}}
@queryObject={{this.searchSelectParams.queryObject}}
@objectKeys={{this.searchSelectParams.objectKeys}}
@passObject={{true}}
@shouldRenderName={{this.searchSelectParams.nameKey}}
data-test-param-select
/>
<div>
<button
type="button"
class="button is-primary has-top-margin-m"
disabled={{(not (and this.selectedAction this.selectedEngine this.paramValue))}}
{{on "click" this.navigateToPage}}
data-test-button={{this.searchSelectParams.buttonText}}
>
{{this.searchSelectParams.buttonText}}
</button>
</div>
{{/if}}
{{else}}
<EmptyState
@title="No mount selected"
@message="Select a mount above to get started."
data-test-no-mount-selected-empty
/>
{{/if}}
{{else}}
<EmptyState
@title="No mount selected"
@message="Select a mount above to get started."
data-test-no-mount-selected-empty
/>
@title="Welcome to quick actions"
@message="Access secret engine actions easily. Enable a compatible secret engine (such as database, KV version 2, or PKI) to get started."
data-test-empty-state="quick-actions"
>
<div>
<LinkTo @route="vault.cluster.settings.mount-secret-backend">Enable a secret engine</LinkTo>
</div>
</EmptyState>
{{/if}}
</Hds::Card::Container>

View File

@@ -1,80 +1,100 @@
<Hds::Card::Container @hasBorder={{true}} class="has-padding-l secrets-engines-card" data-test-card="secrets-engines">
<Hds::Card::Container
@hasBorder={{true}}
class="has-padding-l {{if this.filteredSecretsEngines 'secrets-engines-card'}}"
data-test-card="secrets-engines"
>
<div class="is-flex-between">
<h3 class="title is-4 has-left-margin-xxs" data-test-dashboard-secrets-engines-header>Secrets engines</h3>
<LinkTo class="is-no-underline has-right-margin-xxs" @route="vault.cluster.secrets.backends">
Details
</LinkTo>
{{#if this.filteredSecretsEngines}}
<LinkTo class="is-no-underline has-right-margin-xxs" @route="vault.cluster.secrets.backends">
Details
</LinkTo>
{{/if}}
</div>
<Hds::Table @caption="Five secrets engines" class="is-border-spacing-revert" data-test-dashboard-secrets-engines-table>
<:body as |B|>
{{#each this.firstFiveSecretsEngines as |backend|}}
<B.Tr data-test-secrets-engines-row={{backend.id}}>
<B.Td class="is-flex-between is-flex-center has-gap-m">
<div>
<div class="is-flex-center">
{{#if backend.icon}}
<ToolTip @horizontalPosition="left" as |T|>
<T.Trigger>
<Icon @name={{backend.icon}} class={{unless backend.isSupportedBackend "has-text-grey"}} />
</T.Trigger>
<T.Content @defaultClass="tool-tip">
<div class="box">
{{or backend.engineType backend.path}}
</div>
</T.Content>
</ToolTip>
{{/if}}
{{#if backend.path}}
{{#if backend.isSupportedBackend}}
<LinkTo
@route={{backend.backendLink}}
@model={{backend.id}}
class="has-text-black has-text-weight-semibold"
data-test-secret-path
>
{{backend.path}}
</LinkTo>
{{else}}
<span class="has-text-grey" data-test-secret-path>{{backend.path}}</span>
{{#if this.filteredSecretsEngines}}
<Hds::Table @caption="Five secrets engines" class="is-border-spacing-revert" data-test-dashboard-secrets-engines-table>
<:body as |B|>
{{#each this.firstFiveSecretsEngines as |backend|}}
<B.Tr data-test-secrets-engines-row={{backend.id}}>
<B.Td class="is-flex-between is-flex-center has-gap-m">
<div>
<div class="is-flex-center">
{{#if backend.icon}}
<ToolTip @horizontalPosition="left" as |T|>
<T.Trigger>
<Icon @name={{backend.icon}} class={{unless backend.isSupportedBackend "has-text-grey"}} />
</T.Trigger>
<T.Content @defaultClass="tool-tip">
<div class="box">
{{or backend.engineType backend.path}}
</div>
</T.Content>
</ToolTip>
{{/if}}
{{#if backend.path}}
{{#if backend.isSupportedBackend}}
<LinkTo
@route={{backend.backendLink}}
@model={{backend.id}}
class="has-text-black has-text-weight-semibold"
data-test-secret-path
>
{{backend.path}}
</LinkTo>
{{else}}
<span class="has-text-grey" data-test-secret-path>{{backend.path}}</span>
{{/if}}
{{/if}}
</div>
{{#if backend.accessor}}
<code class="has-text-grey is-size-8" data-test-accessor>
{{backend.accessor}}
</code>
{{/if}}
{{#if backend.description}}
<div data-test-description class="truncate-first-line">
{{backend.description}}
</div>
{{/if}}
</div>
{{#if backend.accessor}}
<code class="has-text-grey is-size-8" data-test-accessor>
{{backend.accessor}}
</code>
{{#if backend.isSupportedBackend}}
<LinkTo
@route={{backend.backendLink}}
@model={{backend.id}}
class="has-text-weight-semibold is-no-underline"
data-test-view
>
View
</LinkTo>
{{/if}}
{{#if backend.description}}
<div data-test-description class="truncate-first-line">
{{backend.description}}
</div>
{{/if}}
</div>
{{#if backend.isSupportedBackend}}
<LinkTo
@route={{backend.backendLink}}
@model={{backend.id}}
class="has-text-weight-semibold is-no-underline"
data-test-view
>
View
</LinkTo>
{{/if}}
</B.Td>
</B.Tr>
{{/each}}
</:body>
</Hds::Table>
</B.Td>
</B.Tr>
{{/each}}
</:body>
</Hds::Table>
{{#if (gt this.filteredSecretsEngines.length 5)}}
<p class="is-size-9 has-top-margin-xs has-text-grey" data-test-secrets-engine-total-help-text>
Showing 5 out of
{{this.filteredSecretsEngines.length}}
secret engines. Navigate to
<Hds::Link::Inline @route="vault.cluster.secrets.backends">details</Hds::Link::Inline>
to view more.
</p>
{{#if (gt this.filteredSecretsEngines.length 5)}}
<p class="is-size-9 has-top-margin-xs has-text-grey" data-test-secrets-engine-total-help-text>
Showing 5 out of
{{this.filteredSecretsEngines.length}}
secret engines. Navigate to
<Hds::Link::Inline @route="vault.cluster.secrets.backends">details</Hds::Link::Inline>
to view more.
</p>
{{/if}}
{{else}}
<EmptyState
@title="No secrets engines enabled"
@message="Secret engines will be listed here. Enable a secret engine to get started."
class="has-top-margin-m"
data-test-empty-state="secrets-engines"
>
<div>
<LinkTo @route="vault.cluster.settings.mount-secret-backend">Enable a secret engine</LinkTo>
</div>
</EmptyState>
{{/if}}
</Hds::Card::Container>

View File

@@ -1,3 +1,4 @@
export const SELECTORS = {
cardName: (name) => `[data-test-card="${name}"]`,
emptyState: (name) => `[data-test-empty-state="${name}"]`,
};

View File

@@ -25,6 +25,7 @@ module('Integration | Component | dashboard/overview', function (hooks) {
performance: {
clusterId: 'abc-1',
state: 'running',
isPrimary: true,
},
};
this.store.pushPayload('secret-engine', {
@@ -61,6 +62,29 @@ module('Integration | Component | dashboard/overview', function (hooks) {
this.refreshModel = () => {};
});
test('it should show dashboard empty states', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1';
this.isRootNamespace = true;
await render(
hbs`
<Dashboard::Overview
@version={{this.version}}
@isRootNamespace={{this.isRootNamespace}}
@refreshModel={{this.refreshModel}} />
`
);
assert.dom('[data-test-dashboard-version-header]').exists();
assert.dom(SELECTORS.cardName('secrets-engines')).exists();
assert.dom(SELECTORS.emptyState('secrets-engines')).exists();
assert.dom(SELECTORS.cardName('learn-more')).exists();
assert.dom(SELECTORS.cardName('quick-actions')).exists();
assert.dom(SELECTORS.emptyState('quick-actions')).exists();
assert.dom(SELECTORS.cardName('configuration-details')).doesNotExist();
assert.dom(SELECTORS.cardName('replication')).doesNotExist();
assert.dom(SELECTORS.cardName('client-count')).doesNotExist();
});
test('it should hide client count and replication card on community', async function (assert) {
this.version = this.owner.lookup('service:version');
this.version.version = '1.13.1';
@@ -135,6 +159,7 @@ module('Integration | Component | dashboard/overview', function (hooks) {
);
assert.dom('[data-test-dashboard-version-header]').exists();
assert.dom('[data-test-badge-namespace]').exists();
assert.dom(SELECTORS.cardName('secrets-engines')).exists();
assert.dom(SELECTORS.cardName('learn-more')).exists();
assert.dom(SELECTORS.cardName('quick-actions')).exists();
@@ -166,6 +191,7 @@ module('Integration | Component | dashboard/overview', function (hooks) {
);
assert.dom('[data-test-dashboard-version-header]').exists();
assert.dom('[data-test-badge-namespace]').exists();
assert.dom(SELECTORS.cardName('secrets-engines')).exists();
assert.dom(SELECTORS.cardName('learn-more')).exists();
assert.dom(SELECTORS.cardName('quick-actions')).exists();