mirror of https://github.com/hashicorp/consul
ui: Moves intentions listing and form into components (#7549)
Whilst we tried to do this with the smallest amount of changes possible, our acceptance tests for trying to submit a blank form started failing due to usage of `destroyRecord`, its seems that the correct way to achieve the same thing is to use `rollbackAttributes` instead. We changed that here and the tests pass once again. Furture work related to this will involve change the rest of the UI where we use `destroyRecord` to achieve the same thing, to use `rollbackAttributes` insteadpull/7344/head
parent
80960c9d54
commit
c20cff9bf5
|
@ -1,12 +1,12 @@
|
|||
<form>
|
||||
<form onsubmit={{action 'submit' _item}}>
|
||||
<fieldset>
|
||||
<div role="group">
|
||||
<fieldset>
|
||||
<h2>Source</h2>
|
||||
<label data-test-source-element class="type-text{{if item.error.SourceName ' has-error'}}">
|
||||
<label data-test-source-element class="type-text{{if _item.error.SourceName ' has-error'}}">
|
||||
<span>Source Service</span>
|
||||
<PowerSelectWithCreate
|
||||
@options={{services}}
|
||||
@options={{_services}}
|
||||
@searchField="Name"
|
||||
@selected={{SourceName}}
|
||||
@searchPlaceholder="Type service name"
|
||||
|
@ -23,10 +23,10 @@
|
|||
<em>Search for an existing service, write in a future one, or write in any Service URI.</em>
|
||||
</label>
|
||||
{{#if (env 'CONSUL_NSPACES_ENABLED')}}
|
||||
<label data-test-source-nspace class="type-text{{if item.error.SourceNS ' has-error'}}">
|
||||
<label data-test-source-nspace class="type-text{{if _item.error.SourceNS ' has-error'}}">
|
||||
<span>Source Namespace</span>
|
||||
<PowerSelectWithCreate
|
||||
@options={{nspaces}}
|
||||
@options={{_nspaces}}
|
||||
@searchField="Name"
|
||||
@selected={{SourceNS}}
|
||||
@searchPlaceholder="Type namespace name"
|
||||
|
@ -46,10 +46,10 @@
|
|||
</fieldset>
|
||||
<fieldset>
|
||||
<h2>Destination</h2>
|
||||
<label data-test-destination-element class="type-text{{if item.error.DestinationName ' has-error'}}">
|
||||
<label data-test-destination-element class="type-text{{if _item.error.DestinationName ' has-error'}}">
|
||||
<span>Destination Service</span>
|
||||
<PowerSelectWithCreate
|
||||
@options={{services}}
|
||||
@options={{_services}}
|
||||
@searchField="Name"
|
||||
@selected={{DestinationName}}
|
||||
@searchPlaceholder="Type service name"
|
||||
|
@ -66,10 +66,10 @@
|
|||
<em>Search for an existing service or write in a future one.</em>
|
||||
</label>
|
||||
{{#if (env 'CONSUL_NSPACES_ENABLED')}}
|
||||
<label data-test-destination-nspace class="type-text{{if item.error.DestinationNS ' has-error'}}">
|
||||
<label data-test-destination-nspace class="type-text{{if _item.error.DestinationNS ' has-error'}}">
|
||||
<span>Destination Namespace</span>
|
||||
<PowerSelectWithCreate
|
||||
@options={{nspaces}}
|
||||
@options={{_nspaces}}
|
||||
@searchField="Name"
|
||||
@selected={{DestinationNS}}
|
||||
@searchPlaceholder="Type namespace name"
|
||||
|
@ -88,30 +88,30 @@
|
|||
{{/if}}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div role="radiogroup" class={{if item.error.Action ' has-error'}}>
|
||||
<div role="radiogroup" class={{if _item.error.Action ' has-error'}}>
|
||||
{{#each (array 'allow' 'deny') as |intent|}}
|
||||
<label>
|
||||
<span>{{ capitalize intent }}</span>
|
||||
<input type="radio" name="Action" value="{{intent}}" checked={{if (eq item.Action intent) 'checked'}} onchange={{ action 'change' }}/>
|
||||
<input type="radio" name="Action" value="{{intent}}" checked={{if (eq _item.Action intent) 'checked'}} onchange={{ action 'change' }}/>
|
||||
</label>
|
||||
{{/each}}
|
||||
</div>
|
||||
<label class="type-text{{if item.error.Description ' has-error'}}">
|
||||
<label class="type-text{{if _item.error.Description ' has-error'}}">
|
||||
<span>Description (Optional)</span>
|
||||
<input type="text" name="Description" value="{{item.Description}}" placeholder="Description (Optional)" onchange={{action 'change'}} />
|
||||
<input type="text" name="Description" value="{{_item.Description}}" placeholder="Description (Optional)" onchange={{action 'change'}} />
|
||||
</label>
|
||||
</fieldset>
|
||||
<div>
|
||||
{{#if create }}
|
||||
<button type="submit" {{ action "create" item}} disabled={{if (or item.isPristine item.isInvalid) 'disabled'}}>Save</button>
|
||||
{{#if _item.isNew }}
|
||||
<button type="submit" disabled={{if (or _item.isPristine _item.isInvalid) 'disabled'}}>Save</button>
|
||||
{{ else }}
|
||||
<button type="submit" {{ action "update" item}} disabled={{if item.isInvalid 'disabled'}}>Save</button>
|
||||
<button type="submit" disabled={{if _item.isInvalid 'disabled'}}>Save</button>
|
||||
{{/if}}
|
||||
<button type="reset" {{ action "cancel" item}}>Cancel</button>
|
||||
{{# if (and item.ID (not-eq item.ID 'anonymous')) }}
|
||||
<button type="reset" onclick={{action oncancel _item}}>Cancel</button>
|
||||
{{# if (and _item.ID (not-eq _item.ID 'anonymous')) }}
|
||||
<ConfirmationDialog @message="Are you sure you want to delete this Intention?">
|
||||
<BlockSlot @name="action" as |confirm|>
|
||||
<button data-test-delete type="button" class="type-delete" {{action confirm 'delete' item parent}}>Delete</button>
|
||||
<button data-test-delete type="button" class="type-delete" {{action confirm ondelete _item}}>Delete</button>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="dialog" as |execute cancel message|>
|
||||
<DeleteConfirmation @message={{message}} @execute={{execute}} @cancel={{cancel}} />
|
|
@ -0,0 +1,113 @@
|
|||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { setProperties, set, get } from '@ember/object';
|
||||
import { assert } from '@ember/debug';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
dom: service('dom'),
|
||||
builder: service('form'),
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
this.form = this.builder.form('intention');
|
||||
},
|
||||
didReceiveAttrs: function() {
|
||||
this._super(...arguments);
|
||||
if (this.item && this.services && this.nspaces) {
|
||||
let services = this.services || [];
|
||||
let nspaces = this.nspaces || [];
|
||||
let source = services.findBy('Name', this.item.SourceName);
|
||||
if (!source) {
|
||||
source = { Name: this.item.SourceName };
|
||||
services = [source].concat(services);
|
||||
}
|
||||
let destination = services.findBy('Name', this.item.DestinationName);
|
||||
if (!destination) {
|
||||
destination = { Name: this.item.DestinationName };
|
||||
services = [destination].concat(services);
|
||||
}
|
||||
|
||||
let sourceNS = nspaces.findBy('Name', this.item.SourceNS);
|
||||
if (!sourceNS) {
|
||||
sourceNS = { Name: this.item.SourceNS };
|
||||
nspaces = [sourceNS].concat(nspaces);
|
||||
}
|
||||
let destinationNS = this.nspaces.findBy('Name', this.item.DestinationNS);
|
||||
if (!destinationNS) {
|
||||
destinationNS = { Name: this.item.DestinationNS };
|
||||
nspaces = [destinationNS].concat(nspaces);
|
||||
}
|
||||
// TODO: Use this.{item,services} when we have this.args
|
||||
setProperties(this, {
|
||||
_item: this.form.setData(this.item).getData(),
|
||||
_services: services,
|
||||
_nspaces: nspaces,
|
||||
SourceName: source,
|
||||
DestinationName: destination,
|
||||
SourceNS: sourceNS,
|
||||
DestinationNS: destinationNS,
|
||||
});
|
||||
} else {
|
||||
assert('@item, @services and @nspaces are required arguments', false);
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
createNewLabel: function(template, term) {
|
||||
return template.replace(/{{term}}/g, term);
|
||||
},
|
||||
isUnique: function(term) {
|
||||
return !this._services.findBy('Name', term);
|
||||
},
|
||||
submit: function(item, e) {
|
||||
e.preventDefault();
|
||||
this.onsubmit(...arguments);
|
||||
},
|
||||
change: function(e, value, item) {
|
||||
const event = this.dom.normalizeEvent(e, value);
|
||||
const form = this.form;
|
||||
const target = event.target;
|
||||
|
||||
let name, selected, match;
|
||||
switch (target.name) {
|
||||
case 'SourceName':
|
||||
case 'DestinationName':
|
||||
case 'SourceNS':
|
||||
case 'DestinationNS':
|
||||
name = selected = target.value;
|
||||
// Names can be selected Service EmberObjects or typed in strings
|
||||
// if its not a string, use the `Name` from the Service EmberObject
|
||||
if (typeof name !== 'string') {
|
||||
name = get(target.value, 'Name');
|
||||
}
|
||||
// mutate the value with the string name
|
||||
// which will be handled by the form
|
||||
target.value = name;
|
||||
// these are 'non-form' variables so not on `item`
|
||||
// these variables also exist in the template so we know
|
||||
// the current selection
|
||||
// basically the difference between
|
||||
// `item.DestinationName` and just `DestinationName`
|
||||
// see if the name is already in the list
|
||||
match = this._services.filterBy('Name', name);
|
||||
if (match.length === 0) {
|
||||
// if its not make a new 'fake' Service that doesn't exist yet
|
||||
// and add it to the possible services to make an intention between
|
||||
selected = { Name: name };
|
||||
switch (target.name) {
|
||||
case 'SourceName':
|
||||
case 'DestinationName':
|
||||
set(this, '_services', [selected].concat(this._services.toArray()));
|
||||
break;
|
||||
case 'SourceNS':
|
||||
case 'DestinationNS':
|
||||
set(this, '_nspaces', [selected].concat(this._nspaces.toArray()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
set(this, target.name, selected);
|
||||
break;
|
||||
}
|
||||
form.handleEvent(event);
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
<TabularCollection class="consul-intention-list" @items={{items}} as |item index|>
|
||||
<BlockSlot @name="header">
|
||||
<th>Source</th>
|
||||
<th> </th>
|
||||
<th>Destination</th>
|
||||
<th>Precedence</th>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="row">
|
||||
<td class="source" data-test-intention={{item.ID}}>
|
||||
<a href={{href-to 'dc.intentions.edit' item.ID}} data-test-intention-source={{item.SourceName}}>
|
||||
{{#if (eq item.SourceName '*') }}
|
||||
All Services (*)
|
||||
{{else}}
|
||||
{{item.SourceName}}
|
||||
{{/if}}
|
||||
{{! TODO: slugify }}
|
||||
<em class={{concat 'nspace-' (or item.SourceNS 'default')}}>{{or item.SourceNS 'default'}}</em>
|
||||
</a>
|
||||
</td>
|
||||
<td class="intent-{{item.Action}}" data-test-intention-action="{{item.Action}}">
|
||||
<strong>{{item.Action}}</strong>
|
||||
</td>
|
||||
<td class="destination" data-test-intention-destination="{{item.DestinationName}}">
|
||||
<span>
|
||||
{{#if (eq item.DestinationName '*') }}
|
||||
All Services (*)
|
||||
{{else}}
|
||||
{{item.DestinationName}}
|
||||
{{/if}}
|
||||
{{! TODO: slugify }}
|
||||
<em class={{concat 'nspace-' (or item.DestinationNS 'default')}}>{{or item.DestinationNS 'default'}}</em>
|
||||
</span>
|
||||
</td>
|
||||
<td class="precedence">
|
||||
{{item.Precedence}}
|
||||
</td>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions" as |index change checked|>
|
||||
<PopoverMenu @expanded={{if (eq checked index) true false}} @onchange={{action change index}} @keyboardAccess={{false}}>
|
||||
<BlockSlot @name="trigger">
|
||||
More
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="menu" as |confirm send keypressClick change|>
|
||||
<li role="none">
|
||||
<a role="menuitem" tabindex="-1" href={{href-to 'dc.intentions.edit' item.ID}}>Edit</a>
|
||||
</li>
|
||||
<li role="none" class="dangerous">
|
||||
<label for={{confirm}} role="menuitem" tabindex="-1" onkeypress={{keypressClick}} data-test-delete>Delete</label>
|
||||
<div role="menu">
|
||||
<div class="confirmation-alert warning">
|
||||
<div>
|
||||
<header>
|
||||
Confirm Delete
|
||||
</header>
|
||||
<p>
|
||||
Are you sure you want to delete this intention?
|
||||
</p>
|
||||
</div>
|
||||
<ul>
|
||||
<li class="dangerous">
|
||||
<button tabindex="-1" type="button" class="type-delete" onclick={{queue (action change) (action ondelete item)}}>Delete</button>
|
||||
</li>
|
||||
<li>
|
||||
<label for={{confirm}}>Cancel</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</BlockSlot>
|
||||
</PopoverMenu>
|
||||
</BlockSlot>
|
||||
</TabularCollection>
|
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -1,100 +1,8 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { get, set } from '@ember/object';
|
||||
export default Controller.extend({
|
||||
dom: service('dom'),
|
||||
builder: service('form'),
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
this.form = this.builder.form('intention');
|
||||
},
|
||||
setProperties: function(model) {
|
||||
let source = model.services.findBy('Name', model.item.SourceName);
|
||||
|
||||
if (!source) {
|
||||
source = { Name: model.item.SourceName };
|
||||
model.services = [source].concat(model.services);
|
||||
}
|
||||
let destination = model.services.findBy('Name', model.item.DestinationName);
|
||||
if (!destination) {
|
||||
destination = { Name: model.item.DestinationName };
|
||||
model.services = [destination].concat(model.services);
|
||||
}
|
||||
|
||||
let sourceNS = model.nspaces.findBy('Name', model.item.SourceNS);
|
||||
if (!sourceNS) {
|
||||
sourceNS = { Name: model.item.SourceNS };
|
||||
model.nspaces = [sourceNS].concat(model.nspaces);
|
||||
}
|
||||
let destinationNS = model.nspaces.findBy('Name', model.item.DestinationNS);
|
||||
if (!destinationNS) {
|
||||
destinationNS = { Name: model.item.DestinationNS };
|
||||
model.nspaces = [destinationNS].concat(model.nspaces);
|
||||
}
|
||||
this._super({
|
||||
...model,
|
||||
...{
|
||||
item: this.form.setData(model.item).getData(),
|
||||
SourceName: source,
|
||||
DestinationName: destination,
|
||||
SourceNS: sourceNS,
|
||||
DestinationNS: destinationNS,
|
||||
},
|
||||
});
|
||||
},
|
||||
actions: {
|
||||
createNewLabel: function(template, term) {
|
||||
return template.replace(/{{term}}/g, term);
|
||||
},
|
||||
isUnique: function(term) {
|
||||
return !this.services.findBy('Name', term);
|
||||
},
|
||||
change: function(e, value, item) {
|
||||
const event = this.dom.normalizeEvent(e, value);
|
||||
const form = this.form;
|
||||
const target = event.target;
|
||||
|
||||
let name, selected, match;
|
||||
switch (target.name) {
|
||||
case 'SourceName':
|
||||
case 'DestinationName':
|
||||
case 'SourceNS':
|
||||
case 'DestinationNS':
|
||||
name = selected = target.value;
|
||||
// Names can be selected Service EmberObjects or typed in strings
|
||||
// if its not a string, use the `Name` from the Service EmberObject
|
||||
if (typeof name !== 'string') {
|
||||
name = get(target.value, 'Name');
|
||||
}
|
||||
// mutate the value with the string name
|
||||
// which will be handled by the form
|
||||
target.value = name;
|
||||
// these are 'non-form' variables so not on `item`
|
||||
// these variables also exist in the template so we know
|
||||
// the current selection
|
||||
// basically the difference between
|
||||
// `item.DestinationName` and just `DestinationName`
|
||||
// see if the name is already in the list
|
||||
match = this.services.filterBy('Name', name);
|
||||
if (match.length === 0) {
|
||||
// if its not make a new 'fake' Service that doesn't exist yet
|
||||
// and add it to the possible services to make an intention between
|
||||
selected = { Name: name };
|
||||
switch (target.name) {
|
||||
case 'SourceName':
|
||||
case 'DestinationName':
|
||||
set(this, 'services', [selected].concat(this.services.toArray()));
|
||||
break;
|
||||
case 'SourceNS':
|
||||
case 'DestinationNS':
|
||||
set(this, 'nspaces', [selected].concat(this.nspaces.toArray()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
set(this, target.name, selected);
|
||||
break;
|
||||
}
|
||||
form.handleEvent(event);
|
||||
route: function() {
|
||||
this.send(...arguments);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -47,4 +47,9 @@ export default Controller.extend(WithSearching, WithFiltering, WithEventSource,
|
|||
filter: function(item, { s = '', currentFilter = '' }) {
|
||||
return currentFilter === '' || get(item, 'Action') === currentFilter;
|
||||
},
|
||||
actions: {
|
||||
route: function() {
|
||||
this.send(...arguments);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -42,7 +42,7 @@ export default Route.extend(WithIntentionActions, {
|
|||
},
|
||||
deactivate: function() {
|
||||
if (get(this.item, 'isNew')) {
|
||||
this.item.destroyRecord();
|
||||
this.item.rollbackAttributes();
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
@import './consul-intention-list/index';
|
||||
.consul-intention-list {
|
||||
@extend %consul-intention-list;
|
||||
}
|
||||
@media #{$--lt-wide-table} {
|
||||
%consul-intention-list tr > :nth-last-child(2) {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
@import './skin';
|
||||
@import './layout';
|
||||
%consul-intention-list td.destination {
|
||||
@extend %tbody-th;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
%consul-intention-list td strong {
|
||||
visibility: hidden;
|
||||
}
|
||||
%consul-intention-list td.intent-allow strong::before {
|
||||
@extend %with-arrow-right-color-icon, %as-pseudo;
|
||||
background-size: 24px;
|
||||
}
|
||||
%consul-intention-list td.intent-deny strong::before {
|
||||
@extend %with-deny-color-icon, %as-pseudo;
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
@import './notice';
|
||||
@import './sort-control';
|
||||
@import './discovery-chain';
|
||||
@import './consul-intention-list';
|
||||
|
||||
@import './tabular-details';
|
||||
@import './tabular-collection';
|
||||
|
|
|
@ -82,9 +82,6 @@ th span em {
|
|||
tr > .actions {
|
||||
display: none;
|
||||
}
|
||||
html.template-intention.template-list tr > :nth-last-child(2) {
|
||||
display: none;
|
||||
}
|
||||
html.template-service.template-list tr > :last-child {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
html.template-intention.template-list td strong {
|
||||
visibility: hidden;
|
||||
}
|
||||
html.template-intention.template-list td.intent-allow strong::before {
|
||||
@extend %with-arrow-right-color-icon, %as-pseudo;
|
||||
background-size: 24px;
|
||||
}
|
||||
html.template-intention.template-list td.intent-deny strong::before {
|
||||
@extend %with-deny-color-icon, %as-pseudo;
|
||||
}
|
||||
html.template-intention.template-list td.destination {
|
||||
@extend %tbody-th;
|
||||
}
|
|
@ -44,6 +44,13 @@
|
|||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{ partial 'dc/intentions/form'}}
|
||||
<ConsulIntentionForm
|
||||
@item={{item}}
|
||||
@services={{services}}
|
||||
@nspaces={{nspaces}}
|
||||
@ondelete={{action "route" "delete"}}
|
||||
@onsubmit={{action "route" (if item.isNew "create" "update")}}
|
||||
@oncancel={{action "route" "cancel"}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
</AppView>
|
|
@ -20,79 +20,10 @@
|
|||
<BlockSlot @name="content">
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<TabularCollection @route="dc.intentions.edit" @key="SourceName" @items={{filtered}} as |item index|>
|
||||
<BlockSlot @name="header">
|
||||
<th>Source</th>
|
||||
<th> </th>
|
||||
<th>Destination</th>
|
||||
<th>Precedence</th>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="row">
|
||||
<td class="source" data-test-intention="{{item.ID}}">
|
||||
<a href={{href-to 'dc.intentions.edit' item.ID}} data-test-intention-source="{{item.SourceName}}">
|
||||
{{#if (eq item.SourceName '*') }}
|
||||
All Services (*)
|
||||
{{else}}
|
||||
{{item.SourceName}}
|
||||
{{/if}}
|
||||
{{! TODO: slugify }}
|
||||
<em class={{concat 'nspace-' (or item.SourceNS 'default')}}>{{or item.SourceNS 'default'}}</em>
|
||||
</a>
|
||||
</td>
|
||||
<td class="intent-{{item.Action}}" data-test-intention-action="{{item.Action}}">
|
||||
<strong>{{item.Action}}</strong>
|
||||
</td>
|
||||
<td class="destination" data-test-intention-destination="{{item.DestinationName}}">
|
||||
<span>
|
||||
{{#if (eq item.DestinationName '*') }}
|
||||
All Services (*)
|
||||
{{else}}
|
||||
{{item.DestinationName}}
|
||||
{{/if}}
|
||||
{{! TODO: slugify }}
|
||||
<em class={{concat 'nspace-' (or item.DestinationNS 'default')}}>{{or item.DestinationNS 'default'}}</em>
|
||||
</span>
|
||||
</td>
|
||||
<td class="precedence">
|
||||
{{item.Precedence}}
|
||||
</td>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions" as |index change checked|>
|
||||
<PopoverMenu @expanded={{if (eq checked index) true false}} @onchange={{action change index}} @keyboardAccess={{false}}>
|
||||
<BlockSlot @name="trigger">
|
||||
More
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="menu" as |confirm send keypressClick|>
|
||||
<li role="none">
|
||||
<a role="menuitem" tabindex="-1" href={{href-to 'dc.intentions.edit' item.ID}}>Edit</a>
|
||||
</li>
|
||||
<li role="none" class="dangerous">
|
||||
<label for={{confirm}} role="menuitem" tabindex="-1" onkeypress={{keypressClick}} data-test-delete>Delete</label>
|
||||
<div role="menu">
|
||||
<div class="confirmation-alert warning">
|
||||
<div>
|
||||
<header>
|
||||
Confirm Delete
|
||||
</header>
|
||||
<p>
|
||||
Are you sure you want to delete this intention?
|
||||
</p>
|
||||
</div>
|
||||
<ul>
|
||||
<li class="dangerous">
|
||||
<button tabindex="-1" type="button" class="type-delete" onclick={{action send 'delete' item}}>Delete</button>
|
||||
</li>
|
||||
<li>
|
||||
<label for={{confirm}}>Cancel</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</BlockSlot>
|
||||
</PopoverMenu>
|
||||
</BlockSlot>
|
||||
</TabularCollection>
|
||||
<ConsulIntentionList
|
||||
@items={{filtered}}
|
||||
@ondelete={{action "route" "delete"}}
|
||||
/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<p>
|
||||
|
|
Loading…
Reference in New Issue