mirror of https://github.com/status-im/consul.git
ui: Add Optgroups and selectedItems to multiple select dropdown and use (#8476)
* ui: Switch selects to use more HTML-like approach for optgroups * Add KV comparator * Use new option/optgroup approach for sort/select * Fix up tests for new order of menu items
This commit is contained in:
parent
7f711bb68f
commit
a686de0414
|
@ -27,7 +27,12 @@
|
|||
</a>
|
||||
{{/let}}
|
||||
{{else}}
|
||||
<button role="menuitem" tabindex="-1" type="button" onclick={{action this.onclick}}>
|
||||
<button
|
||||
type="button"
|
||||
role="menuitem"
|
||||
aria-selected={{if selected 'true'}}
|
||||
tabindex="-1"
|
||||
onclick={{action (or this.onclick (noop))}}>
|
||||
<YieldSlot @name="label">
|
||||
{{yield}}
|
||||
</YieldSlot>
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
<div class="popover-select" ...attributes>
|
||||
<PopoverMenu as |components menu|>
|
||||
<PopoverMenu @position={{or position "left"}} class="popover-select" ...attributes as |components menu|>
|
||||
{{yield}}
|
||||
{{#let
|
||||
(component 'popover-select/optgroup' components=components)
|
||||
(component 'popover-select/option'
|
||||
select=this components=components
|
||||
onclick=(queue
|
||||
(action "click")
|
||||
(if multiple (noop) menu.toggle)
|
||||
)
|
||||
)
|
||||
as |Optgroup Option|
|
||||
}}
|
||||
<BlockSlot @name="trigger">
|
||||
<span>
|
||||
{{selected.value}}
|
||||
</span>
|
||||
<YieldSlot @name="selected">
|
||||
{{yield (hash
|
||||
Optgroup=Optgroup
|
||||
Option=Option
|
||||
)}}
|
||||
</YieldSlot>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="menu">
|
||||
{{#let components.MenuItem components.MenuSeparator as |MenuItem MenuSeparator|}}
|
||||
<MenuSeparator>
|
||||
<BlockSlot @name="label">
|
||||
{{title}}
|
||||
<YieldSlot @name="options">
|
||||
{{yield (hash
|
||||
Optgroup=Optgroup
|
||||
Option=Option
|
||||
)}}
|
||||
</YieldSlot>
|
||||
</BlockSlot>
|
||||
</MenuSeparator>
|
||||
{{#each options as |option|}}
|
||||
<MenuItem
|
||||
class={{if (eq selected.key option.key) 'is-active'}}
|
||||
@onclick={{action (queue (action 'change' option) (if multiple (noop) menu.toggle))}}
|
||||
>
|
||||
<BlockSlot @name="label">
|
||||
{{option.value}}
|
||||
</BlockSlot>
|
||||
</MenuItem>
|
||||
{{/each}}
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverMenu>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,53 @@
|
|||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Slotted from 'block-slots';
|
||||
|
||||
export default Component.extend({
|
||||
export default Component.extend(Slotted, {
|
||||
tagName: '',
|
||||
dom: service('dom'),
|
||||
multiple: false,
|
||||
subtractive: true,
|
||||
onchange: function() {},
|
||||
addOption: function(option) {
|
||||
if (typeof this._options === 'undefined') {
|
||||
this._options = new Set();
|
||||
}
|
||||
if (this.subtractive) {
|
||||
if (!option.selected) {
|
||||
this._options.add(option.value);
|
||||
}
|
||||
} else {
|
||||
if (option.selected) {
|
||||
this._options.add(option.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
removeOption: function(option) {
|
||||
this._options.delete(option.value);
|
||||
},
|
||||
actions: {
|
||||
click: function(e, value) {
|
||||
let options = [value];
|
||||
if (this.multiple) {
|
||||
if (this._options.has(value)) {
|
||||
this._options.delete(value);
|
||||
} else {
|
||||
this._options.add(value);
|
||||
}
|
||||
options = this._options;
|
||||
}
|
||||
this.onchange(
|
||||
this.dom.setEventTargetProperties(e, {
|
||||
selected: target => value,
|
||||
selectedItems: target => {
|
||||
const opts = [...options];
|
||||
if (opts.length > 0) {
|
||||
return opts.join(',');
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
},
|
||||
change: function(option, e) {
|
||||
this.onchange(this.dom.setEventTargetProperty(e, 'selected', selected => option));
|
||||
},
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{{#let components.MenuSeparator as |MenuSeparator|}}
|
||||
<MenuSeparator>
|
||||
<BlockSlot @name="label">
|
||||
{{label}}
|
||||
</BlockSlot>
|
||||
</MenuSeparator>
|
||||
{{yield}}
|
||||
{{/let}}
|
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
{{#let components.MenuItem as |MenuItem|}}
|
||||
<MenuItem
|
||||
class={{if selected 'is-active'}}
|
||||
@onclick={{action 'click'}}
|
||||
@selected={{selected}}
|
||||
>
|
||||
<BlockSlot @name="label">
|
||||
{{yield}}
|
||||
</BlockSlot>
|
||||
</MenuItem>
|
||||
{{/let}}
|
|
@ -0,0 +1,20 @@
|
|||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
dom: service('dom'),
|
||||
didInsertElement: function() {
|
||||
this._super(...arguments);
|
||||
this.select.addOption(this);
|
||||
},
|
||||
willDestroyElement: function() {
|
||||
this._super(...arguments);
|
||||
this.select.removeOption(this);
|
||||
},
|
||||
actions: {
|
||||
click: function(e) {
|
||||
this.onclick(e, this.value);
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,24 +1,23 @@
|
|||
{{yield}}
|
||||
<form class={{concat 'filter-bar' (if (eq secondary 'sort') ' with-sort')}} ...attributes>
|
||||
{{#yield-slot name="primary"}}
|
||||
<fieldset>
|
||||
{{yield}}
|
||||
</fieldset>
|
||||
{{else}}
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{value}}
|
||||
@placeholder={{or placeholder 'Search'}}
|
||||
/>
|
||||
{{/yield-slot}}
|
||||
{{#yield-slot name="secondary"}}
|
||||
<fieldset>
|
||||
{{yield}}
|
||||
</fieldset>
|
||||
{{else}}
|
||||
{{#if options}}
|
||||
{{#if (eq secondary 'sort')}}
|
||||
<fieldset>
|
||||
<PopoverSelect
|
||||
data-popover-select
|
||||
@selected={{selected}}
|
||||
@options={{options}}
|
||||
@onchange={{action onchange}}
|
||||
@title="Sort By"
|
||||
/>
|
||||
</fieldset>
|
||||
{{else}}
|
||||
<RadioGroup
|
||||
@keyboardAccess={{true}}
|
||||
|
|
|
@ -5,6 +5,7 @@ import { alias } from '@ember/object/computed';
|
|||
export default Controller.extend({
|
||||
items: alias('item.Nodes'),
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import Controller from '@ember/controller';
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
filterBy: {
|
||||
as: 'action',
|
||||
},
|
||||
sortBy: 'sort',
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import service from 'consul-ui/sort/comparators/service';
|
||||
import kv from 'consul-ui/sort/comparators/kv';
|
||||
import check from 'consul-ui/sort/comparators/check';
|
||||
import intention from 'consul-ui/sort/comparators/intention';
|
||||
import token from 'consul-ui/sort/comparators/token';
|
||||
|
@ -11,6 +12,7 @@ export function initialize(container) {
|
|||
const Sort = container.resolveRegistration('service:sort');
|
||||
const comparators = {
|
||||
service: service(),
|
||||
kv: kv(),
|
||||
check: check(),
|
||||
intention: intention(),
|
||||
token: token(),
|
||||
|
|
|
@ -69,6 +69,24 @@ export default Service.extend({
|
|||
},
|
||||
});
|
||||
},
|
||||
setEventTargetProperties: function(e, propObj) {
|
||||
const target = e.target;
|
||||
return new Proxy(e, {
|
||||
get: function(obj, prop, receiver) {
|
||||
if (prop === 'target') {
|
||||
return new Proxy(target, {
|
||||
get: function(obj, prop, receiver) {
|
||||
if (typeof propObj[prop] !== 'undefined') {
|
||||
return propObj[prop](e.target);
|
||||
}
|
||||
return target[prop];
|
||||
},
|
||||
});
|
||||
}
|
||||
return Reflect.get(...arguments);
|
||||
},
|
||||
});
|
||||
},
|
||||
listeners: createListeners,
|
||||
root: function() {
|
||||
return this.doc.documentElement;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export default () => key => {
|
||||
return key;
|
||||
};
|
|
@ -5,7 +5,7 @@
|
|||
border-bottom: $decor-border-200;
|
||||
}
|
||||
%app-view-content h2,
|
||||
%app-view-content fieldset {
|
||||
%app-view-content form:not(.filter-bar) fieldset {
|
||||
border-bottom: $decor-border-200;
|
||||
}
|
||||
%app-view-content fieldset h2 {
|
||||
|
@ -22,7 +22,7 @@
|
|||
}
|
||||
%app-view-title,
|
||||
%app-view-content h2,
|
||||
%app-view-content fieldset {
|
||||
%app-view-content form:not(.filter-bar) fieldset {
|
||||
border-color: $gray-200;
|
||||
}
|
||||
// We know that any sibling navs might have a top border
|
||||
|
|
|
@ -5,7 +5,6 @@ a.type-create {
|
|||
// TODO: Once we move action-groups to use aria menu we can get rid of
|
||||
// some of this and just use not(aria-haspopup)
|
||||
button[type='reset'],
|
||||
%app-view-content form button[type='button']:not([aria-haspopup='menu']),
|
||||
header .actions button[type='button']:not(.copy-btn),
|
||||
button.type-cancel,
|
||||
html.template-error div > a {
|
||||
|
|
|
@ -4,14 +4,7 @@
|
|||
{{title 'Access Controls'}}
|
||||
{{/if}}
|
||||
|
||||
{{#let (selectable-key-values
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
selected=sortBy
|
||||
)
|
||||
as |sort|
|
||||
}}
|
||||
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
<AppView
|
||||
@class="policy list"
|
||||
@loading={{isLoading}}
|
||||
|
@ -45,15 +38,41 @@
|
|||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@secondary="sort"
|
||||
@selected={{sort.selected}}
|
||||
@options={{sort.items}}
|
||||
@onchange={{action (mut sortBy) value='target.selected.key'}}
|
||||
/>
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (sort-by (comparator 'policy' sort.selected.key) items) as |sorted|}}
|
||||
{{#let (sort-by (comparator 'policy' sort) items) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'policy' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulPolicyList
|
||||
|
|
|
@ -4,16 +4,7 @@
|
|||
{{title 'Access Controls'}}
|
||||
{{/if}}
|
||||
|
||||
{{#let (selectable-key-values
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
(array "CreateIndex:asc" "Newest to oldest")
|
||||
(array "CreateIndex:desc" "Oldest to newest")
|
||||
selected=sortBy
|
||||
)
|
||||
as |sort|
|
||||
}}
|
||||
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
<AppView
|
||||
@class="role list"
|
||||
@loading={{isLoading}}
|
||||
|
@ -47,15 +38,47 @@
|
|||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@secondary="sort"
|
||||
@selected={{sort.selected}}
|
||||
@options={{sort.items}}
|
||||
@onchange={{action (mut sortBy) value='target.selected.key'}}
|
||||
/>
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
(array "CreateIndex:desc" "Newest to oldest")
|
||||
(array "CreateIndex:asc" "Oldest to newest")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Creation">
|
||||
<Option @value="CreateIndex:desc" @selected={{eq "CreateIndex:desc" sort}}>Newest to oldest</Option>
|
||||
<Option @value="CreateIndex:asc" @selected={{eq "CreateIndex:asc" sort}}>Oldest to newest</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (sort-by (comparator 'role' sort.selected.key) items) as |sorted|}}
|
||||
{{#let (sort-by (comparator 'role' sort) items) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'role' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulRoleList
|
||||
|
|
|
@ -4,13 +4,7 @@
|
|||
{{title 'Access Controls'}}
|
||||
{{/if}}
|
||||
|
||||
{{#let (selectable-key-values
|
||||
(array "CreateTime:desc" "Newest to oldest")
|
||||
(array "CreateTime:asc" "Oldest to newest")
|
||||
selected=sortBy
|
||||
)
|
||||
as |sort|
|
||||
}}
|
||||
{{#let (or sortBy "CreateTime:desc") as |sort|}}
|
||||
<AppView
|
||||
@class="token list"
|
||||
@loading={{isLoading}}
|
||||
|
@ -42,21 +36,46 @@
|
|||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0)}}
|
||||
<SearchBar
|
||||
data-test-intention-filter="true"
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@secondary="sort"
|
||||
@selected={{sort.selected}}
|
||||
@options={{sort.items}}
|
||||
@onchange={{action (mut sortBy) value='target.selected.key'}}
|
||||
/>
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "CreateTime:desc" "Newest to oldest")
|
||||
(array "CreateTime:asc" "Oldest to newest")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Creation">
|
||||
<Option @value="CreateTime:desc" @selected={{eq "CreateTime:desc" sort}}>Newest to oldest</Option>
|
||||
<Option @value="CreateTime:asc" @selected={{eq "CreateTime:asc" sort}}>Oldest to newest</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#if (token/is-legacy items)}}
|
||||
<p data-test-notification-update class="notice info"><strong>Update.</strong> We have upgraded our ACL System to allow the creation of reusable policies that can be applied to tokens. Read more about the changes and how to upgrade legacy tokens in our <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
|
||||
{{/if}}
|
||||
{{#let (sort-by (comparator 'token' sort.selected.key) items) as |sorted|}}
|
||||
{{#let (sort-by (comparator 'token' sort) items) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'token' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulTokenList
|
||||
|
|
|
@ -6,21 +6,7 @@
|
|||
</BlockSlot>
|
||||
|
||||
<BlockSlot @name="loaded">
|
||||
|
||||
{{#let (selectable-key-values
|
||||
(array "Action:asc" "Allow to Deny")
|
||||
(array "Action:desc" "Deny to Allow")
|
||||
(array "SourceName:asc" "Source: A to Z")
|
||||
(array "SourceName:desc" "Source: Z to A")
|
||||
(array "DestinationName:asc" "Destination: A to Z")
|
||||
(array "DestinationName:desc" "Destination: Z to A")
|
||||
(array "Precedence:asc" "Precedence: Asc")
|
||||
(array "Precedence:desc" "Precedence: Desc")
|
||||
selected=sortBy
|
||||
)
|
||||
as |sort|
|
||||
}}
|
||||
|
||||
{{#let (or sortBy "Action:asc") as |sort|}}
|
||||
<AppView @class="intention list">
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
|
@ -34,18 +20,61 @@
|
|||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt api.data.length 0) }}
|
||||
<SearchBar
|
||||
data-test-intention-filter="true"
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@secondary="sort"
|
||||
@selected={{sort.selected}}
|
||||
@options={{sort.items}}
|
||||
@onchange={{action (mut sortBy) value='target.selected.key'}}
|
||||
/>
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Action:asc" "Allow to Deny")
|
||||
(array "Action:desc" "Deny to Allow")
|
||||
(array "SourceName:asc" "Source: A to Z")
|
||||
(array "SourceName:desc" "Source: Z to A")
|
||||
(array "DestinationName:asc" "Destination: A to Z")
|
||||
(array "DestinationName:desc" "Destination: Z to A")
|
||||
(array "Precedence:asc" "Precedence: Ascending")
|
||||
(array "Precedence:desc" "Precedence: Descending")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Permission">
|
||||
<Option @value="Action:asc" @selected={{eq "Action:asc" sort}}>Allow to Deny</Option>
|
||||
<Option @value="Action:desc" @selected={{eq "Action:desc" sort}}>Deny to Allow</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Source">
|
||||
<Option @value="SourceName:asc" @selected={{eq "SourceName:asc" sort}}>A to Z</Option>
|
||||
<Option @value="SourceName:desc" @selected={{eq "SourceName:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Destination">
|
||||
<Option @value="DestinationName:asc" @selected={{eq "DestinationName:asc" sort}}>A to Z</Option>
|
||||
<Option @value="DestinationName:desc" @selected={{eq "DestinationName:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Precedence">
|
||||
<Option @value="Precedence:asc" @selected={{eq "Precedence:asc" sort}}>Ascending</Option>
|
||||
<Option @value="Precedence:desc" @selected={{eq "Precedence:desc" sort}}>Descending</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (sort-by (comparator 'intention' sort.selected.key) api.data) as |sorted|}}
|
||||
{{#let (sort-by (comparator 'intention' sort) api.data) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'intention' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="content" as |filtered|>
|
||||
<ConsulIntentionList
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{{title 'Key/Value'}}
|
||||
{{#let (or sortBy "isFolder:asc") as |sort|}}
|
||||
<AppView @class="kv list">
|
||||
<BlockSlot @name="breadcrumbs">
|
||||
<ol>
|
||||
|
@ -23,10 +24,45 @@
|
|||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<SearchBar
|
||||
@placeholder="Search by name"
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
/>
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Key:asc" "A to Z")
|
||||
(array "Key:desc" "Z to A")
|
||||
(array "isFolder:desc" "Folders to Keys")
|
||||
(array "isFolder:asc" "Keys to Folders")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Key:asc" @selected={{eq "Key:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Key:desc" @selected={{eq "Key:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Type">
|
||||
<Option @value="isFolder:desc" @selected={{eq "isFolder:desc" sort}}>Folders to Keys</Option>
|
||||
<Option @value="isFolder:asc" @selected={{eq "isFolder:asc" sort}}>Keys to Folders</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
|
@ -37,10 +73,11 @@
|
|||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<ChangeableSet @dispatcher={{searchable 'kv' items}} @terms={{search}}>
|
||||
{{#let (sort-by (comparator 'kv' sort) items) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'kv' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="content" as |filtered|>
|
||||
<ConsulKvList
|
||||
@items={{sort-by "isFolder:desc" "Key:asc" filtered}}
|
||||
@items={{filtered}}
|
||||
@parent={{parent}}
|
||||
@ondelete={{refresh-route}}
|
||||
>
|
||||
|
@ -75,5 +112,7 @@
|
|||
</ConsulKvList>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
|
@ -1,11 +1,5 @@
|
|||
{{title 'Namespaces'}}
|
||||
{{#let (selectable-key-values
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
selected=sortBy
|
||||
)
|
||||
as |sort|
|
||||
}}
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
<EventSource @src={{items}} />
|
||||
<AppView @class="nspace list" @loading={{isLoading}}>
|
||||
<BlockSlot @name="notification" as |status type subject|>
|
||||
|
@ -24,15 +18,41 @@
|
|||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@secondary="sort"
|
||||
@selected={{sort.selected}}
|
||||
@options={{sort.items}}
|
||||
@onchange={{action (mut sortBy) value='target.selected.key'}}
|
||||
/>
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (sort-by (comparator 'nspace' sort.selected.key) items) as |sorted|}}
|
||||
{{#let (sort-by (comparator 'nspace' sort) items) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'nspace' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulNspaceList
|
||||
|
|
|
@ -1,14 +1,6 @@
|
|||
{{title 'Services'}}
|
||||
<EventSource @src={{items}} />
|
||||
{{#let (selectable-key-values
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
(array "Status:asc" "Unhealthy to Healthy")
|
||||
(array "Status:desc" "Healthy to Unhealthy")
|
||||
selected=sortBy
|
||||
)
|
||||
as |sort|
|
||||
}}
|
||||
{{#let (or sortBy "Name:asc") as |sort|}}
|
||||
<AppView @class="service list">
|
||||
<BlockSlot @name="notification" as |status type|>
|
||||
{{partial 'dc/services/notifications'}}
|
||||
|
@ -24,15 +16,47 @@
|
|||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@secondary="sort"
|
||||
@selected={{sort.selected}}
|
||||
@options={{sort.items}}
|
||||
@onchange={{action (mut sortBy) value='target.selected.key'}}
|
||||
/>
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
(array "Status:asc" "Unhealthy to Healthy")
|
||||
(array "Status:desc" "Healthy to Unhealthy")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Health Status">
|
||||
<Option @value="Status:asc" @selected={{eq "Status:asc" sort}}>Unhealthy to Healthy</Option>
|
||||
<Option @value="Status:desc" @selected={{eq "Status:desc" sort}}>Healthy to Unhealthy</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Service Name">
|
||||
<Option @value="Name:asc" @selected={{eq "Name:asc" sort}}>A to Z</Option>
|
||||
<Option @value="Name:desc" @selected={{eq "Name:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (sort-by (comparator 'service' sort.selected.key) services) as |sorted|}}
|
||||
{{#let (sort-by (comparator 'service' sort) services) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'service' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulServiceList @items={{filtered}} @proxies={{proxies}}/>
|
||||
|
|
|
@ -3,38 +3,68 @@
|
|||
<ErrorState @error={{api.error}} />
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="loaded">
|
||||
{{#let (filter-by "Action" "deny" api.data) as |denied|}}
|
||||
{{#let (selectable-key-values
|
||||
(array "" (concat "All (" api.data.length ")"))
|
||||
(array "allow" (concat "Allow (" (sub api.data.length denied.length) ")"))
|
||||
(array "deny" (concat "Deny (" denied.length ")"))
|
||||
selected=filterBy
|
||||
)
|
||||
as |filter|
|
||||
}}
|
||||
{{#let (or sortBy "Action:asc") as |sort|}}
|
||||
<div id="intentions" class="tab-section">
|
||||
<div role="tabpanel">
|
||||
<Portal @target="app-view-actions">
|
||||
<a data-test-create href={{href-to 'dc.services.show.intentions.create'}} class="type-create">Create</a>
|
||||
</Portal>
|
||||
{{#if (gt api.data.length 0) }}
|
||||
<input type="checkbox" id="toolbar-toggle" />
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@selected={{filter.selected}}
|
||||
@options={{filter.items}}
|
||||
@onchange={{action (mut filterBy) value='target.value'}}
|
||||
/>
|
||||
{{/if}}
|
||||
<ChangeableSet
|
||||
@dispatcher={{
|
||||
searchable
|
||||
'intention'
|
||||
(if (eq filter.selected.key "") api.data (filter-by "Action" filter.selected.key api.data))
|
||||
}}
|
||||
@terms={{search}}
|
||||
class="with-sort"
|
||||
>
|
||||
<BlockSlot @name="secondary">
|
||||
<PopoverSelect
|
||||
@position="right"
|
||||
@onchange={{action (mut sortBy) value='target.selected'}}
|
||||
@multiple={{false}}
|
||||
as |components|>
|
||||
<BlockSlot @name="selected">
|
||||
<span>
|
||||
{{#let (from-entries (array
|
||||
(array "Action:asc" "Allow to Deny")
|
||||
(array "Action:desc" "Deny to Allow")
|
||||
(array "SourceName:asc" "Source: A to Z")
|
||||
(array "SourceName:desc" "Source: Z to A")
|
||||
(array "DestinationName:asc" "Destination: A to Z")
|
||||
(array "DestinationName:desc" "Destination: Z to A")
|
||||
(array "Precedence:asc" "Precedence: Ascending")
|
||||
(array "Precedence:desc" "Precedence: Descending")
|
||||
))
|
||||
as |selectable|
|
||||
}}
|
||||
{{get selectable sort}}
|
||||
{{/let}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="options">
|
||||
{{#let components.Optgroup components.Option as |Optgroup Option|}}
|
||||
<Optgroup @label="Permission">
|
||||
<Option @value="Action:asc" @selected={{eq "Action:asc" sort}}>Allow to Deny</Option>
|
||||
<Option @value="Action:desc" @selected={{eq "Action:desc" sort}}>Deny to Allow</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Source">
|
||||
<Option @value="SourceName:asc" @selected={{eq "SourceName:asc" sort}}>A to Z</Option>
|
||||
<Option @value="SourceName:desc" @selected={{eq "SourceName:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Destination">
|
||||
<Option @value="DestinationName:asc" @selected={{eq "DestinationName:asc" sort}}>A to Z</Option>
|
||||
<Option @value="DestinationName:desc" @selected={{eq "DestinationName:desc" sort}}>Z to A</Option>
|
||||
</Optgroup>
|
||||
<Optgroup @label="Precedence">
|
||||
<Option @value="Precedence:asc" @selected={{eq "Precedence:asc" sort}}>Ascending</Option>
|
||||
<Option @value="Precedence:desc" @selected={{eq "Precedence:desc" sort}}>Descending</Option>
|
||||
</Optgroup>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</PopoverSelect>
|
||||
</BlockSlot>
|
||||
</SearchBar>
|
||||
{{/if}}
|
||||
{{#let (sort-by (comparator 'intention' sort) api.data) as |sorted|}}
|
||||
<ChangeableSet @dispatcher={{searchable 'intention' sorted}} @terms={{search}}>
|
||||
<BlockSlot @name="content" as |filtered|>
|
||||
<ConsulIntentionList
|
||||
@items={{filtered}}
|
||||
|
@ -51,9 +81,9 @@
|
|||
</ConsulIntentionList>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
</div>
|
||||
</div>
|
||||
{{/let}}
|
||||
</div>
|
||||
</div>
|
||||
{{/let}}
|
||||
</BlockSlot>
|
||||
</DataLoader>
|
||||
|
|
|
@ -38,7 +38,7 @@ Feature: dc / acls / roles / sorting
|
|||
- "system-D"
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.2.button on the sort
|
||||
When I click options.3.button on the sort
|
||||
Then I see name on the roles vertically like yaml
|
||||
---
|
||||
- "system-C"
|
||||
|
@ -47,7 +47,7 @@ Feature: dc / acls / roles / sorting
|
|||
- "system-B"
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.3.button on the sort
|
||||
When I click options.2.button on the sort
|
||||
Then I see name on the roles vertically like yaml
|
||||
---
|
||||
- "system-B"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / services / intentions: Intentions per service
|
||||
Feature: dc / services / show / intentions: Intentions per service
|
||||
Background:
|
||||
Given 1 datacenter model with the value "dc1"
|
||||
And 1 node models
|
||||
|
@ -26,6 +26,6 @@ Feature: dc / services / intentions: Intentions per service
|
|||
And I click actions on the intentions
|
||||
And I click delete on the intentions
|
||||
And I click confirmDelete on the intentions
|
||||
Then a DELETE request was made to "/v1/connect/intentions/ee52203d-989f-4f7a-ab5a-2bef004164ca?dc=dc1"
|
||||
Then a DELETE request was made to "/v1/connect/intentions/755b72bd-f5ab-4c92-90cc-bed0e7d8e9f0?dc=dc1"
|
||||
And "[data-notification]" has the "notification-delete" class
|
||||
And "[data-notification]" has the "success" class
|
||||
|
|
|
@ -40,7 +40,7 @@ Feature: dc / services / sorting
|
|||
dc: dc-1
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.1.button on the sort
|
||||
When I click options.3.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-F
|
||||
|
@ -51,20 +51,20 @@ Feature: dc / services / sorting
|
|||
- Service-A
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.0.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-A
|
||||
- Service-B
|
||||
- Service-C
|
||||
- Service-D
|
||||
- Service-E
|
||||
- Service-F
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.2.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-A
|
||||
- Service-B
|
||||
- Service-C
|
||||
- Service-D
|
||||
- Service-E
|
||||
- Service-F
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.0.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-B
|
||||
- Service-C
|
||||
- Service-A
|
||||
|
@ -73,7 +73,7 @@ Feature: dc / services / sorting
|
|||
- Service-E
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.3.button on the sort
|
||||
When I click options.1.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-E
|
||||
|
|
Loading…
Reference in New Issue