Ui/redesign delete confirmation (#7271)

* add initial Confirm component to secrets list page

* use ember-wormholes to render confirmation message

* use maybe-in-element instead of ember-wormhole

* hide overlay initially

* animate confirm overlay left and right on click

* hide overlay in the DOM to properly set height

* adjust height when showing/hiding confirm-overlay

* disable confirmation buttons until trigger has been rendered

* adjust height of confirm-wrapper instead of confirm

* move Confirm/ to core

* only add style attribute when a height property exists

* fix indentation

* wip - use new Confirm inside status menu

* add Confirm to Storybook

* ensure confirm links have proper styling in Storybook and outside popup-menu

* fix height transition

* disable no-inline-styles

* add test selector

* remove comment

* consolidate Message into Trigger to make Confirm easier to use

* use new Trigger API in status menu

* remove height transition

* fix binding inline style warning

* rename confirmMessage to message

* update Confirm for Storybook

* fix indentation

* do not pass in onCancel from outer template because it is static

* add jsdoc comments to Trigger

* wip - add trigger and confirm to storybook

* fix status menu styling

* fix styling of confirm stories

* use new Confirm on secrets engine list

* use bulma speed variable

* fix indentation

* re-renable eslint no-inline-styles

* showConfirm when rendered trigger matches id

* fix background color on namespace picker

* do not expose onTrigger

* Revert "re-renable eslint no-inline-styles"

This reverts commit c7b2a9097f177a2876afaaec6020f73b07bad3c7.

* rename Confirm Trigger to Message

* add tests

* update JSDocs

* focus trigger after cancelling the confirm message

* update Confirm JSDocs

* differentiate between ConfirmAction and Confirm

* add Message to Storybook

* re-enable eslint import/extensions

* update confirmButtonText to Revoke token

* remove linebreak and extra whitespace

* fix typo

* add loading to empty button

* fix more typos

* only show Message contents when showConfirm is true

* no need to disable the confirm buttons since they only render in the DOM when showConfirm is true

* use Confirm to delete aws roles

* use Confirm to delete pki roles

* use Confirm to delete ssh roles

* add Confirm to entity alias page

* fix confirm button text on Revoke token in status menu

* ensure you can use tab to revoke a token from status menu

* reset the open trigger after the confirm has been confirmed

* use Confirm on identity list pages

* fix Disable engine confirmation text

* use <PopupMenu /> angle brack syntax

* use Confirm on policies list page

* use Confirm for namespaces

* use Confirm for kmip scopes

* use Confirm for deleting kmip roles

* use Confirm for revoking KMIP credentials

* fix Revoke token triggerText
This commit is contained in:
Noelle Daley
2019-08-27 15:50:53 -07:00
committed by GitHub
parent f1088406f2
commit 3763bb64a4
38 changed files with 976 additions and 500 deletions

View File

@@ -1,3 +1,75 @@
.confirm-wrapper {
position: relative;
overflow: hidden;
border-radius: 2px;
box-shadow: $box-shadow, $box-shadow-middle;
}
.confirm {
transition: transform $speed;
padding-top: 2px;
}
.show-confirm {
transform: translateX(-100%);
transition: transform $speed;
}
.confirm.show-confirm {
visibility: hidden;
}
.confirm-overlay {
position: absolute;
background-color: white;
top: 0;
left: 100%;
width: 100%;
}
.confirm,
.confirm-overlay {
button.link,
a {
background-color: $white;
color: $menu-item-color;
&:hover {
background-color: $menu-item-hover-background-color;
color: $menu-item-hover-color;
}
&.is-active {
background-color: $menu-item-active-background-color;
color: $menu-item-active-color;
}
&.is-destroy {
color: $red;
&:hover {
background-color: $red;
color: $white;
}
}
&.disabled {
opacity: 0.5;
&:hover {
background: transparent;
cursor: default;
}
}
}
}
.confirm-action span .button {
display: block;
margin: 0.25rem auto;
width: 95%;
}
.confirm-action > span {
@include from($tablet) {
align-items: center;
@@ -22,7 +94,7 @@
}
}
.popup-menu-content .confirm-action-message {
.confirm-action-message {
margin: 0;
.message {
@@ -56,6 +128,7 @@
flex: 1;
text-align: center;
width: auto;
padding: $spacing-xs;
}
}
}

View File

@@ -64,6 +64,10 @@
border-radius: $radius;
box-shadow: $box-shadow, $box-shadow-high;
&.ember-basic-dropdown-content {
background: $white;
}
@include from($mobile) {
width: $drawer-width;
}

View File

@@ -135,6 +135,8 @@
}
.ember-basic-dropdown-content {
background-color: transparent;
&--left.popup-menu {
margin: 0px 0 0 -8px;
}

View File

@@ -1,3 +1,4 @@
<Confirm as |c|>
<div class="popup-menu-content">
<div class="box">
<div class="menu-label">
@@ -30,27 +31,25 @@
</button>
</li>
<li class="action">
<ConfirmAction
@buttonClasses="button link is-destroy"
@confirmTitle={{concat "Revoke " (get auth 'authData.displayName') "?"}}
@confirmMessage={{concat "You will not be able to log in again with this token."}}
@confirmButtonText="Revoke"
@onConfirmAction={{action "revokeToken"}}
>
Revoke token
</ConfirmAction>
<c.Message
@id={{get auth 'authData.displayName'}}
@title={{concat "Revoke " (get auth 'authData.displayName') "?"}}
@onConfirm={{action "revokeToken"}}
@message="You will not be able to log in again with this token."
@triggerText="Revoke token"
@confirmButtonText='Revoke'
/>
</li>
{{else}}
<li class="action text-right">
<ConfirmAction
@buttonClasses="button link is-destroy"
@confirmTitle={{concat "Revoke " (get auth 'authData.displayName') "?"}}
@confirmMessage={{concat "You will not be able to log in again with this token."}}
@confirmButtonText="Revoke"
@onConfirmAction={{action "revokeToken"}}
>
Revoke token
</ConfirmAction>
<c.Message
@id={{get auth 'authData.displayName'}}
@title={{concat "Revoke " (get auth 'authData.displayName') "?"}}
@onConfirm={{action "revokeToken"}}
@message="You will not be able to log in again with this token."
@triggerText="Revoke token"
@confirmButtonText='Revoke'
/>
</li>
{{/if}}
{{/if}}
@@ -63,3 +62,4 @@
</nav>
</div>
</div>
</Confirm>

View File

@@ -1,39 +1,37 @@
{{#popup-menu name="alias-menu"}}
{{#with params.firstObject as |item|}}
<nav class="menu">
<ul class="menu-list">
<li class="action">
{{#link-to "vault.cluster.access.identity.aliases.show" (pluralize item.parentType) item.id "details"}}
Details
{{/link-to}}
</li>
{{#if item.updatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canEdit}}
<Confirm as |c|>
{{#with params.firstObject as |item|}}
<nav class="menu">
<ul class="menu-list">
<li class="action">
{{#link-to "vault.cluster.access.identity.aliases.edit" (pluralize item.parentType) item.id}}
Edit
{{#link-to "vault.cluster.access.identity.aliases.show" (pluralize item.parentType) item.id "details"}}
Details
{{/link-to}}
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<ConfirmAction
@buttonClasses="link is-destroy"
@onConfirmAction={{action "performTransaction" item}}
data-test-item-delete="true"
>
Delete
</ConfirmAction>
</li>
{{/if}}
{{/if}}
</ul>
</nav>
{{/with}}
{{#if item.updatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canEdit}}
<li class="action">
{{#link-to "vault.cluster.access.identity.aliases.edit" (pluralize item.parentType) item.id}}
Edit
{{/link-to}}
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<c.Message
@id={{item.id}}
@onConfirm={{action "performTransaction" item}} />
</li>
{{/if}}
{{/if}}
</ul>
</nav>
{{/with}}
</Confirm>
{{/popup-menu}}

View File

@@ -1,57 +1,56 @@
{{#popup-menu name="role-aws-nav" contentClass="is-wide"}}
<nav class="menu">
<ul class="menu-list">
{{#if item.generatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else if item.canGenerate}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.credentials" item.id data-test-role-aws-link="generate"}}
Generate credentials
{{/link-to}}
</li>
{{/if}}
{{#if item.updatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canRead}}
<Confirm as |c|>
<nav class="menu">
<ul class="menu-list">
{{#if item.generatePath.isPending}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.show" item.id data-test-role-ssh-link="show"}}
Details
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else if item.canGenerate}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.credentials" item.id data-test-role-aws-link="generate"}}
Generate credentials
{{/link-to}}
</li>
{{/if}}
{{#if item.canEdit}}
{{#if item.updatePath.isPending}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.edit" item.id data-test-role-ssh-link="edit"}}
Edit
{{/link-to}}
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<ConfirmAction
@buttonClasses="link is-destroy"
@onConfirmAction={{action "delete" item}}
data-test-aws-role-delete={{item.id}}
>
Delete
</ConfirmAction>
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canRead}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.show" item.id data-test-role-ssh-link="show"}}
Details
{{/link-to}}
</li>
{{/if}}
{{#if item.canEdit}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.edit" item.id data-test-role-ssh-link="edit"}}
Edit
{{/link-to}}
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<c.Message
@id={{item.id}}
@onConfirm={{action "delete" item}}
data-test-aws-role-delete={{item.id}}/>
</li>
{{/if}}
{{/if}}
{{/if}}
</ul>
</nav>
</ul>
</nav>
</Confirm>
{{/popup-menu}}

View File

@@ -1,70 +1,69 @@
{{#popup-menu name="role-aws-nav"}}
<nav class="menu">
{{#if (or item.generatePath.isPending item.signPath.isPending)}}
<ul class="menu-list">
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
</ul>
{{else if (or item.canGenerate item.canSign)}}
<ul class="menu-list">
{{#if item.canGenerate}}
<Confirm as |c|>
<nav class="menu">
{{#if (or item.generatePath.isPending item.signPath.isPending)}}
<ul class="menu-list">
<li class="action">
{{#link-to "vault.cluster.secrets.backend.credentials" item.id (query-params action="issue") data-test-role-pki-link="generate-certificate"}}
Generate certificate
{{/link-to}}
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{/if}}
{{#if item.canSign}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.credentials" item.id (query-params action="sign") data-test-role-pki-link="sign-certificate"}}
Sign certificate
{{/link-to}}
</li>
{{/if}}
</ul>
{{/if}}
<ul class="menu-list">
{{#if item.updatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canRead}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.show" item.id data-test-role-pki-link="show"}}
Details
{{/link-to}}
</li>
{{/if}}
{{#if item.canEdit}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.edit" item.id data-test-role-pki-link="edit"}}
Edit
{{/link-to}}
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<ConfirmAction
@buttonClasses="link is-destroy"
@onConfirmAction={{action "delete" item}}
data-test-aws-role-delete={{item.id}}
>
Delete
</ConfirmAction>
</li>
{{/if}}
</ul>
{{else if (or item.canGenerate item.canSign)}}
<ul class="menu-list">
{{#if item.canGenerate}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.credentials" item.id (query-params action="issue") data-test-role-pki-link="generate-certificate"}}
Generate certificate
{{/link-to}}
</li>
{{/if}}
{{#if item.canSign}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.credentials" item.id (query-params action="sign") data-test-role-pki-link="sign-certificate"}}
Sign certificate
{{/link-to}}
</li>
{{/if}}
</ul>
{{/if}}
</ul>
</nav>
<ul class="menu-list">
{{#if item.updatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canRead}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.show" item.id data-test-role-pki-link="show"}}
Details
{{/link-to}}
</li>
{{/if}}
{{#if item.canEdit}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.edit" item.id data-test-role-pki-link="edit"}}
Edit
{{/link-to}}
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<c.Message
@id={{item.id}}
@onConfirm={{action "delete" item}}
data-test-pki-role-delete={{item.id}}/>
</li>
{{/if}}
{{/if}}
</ul>
</nav>
</Confirm>
{{/popup-menu}}

View File

@@ -1,94 +1,93 @@
{{#popup-menu name="role-ssh-nav"}}
<nav class="menu">
<ul class="menu-list">
{{#if (eq item.keyType 'otp')}}
{{#if item.generatePath.isPending}}
<Confirm as |c|>
<nav class="menu">
<ul class="menu-list">
{{#if (eq item.keyType 'otp')}}
{{#if item.generatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else if item.canGenerate}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.credentials" item.id data-test-role-ssh-link="generate"}}
Generate Credentials
{{/link-to}}
</li>
{{/if}}
{{else if (eq item.keyType 'ca')}}
{{#if item.signPath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else if item.canGenerate}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.sign" item.id data-test-role-ssh-link="generate"}}
Sign Keys
{{/link-to}}
</li>
{{/if}}
{{/if}}
{{#if item.canEditZeroAddress}}
{{#if item.zeroAddress}}
<li class="action">
<button type="button" disabled={{get this (concat "loading-" item.id)}} class="link button is-transparent
{{if (get this (concat "loading-" item.id)) 'is-loading'}} " {{action "toggleZeroAddress" item backendModel}}>
Disable Zero Address
</button>
</li>
{{else}}
<li class="action">
<button
type="button"
disabled={{get this (concat "loading-" item.id)}}
class="link button is-transparent {{if (get this (concat "loading-" item.id)) 'is-loading'}}"
{{action "toggleZeroAddress" item backendModel}}
>
Enable Zero Address
</button>
</li>
{{/if}}
{{/if}}
{{#if item.updatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else if item.canGenerate}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.credentials" item.id data-test-role-ssh-link="generate"}}
Generate Credentials
{{/link-to}}
</li>
{{/if}}
{{else if (eq item.keyType 'ca')}}
{{#if item.signPath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else if item.canGenerate}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.sign" item.id data-test-role-ssh-link="generate"}}
Sign Keys
{{/link-to}}
</li>
{{/if}}
{{/if}}
{{#if item.canEditZeroAddress}}
{{#if item.zeroAddress}}
<li class="action">
<button type="button" disabled={{get this (concat "loading-" item.id)}} class="link button is-transparent
{{if (get this (concat "loading-" item.id)) 'is-loading'}} " {{action "toggleZeroAddress" item backendModel}}>
Disable Zero Address
</button>
</li>
{{else}}
<li class="action">
<button
type="button"
disabled={{get this (concat "loading-" item.id)}}
class="link button is-transparent {{if (get this (concat "loading-" item.id)) 'is-loading'}}"
{{action "toggleZeroAddress" item backendModel}}
>
Enable Zero Address
</button>
</li>
{{#if item.canRead}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.show" item.id data-test-role-ssh-link="show"}}
Details
{{/link-to}}
</li>
{{/if}}
{{#if item.canEdit}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.edit" item.id data-test-role-ssh-link="edit"}}
Edit
{{/link-to}}
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<c.Message
@id={{item.id}}
@onConfirm={{action "delete" item}}
data-test-ssh-role-delete={{item.id}}/>
</li>
{{/if}}
{{/if}}
{{/if}}
{{#if item.updatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canRead}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.show" item.id data-test-role-ssh-link="show"}}
Details
{{/link-to}}
</li>
{{/if}}
{{#if item.canEdit}}
<li class="action">
{{#link-to "vault.cluster.secrets.backend.edit" item.id data-test-role-ssh-link="edit"}}
Edit
{{/link-to}}
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<ConfirmAction
@buttonClasses="link is-destroy"
@onConfirmAction={{action "delete" item}}
data-test-ssh-role-delete={{item.id}}
>
Delete
</ConfirmAction>
</li>
{{/if}}
{{/if}}
</ul>
</nav>
</ul>
</nav>
</Confirm>
{{/popup-menu}}

View File

@@ -14,88 +14,78 @@
<SecretLink
@mode={{if item.isFolder "list" "show" }}
@secret={{item.id}}
@class="has-text-black has-text-weight-semibold"
><Icon
@class="has-text-black has-text-weight-semibold"><Icon
@glyph={{if item.isFolder 'folder-outline' 'file-outline' }}
@class="has-text-grey-light"
/>{{if (eq item.id ' ') '(self)' (or item.keyWithoutParent item.id)}}
@class="has-text-grey-light"/>{{if (eq item.id ' ') '(self)' (or item.keyWithoutParent item.id)}}
</SecretLink>
</div>
<div class="column has-text-right">
<PopupMenu name="secret-menu">
<nav class="menu">
<ul class="menu-list">
{{#if item.isFolder}}
<SecretLink
@mode="list"
@secret={{item.id}}
@class="has-text-black has-text-weight-semibold"
>
Contents
</SecretLink>
{{else}}
{{#if (or item.versionPath.isLoading item.secretPath.isLoading)}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canRead}}
<li class="action">
<SecretLink
@mode="show"
@secret={{item.id}}
@class="has-text-black has-text-weight-semibold"
>
Details
</SecretLink>
</li>
{{#if backendModel.isV2KV}}
<Confirm as |c|>
<nav class="menu">
<ul class="menu-list">
{{#if item.isFolder}}
<SecretLink
@mode="list"
@secret={{item.id}}
@class="has-text-black has-text-weight-semibold">
Contents
</SecretLink>
{{else}}
{{#if (or item.versionPath.isLoading item.secretPath.isLoading)}}
<li class="action">
<SecretLink
@mode="versions"
@secret={{item.id}}
@class="has-text-black has-text-weight-semibold"
>
View version history
</SecretLink>
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canRead}}
<li class="action">
<SecretLink
@mode="show"
@secret={{item.id}}
@class="has-text-black has-text-weight-semibold">
Details
</SecretLink>
</li>
{{#if backendModel.isV2KV}}
<li class="action">
<SecretLink
@mode="versions"
@secret={{item.id}}
@class="has-text-black has-text-weight-semibold">
View version history
</SecretLink>
</li>
{{/if}}
{{/if}}
{{#if item.canEdit}}
<li class="action">
<SecretLink
@mode="edit"
@secret={{item.id}}
@class="has-text-black has-text-weight-semibold">
{{if backendModel.isV2KV
"Create new version"
"Edit"
}}
</SecretLink>
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<c.Message
@id={{item.id}}
@triggerText={{if backendModel.isV2KV "Permanently delete" "Delete"}}
@message="This will permanently delete this secret and all its versions."
@onConfirm={{action "delete" item "secret"}}/>
</li>
{{/if}}
{{/if}}
{{/if}}
{{#if item.canEdit}}
<li class="action">
<SecretLink
@mode="edit"
@secret={{item.id}}
@class="has-text-black has-text-weight-semibold"
>
{{if backendModel.isV2KV
"Create new version"
"Edit"
}}
</SecretLink>
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<ConfirmAction
@buttonClasses="link is-destroy"
@confirmMessage="This will permanently delete this secret and all its versions."
@onConfirmAction={{action "delete" item "secret"}}
data-test-v2-kv-delete={{item.id}}
>
{{if backendModel.isV2KV
"Permanently delete"
"Delete"
}}
</ConfirmAction>
</li>
{{/if}}
{{/if}}
{{/if}}
</ul>
</nav>
</ul>
</nav>
</Confirm>
</PopupMenu>
</div>
</div>

View File

@@ -31,65 +31,63 @@
</div>
<div class="column has-text-right">
{{#popup-menu name="identity-item" onOpen=(action "reloadRecord" item)}}
<nav class="menu">
<ul class="menu-list">
<li class="action">
{{#link-to "vault.cluster.access.identity.show" item.id "details"}}
Details
{{/link-to}}
</li>
{{#if (or item.isReloading item.updatePath.isPending item.aliasPath.isPending)}}
<Confirm as |c|>
<nav class="menu">
<ul class="menu-list">
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
{{#link-to "vault.cluster.access.identity.show" item.id "details"}}
Details
{{/link-to}}
</li>
{{else}}
{{#if item.canAddAlias}}
{{#if (or item.isReloading item.updatePath.isPending item.aliasPath.isPending)}}
<li class="action">
{{#link-to "vault.cluster.access.identity.aliases.add" (pluralize identityType) item.id}}
Create alias
{{/link-to}}
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canAddAlias}}
<li class="action">
{{#link-to "vault.cluster.access.identity.aliases.add" (pluralize identityType) item.id}}
Create alias
{{/link-to}}
</li>
{{/if}}
{{#if item.canEdit}}
<li class="action">
{{#link-to "vault.cluster.access.identity.edit" item.id}}
Edit
{{/link-to}}
</li>
<li class="action">
{{#if item.disabled}}
<button type="button" {{action "toggleDisabled" item}} class="link">
Enable
</button>
{{else if (eq identityType 'entity')}}
<c.Message
@id="{{item.id}}-disable"
@triggerText="Disable"
@message="Associated tokens will not be revoked, but cannot be used"
@title="Disable this?"
@confirmButtonText="Disable"
@onConfirm={{action "toggleDisabled" item}}
data-test-engine-disable="true"/>
{{/if}}
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<c.Message
@id={{item.id}}
@onConfirm={{action "delete" item}}
data-test-item-delete="true"/>
</li>
{{/if}}
{{/if}}
{{#if item.canEdit}}
<li class="action">
{{#link-to "vault.cluster.access.identity.edit" item.id}}
Edit
{{/link-to}}
</li>
<li class="action">
{{#if item.disabled}}
<button type="button" {{action "toggleDisabled" item}} class="link">
Enable
</button>
{{else if (eq identityType 'entity')}}
<ConfirmAction
@buttonClasses="link is-destroy"
@confirmTitle="Disable this?"
@confirmMessage="Associated tokens will not be revoked, but cannot be used"
@confirmButtonText="Disable"
@onConfirmAction={{action "toggleDisabled" item}}
>
Disable
</ConfirmAction>
{{/if}}
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<ConfirmAction
@buttonClasses="link is-destroy"
@onConfirmAction={{action "delete" item}}
data-test-item-delete="true"
>
Delete
</ConfirmAction>
</li>
{{/if}}
{{/if}}
</ul>
</nav>
</ul>
</nav>
</Confirm>
{{/popup-menu}}
</div>
</div>

View File

@@ -34,7 +34,7 @@
<Item.content>
{{list.item.id}}
</Item.content>
<Item.menu>
<Item.menu as |m|>
{{#with (concat currentNamespace (if currentNamespace "/") list.item.id) as |targetNamespace|}}
{{#if (contains targetNamespace accessibleNamespaces)}}
<li class="action">
@@ -45,11 +45,11 @@
{{/if}}
{{/with}}
<li class="action">
<ConfirmAction
@buttonClasses="link is-destroy"
<m.Message
@id={{list.item.id}}
@confirmButtonText="Remove"
@confirmMessage="Any engines or mounts in this namespace will also be removed."
@onConfirmAction={{action
@message="Any engines or mounts in this namespace will also be removed."
@onConfirm={{action
(perform
Item.callMethod
"destroyRecord"
@@ -58,10 +58,7 @@
"There was an error deleting this namespace: "
(action "refreshNamespaceList")
)
}}
>
Delete
</ConfirmAction>
}} />
</li>
</Item.menu>
</ListItem>

View File

@@ -88,51 +88,50 @@
{{/link-to}}
</div>
<div class="column has-text-right">
{{#popup-menu name="policy-nav"}}
<nav class="menu">
<ul class="menu-list">
{{#if item.updatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canRead}}
<PopupMenu name="policy-nav">
<Confirm as |c|>
<nav class="menu">
<ul class="menu-list">
{{#if item.updatePath.isPending}}
<li class="action">
{{#link-to "vault.cluster.policy.show" policyType item.id data-test-policy-link="show"}}
Details
{{/link-to}}
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{/if}}
{{#if item.canEdit}}
<li class="action">
{{#link-to "vault.cluster.policy.edit" policyType item.id data-test-policy-link="edit"}}
Edit
{{/link-to}}
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{else}}
{{#if item.canRead}}
<li class="action">
{{#link-to "vault.cluster.policy.show" policyType item.id data-test-policy-link="show"}}
Details
{{/link-to}}
</li>
{{/if}}
{{#if item.canEdit}}
<li class="action">
{{#link-to "vault.cluster.policy.edit" policyType item.id data-test-policy-link="edit"}}
Edit
{{/link-to}}
</li>
{{/if}}
{{#if item.canDelete}}
<li class="action">
<c.Message
@id={{item.id}}
@confirmMessage="This will permanently delete this policy and may affect access to some data"
@onConfirm={{action "deletePolicy" item}}
data-test-policy-delete={{item.id}}/>
</li>
{{/if}}
{{/if}}
{{#if item.canDelete}}
<li class="action">
<ConfirmAction
@buttonClasses="link is-destroy"
@confirmMessage="This will permanently delete this policy and may affect access to some data"
@onConfirmAction={{action "deletePolicy" item}}
data-test-policy-delete={{item.id}}
>
Delete
</ConfirmAction>
</li>
{{/if}}
{{/if}}
</ul>
</nav>
{{/popup-menu}}
</ul>
</nav>
</Confirm>
</PopupMenu>
</div>
</div>
{{/linked-block}}

View File

@@ -69,6 +69,7 @@
<div class="level-right is-flex is-paddingless is-marginless">
<div class="level-item">
{{#popup-menu name="engine-menu"}}
<Confirm as |c|>
<nav class="menu">
<ul class="menu-list">
<li class="action">
@@ -78,25 +79,27 @@
</li>
{{#unless (eq backend.type "cubbyhole")}}
<li class="action">
<ConfirmAction
@buttonClasses="link is-destroy"
@confirmTitle="Disable engine?"
@confirmMessage="Any data in this engine will be permanently deleted."
<c.Message
@id={{backend.id}}
@triggerText="Disable"
@message="Any data in this engine will be permanently deleted."
@title="Disable engine?"
@confirmButtonText="Disable"
@onConfirmAction={{perform disableEngine backend}}
@onConfirm={{perform disableEngine backend}}
data-test-engine-disable="true"
>
Disable
</ConfirmAction>
/>
</li>
{{/unless}}
{{#if item.updatePath.isPending}}
<li class="action">
<button disabled type="button" class="link button is-loading is-transparent"></button>
<button disabled type="button" class="link button is-loading is-transparent">
loading
</button>
</li>
{{/if}}
</ul>
</nav>
</ul>
</nav>
</Confirm>
{{/popup-menu}}
</div>
</div>
@@ -131,29 +134,30 @@
</div>
<div class="level-right is-flex is-paddingless is-marginless">
<div class="level-item">
{{#popup-menu name="engine-menu"}}
<nav class="menu">
<ul class="menu-list">
<li class="action">
{{#link-to "vault.cluster.secrets.backend.configuration" backend.id data-test-engine-config}}
View configuration
{{/link-to}}
</li>
<li>
<ConfirmAction
@buttonClasses="link is-destroy"
@confirmTitle="Disable engine?"
@confirmMessage="Any data in this engine will be permanently deleted."
@confirmButtonText="Disable"
@onConfirmAction={{perform disableEngine backend}}
data-test-engine-disable="true"
>
Disable
</ConfirmAction>
</li>
</ul>
</nav>
{{/popup-menu}}
<PopupMenu name="engine-menu">
<Confirm as |c|>
<nav class="menu">
<ul class="menu-list">
<li class="action">
{{#link-to "vault.cluster.secrets.backend.configuration" backend.id data-test-engine-config}}
View configuration
{{/link-to}}
</li>
<li>
<c.Message
@id={{backend.id}}
@triggerText="Disable"
@message="Any data in this engine will be permanently deleted."
@title="Disable engine?"
@confirmButtonText="Disable"
@onConfirm={{perform disableEngine backend}}
data-test-engine-disable="true"
/>
</li>
</ul>
</nav>
</Confirm>
</PopupMenu>
</div>
</div>
</div>

View File

@@ -1,4 +1,4 @@
/* eslint-disable import/extensions */
import hbs from 'htmlbars-inline-precompile';
import { storiesOf } from '@storybook/ember';
<%= importMD %>

View File

@@ -3,7 +3,7 @@ import layout from '../templates/components/confirm-action';
/**
* @module ConfirmAction
* `ConfirmAction` is a button followed by a confirmation message and button used to prevent users from performing actions they do not intend to.
* `ConfirmAction` is a button followed by a pop up confirmation message and button used to prevent users from performing actions they do not intend to.
*
* @example
* ```js

View File

@@ -0,0 +1,70 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import { htmlSafe } from '@ember/template';
import layout from '../templates/components/confirm';
import { next } from '@ember/runloop';
/**
* @module Confirm
* `Confirm` components prevent users from performing actions they do not intend to by showing a confirmation message as an overlay. This is a contextual component that should always be rendered with a `Message` which triggers the message.
*
* @example
* ```js
* <div class="box">
* <Confirm as |c|>
* <c.Message
* @id={{item.id}}
* @triggerText="Delete"
* @message="This will permanently delete this secret and all its versions."
* @onConfirm={{action "delete" item "secret"}}
* />
* </Confirm>
* </div>
* ```
*/
export default Component.extend({
layout,
openTrigger: null,
height: 0,
focusTrigger: null,
style: computed('height', function() {
return htmlSafe(`height: ${this.height}px`);
}),
wormholeReference: null,
wormholeId: computed(function() {
return `confirm-${this.elementId}`;
}),
didInsertElement() {
this.set('wormholeReference', this.element.querySelector(`#${this.wormholeId}`));
},
didRender() {
this.updateHeight();
},
updateHeight: function() {
let height;
height = this.openTrigger
? this.element.querySelector('.confirm-overlay').clientHeight
: this.element.querySelector('.confirm').clientHeight;
this.set('height', height);
},
actions: {
onTrigger: function(itemId, e) {
this.set('openTrigger', itemId);
// store a reference to the trigger so we can focus the element
// after clicking cancel
this.set('focusTrigger', e.target);
this.updateHeight();
},
onCancel: function() {
this.set('openTrigger', '');
this.updateHeight();
next(() => {
this.focusTrigger.focus();
this.set('focusTrigger', null);
});
},
},
});

View File

@@ -0,0 +1,54 @@
import Component from '@ember/component';
import { computed } from '@ember/object';
import layout from '../../templates/components/confirm/message';
/**
* @module Message
* `Message` components trigger and display a confirmation message. They should only be used within a `Confirm` component.
*
* @example
* ```js
* <div class="box">
* <Confirm as |c|>
* <c.Message
* @id={{item.id}}
* @triggerText="Delete"
* @message="This will permanently delete this secret and all its versions."
* @onConfirm={{action "delete" item "secret"}}
* />
* </Confirm>
* </div>
* ```
*
* @property id=null {ID} - A unique identifier used to bind a trigger to a confirmation message.
* @property onConfirm=null {Func} - The action to take when the user clicks the confirm button.
* @property [triggerText='Delete'] {String} - The text on the trigger button.
* @property [title='Delete this?'] {String} - The header text to display in the confirmation message.
* @property [message='You will not be able to recover it later.'] {String} - The message to display above the confirm and cancel buttons.
* @property [confirmButtonText='Delete'] {String} - The text to display on the confirm button.
* @property [cancelButtonText='Cancel'] {String} - The text to display on the cancel button.
*/
export default Component.extend({
layout,
tagName: '',
renderedTrigger: null,
id: null,
onCancel() {},
onConfirm() {},
resetTrigger() {},
title: 'Delete this?',
message: 'You will not be able to recover it later.',
triggerText: 'Delete',
confirmButtonText: 'Delete',
cancelButtonText: 'Cancel',
showConfirm: computed('renderedTrigger', function() {
return this.renderedTrigger === this.id;
}),
actions: {
onConfirm() {
this.onConfirm();
this.resetTrigger();
},
},
});

View File

@@ -0,0 +1,17 @@
{{! template-lint-disable no-inline-styles}}
<div class="confirm-wrapper" style={{style}}>
<div class="confirm {{if openTrigger "show-confirm"}}" ...attributes>
{{yield (hash
Message=(component "confirm/message"
renderedTrigger=(readonly this.openTrigger)
wormholeReference=this.wormholeReference
onCancel=(action 'onCancel')
onTrigger=(action 'onTrigger')
resetTrigger=(action (mut this.openTrigger) "")
))
}}
</div>
<div id={{this.wormholeId}} class="confirm-overlay {{if openTrigger "show-confirm"}}">
</div>
</div>

View File

@@ -0,0 +1,40 @@
{{#if showConfirm}}
{{#maybe-in-element wormholeReference false}}
<div class="confirm-action-message">
<div class="message is-highlight">
<div class="message-title">
<Icon @glyph="alert-triangle" />
{{title}}
</div>
<p>
{{message}}
</p>
</div>
<div class="confirm-action-options">
<button
type="button"
class="link is-destroy"
data-test-confirm-button="true"
onclick={{action "onConfirm"}}>
{{confirmButtonText}}
</button>
<button
type="button"
class="link"
data-test-confirm-cancel-button="true"
{{action this.onCancel}}>
{{cancelButtonText}}
</button>
</div>
</div>
{{/maybe-in-element}}
{{/if}}
<button
type="button"
class="link is-destroy"
disabled={{showConfirm}}
onclick={{action this.onTrigger id}}
data-test-confirm-action-trigger={{id}}>
{{triggerText}}
</button>

View File

@@ -1,10 +1,12 @@
{{#if hasMenu}}
<PopupMenu>
<nav class="menu">
<ul class="menu-list">
{{yield item}}
</ul>
</nav>
<Confirm as |c|>
<nav class="menu">
<ul class="menu-list">
{{yield (hash Message=c.Message)}}
</ul>
</nav>
</Confirm>
</PopupMenu>
{{else}}
{{yield item}}

View File

@@ -0,0 +1 @@
export { default } from 'core/components/confirm';

View File

@@ -0,0 +1 @@
export { default } from 'core/components/confirm/message';

View File

@@ -1,7 +1,7 @@
<!--THIS FILE IS AUTO GENERATED. This file is generated from JSDoc comments in app/components/confirm-action.js. To make changes, first edit that file and run "yarn gen-story-md confirm-action" to re-generate the content.-->
## ConfirmAction
`ConfirmAction` is a button followed by a confirmation message and button used to prevent users from performing actions they do not intend to.
`ConfirmAction` is a button followed by a pop up confirmation message and button used to prevent users from performing actions they do not intend to.
**Properties**

View File

@@ -0,0 +1,28 @@
<!--THIS FILE IS AUTO GENERATED. This file is generated from JSDoc comments in lib/core/addon/components/confirm.js. To make changes, first edit that file and run "yarn gen-story-md confirm" to re-generate the content.-->
## Confirm
`Confirm` components prevent users from performing actions they do not intend to by showing a confirmation message as an overlay. This is a contextual component that should always be rendered with a `Message` which triggers the message.
See the `Message` component for a description of properties.
**Example**
```js
<div class="box">
<Confirm as |c|>
<c.Message
@id={{item.id}}
@triggerText="Delete"
@message="This will permanently delete this secret and all its versions."
@onConfirm={{action "delete" item "secret"}}
/>
</Confirm>
</div>
```
**See**
- [Uses of Confirm](https://github.com/hashicorp/vault/search?l=Handlebars&q=Confirm+OR+confirm)
- [Confirm Source Code](https://github.com/hashicorp/vault/blob/master/ui/lib/core/addon/components/confirm.js)
---

View File

@@ -0,0 +1,47 @@
import hbs from 'htmlbars-inline-precompile';
import { storiesOf } from '@storybook/ember';
import notes from './confirm.md';
import { withKnobs, text } from '@storybook/addon-knobs';
storiesOf('Confirm/Confirm', module)
.addParameters({ options: { showPanel: true } })
.addDecorator(
withKnobs({
escapeHTML: false,
})
)
.add(
`Confirm`,
() => ({
template: hbs`
<h5 class="title is-5">Confirm</h5>
<div class="popup-menu-content">
<div class="box">
<Confirm as |c|>
<c.Message
@id={{id}}
@title={{title}}
@triggerText={{triggerText}}
@message={{message}}
@confirmButtonText={{confirmButtonText}}
@cancelButtonText={{cancelButtonText}}
@onConfirm={{onConfirm}}
/>
</Confirm>
</div>
</div>
`,
context: {
id: 'foo',
onConfirm: () => {
alert('Confirmed!');
},
title: text('title', 'Delete this?'),
message: text('message', 'You will not be able to recover it later.'),
confirmButtonText: text('confirmButtonText', 'Delete'),
cancelButtonText: text('cancelButtonText', 'Cancel'),
triggerText: text('triggerText', 'Delete'),
},
}),
{ notes }
);

View File

@@ -0,0 +1,39 @@
<!--THIS FILE IS AUTO GENERATED. This file is generated from JSDoc comments in lib/core/addon/components/confirm.js. To make changes, first edit that file and run "yarn gen-story-md confirm" to re-generate the content.-->
## Message
`Message` components trigger and display a confirmation message. They should only be used within a `Confirm` component.
**Properties**
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| id | <code>ID</code> | <code></code> | A unique identifier used to bind a trigger to a confirmation message. |
| onConfirm | <code>Func</code> | <code></code> | The action to take when the user clicks the confirm button. |
| [triggerText] | <code>String</code> |<code>'Delete'</code> | The text on the trigger button. |
| [title] | <code>String</code> | <code>'Delete this?'</code> | The header text to display in the confirmation message. |
| [message] | <code>String</code> | <code>'You will not be able to recover it later.'</code> | The message to display above the confirm and cancel buttons. |
| [confirmButtonText] | <code>String</code> | <code>'Delete'</code> | The text to display on the confirm button. |
| [cancelButtonText] | <code>String</code> | <code>'Cancel'</code> | The text to display on the cancel button. |
**Example**
```js
<div class="box">
<Confirm as |c|>
<c.Message
@id={{item.id}}
@triggerText="Delete"
@message="This will permanently delete this secret and all its versions."
@onConfirm={{action "delete" item "secret"}}
/>
</Confirm>
</div>
```
**See**
- [Uses of Confirm](https://github.com/hashicorp/vault/search?l=Handlebars&q=Confirm+OR+confirm)
- [Confirm Source Code](https://github.com/hashicorp/vault/blob/master/ui/lib/core/addon/components/confirm.js)
---

View File

@@ -0,0 +1,19 @@
import hbs from 'htmlbars-inline-precompile';
import { storiesOf } from '@storybook/ember';
import notes from './message.md';
storiesOf('Confirm/Message/', module)
.addParameters({ options: { showPanel: true } })
.add(
`Message`,
() => ({
template: hbs`
<h5 class="title is-5">Message</h5>
<p>
<code>Message</code> components should never render on their own. See the <code>Confirm</code> component for an example of what a <code>Message</code> looks like.
</p>
`,
context: {},
}),
{ notes }
);

View File

@@ -54,7 +54,7 @@
<Item.content>
<Icon @glyph="pki" class="has-text-grey-light" @size="l" />{{list.item.id}}
</Item.content>
<Item.menu>
<Item.menu as |m|>
<li class="action">
{{#link-to "credentials.show" this.scope this.role list.item.id class="is-block"}}
View credentials
@@ -62,9 +62,13 @@
</li>
{{#if list.item.deletePath.canDelete}}
<MenuLoader @loadingParam={{list.item.deletePath.isPending}}>
<ConfirmAction
@buttonClasses="link is-destroy"
@onConfirmAction={{action
<m.Message
@id={{list.item.id}}
@triggerText="Revoke credentials"
@title="Revoke this?"
@message="Any client using these credentials will no longer be able to."
@confirmButtonText="Revoke"
@onConfirm={{action
(perform
Item.callMethod
"destroyRecord"
@@ -74,13 +78,7 @@
(action "refresh")
)
}}
@confirmTitle="Revoke this?"
@confirmMessage="Any client using these credentials will no longer be able to."
@cancelButtonText="Cancel"
@confirmButtonText="Revoke"
>
Revoke credentials
</ConfirmAction>
/>
</MenuLoader>
{{/if}}
</Item.menu>

View File

@@ -64,7 +64,7 @@
<Item.content>
<Icon @glyph="user-plain" class="has-text-grey-light" @size="l" />{{list.item.id}}
</Item.content>
<Item.menu>
<Item.menu as |m|>
<li class="action">
{{#link-to "credentials" this.scope list.item.id class="is-block"}}
View credentials
@@ -84,9 +84,11 @@
{{/if}}
{{#if list.item.updatePath.canDelete}}
<MenuLoader @loadingParam={{list.item.updatePath.isPending}}>
<ConfirmAction
@buttonClasses="link is-destroy"
@onConfirmAction={{action
<m.Message
@id={{list.item.id}}
@triggerText='Delete role'
@message={{concat "Are you sure you want to delete " list.item.id "?"}}
@onConfirm={{action
(perform
Item.callMethod
"destroyRecord"
@@ -96,12 +98,8 @@
(action "refresh")
)
}}
@confirmMessage={{concat "Are you sure you want to delete " list.item.id "?"}}
@cancelButtonText="Cancel"
data-test-scope-delete="true"
>
Delete role
</ConfirmAction>
/>
</MenuLoader>
{{/if}}
</Item.menu>

View File

@@ -54,7 +54,7 @@
<Item.content>
<Icon @glyph="folder-outline" class="has-text-grey-light" @size="l" />{{list.item.id}}
</Item.content>
<Item.menu>
<Item.menu as |m|>
<li class="action">
{{#link-to "scope" list.item.id class="is-block"}}
View scope
@@ -62,9 +62,13 @@
</li>
{{#if list.item.updatePath.canDelete}}
<MenuLoader @loadingParam={{list.item.updatePath.isPending}}>
<ConfirmAction
@buttonClasses="link is-destroy"
@onConfirmAction={{action
<m.Message
@id={{list.item.id}}
@triggerText='Delete scope'
@title={{concat "Delete scope " list.item.id "?"}}
@message="This will permanently delete this scope and all roles and credentials contained within"
@cancelButtonText="Cancel"
@onConfirm={{action
(perform
Item.callMethod
"destroyRecord"
@@ -74,13 +78,7 @@
(action "refresh")
)
}}
@confirmTitle={{concat "Delete scope " list.item.id "?"}}
@confirmMessage="This will permanently delete this scope and all roles and credentials contained within"
@cancelButtonText="Cancel"
data-test-scope-delete="true"
>
Delete scope
</ConfirmAction>
data-test-scope-delete="true" />
</MenuLoader>
{{/if}}
</Item.menu>

View File

@@ -1,4 +1,4 @@
/* eslint-disable import/extensions */
import hbs from 'htmlbars-inline-precompile';
import { storiesOf } from '@storybook/ember';
import notes from './alert-popup.md';

View File

@@ -1,4 +1,4 @@
/* eslint-disable import/extensions */
import hbs from 'htmlbars-inline-precompile';
import { storiesOf } from '@storybook/ember';
import { withKnobs, select } from '@storybook/addon-knobs';

View File

@@ -1,4 +1,4 @@
/* eslint-disable import/extensions */
import hbs from 'htmlbars-inline-precompile';
import { storiesOf } from '@storybook/ember';
import { withKnobs, select } from '@storybook/addon-knobs';

View File

@@ -1,4 +1,4 @@
/* eslint-disable import/extensions */
import hbs from 'htmlbars-inline-precompile';
import { storiesOf } from '@storybook/ember';
import notes from './auth-form.md';

View File

@@ -1,4 +1,4 @@
/* eslint-disable import/extensions */
import hbs from 'htmlbars-inline-precompile';
import { storiesOf } from '@storybook/ember';
import { withKnobs, object } from '@storybook/addon-knobs';

View File

@@ -1,4 +1,4 @@
/* eslint-disable import/extensions */
import hbs from 'htmlbars-inline-precompile';
import { storiesOf } from '@storybook/ember';
import { withKnobs, object } from '@storybook/addon-knobs';

View File

@@ -1,4 +1,4 @@
/* eslint-disable import/extensions */
import hbs from 'htmlbars-inline-precompile';
import { storiesOf } from '@storybook/ember';
import { withKnobs, object } from '@storybook/addon-knobs';

View File

@@ -0,0 +1,102 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, click } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon';
module('Integration | Component | Confirm', function(hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function() {
this.set('id', 'foo');
this.set('title', 'Are you sure?');
this.set('message', 'You will not be able to recover this item later.');
this.set('triggerText', 'Click me!');
this.set('onConfirm', sinon.spy());
});
test('it renders', async function(assert) {
await render(hbs`
<Confirm as |c|>
<c.Message
@id={{id}}
@title={{title}}
@triggerText={{triggerText}}
@message={{message}}
@onConfirm={{onConfirm}}
/>
</Confirm>
`);
assert.dom('.confirm-wrapper').exists();
assert.dom('.confirm').containsText(this.triggerText);
});
test('does not show the confirmation message until it is triggered', async function(assert) {
await render(hbs`
<Confirm as |c|>
<c.Message
@id={{id}}
@title={{title}}
@triggerText={{triggerText}}
@message={{message}}
@onConfirm={{onConfirm}}
/>
</Confirm>
`);
assert.dom('.confirm-overlay').doesNotContainText(this.message);
await click('[data-test-confirm-action-trigger]');
assert.dom('.confirm-overlay').containsText(this.title);
assert.dom('.confirm-overlay').containsText(this.message);
});
test('it calls onConfirm when the confirm button is clicked', async function(assert) {
await render(hbs`
<Confirm as |c|>
<c.Message
@id={{id}}
@title={{title}}
@triggerText={{triggerText}}
@message={{message}}
@onConfirm={{onConfirm}}
/>
</Confirm>
`);
await click('[data-test-confirm-action-trigger]');
await click('[data-test-confirm-button=true]');
assert.ok(this.onConfirm.calledOnce);
});
test('it shows only the active triggers message', async function(assert) {
await render(hbs`
<Confirm as |c|>
<c.Message
@id={{id}}
@title={{title}}
@triggerText={{triggerText}}
@message={{message}}
@onConfirm={{onConfirm}}
/>
<c.Message
@id='bar'
@title='Wow'
@message='Bazinga!'
@onConfirm={{onConfirm}}
/>
</Confirm>
`);
await click(`[data-test-confirm-action-trigger=${this.id}]`);
assert.dom('.confirm-overlay').containsText(this.title);
assert.dom('.confirm-overlay').containsText(this.message);
await click('[data-test-confirm-cancel-button]');
await click("[data-test-confirm-action-trigger='bar']");
assert.dom('.confirm-overlay').containsText('Wow');
assert.dom('.confirm-overlay').containsText('Bazinga!');
});
});