mirror of https://github.com/status-im/consul.git
ui: Remove WithSearching mixin, use helpers instead (#7961)
* ui: Remove WithSearching mixin, use composable helpers instead
This commit is contained in:
parent
d459bfd81c
commit
002797af82
|
@ -1,4 +0,0 @@
|
|||
{{!<form>}}
|
||||
<FreetextFilter @searchable={{searchable}} @value={{search}} @placeholder="Search by name/token" />
|
||||
<RadioGroup @keyboardAccess={{true}} @name="type" @value={{type}} @items={{filters}} @onchange={{action onchange}} />
|
||||
{{!</form>}}
|
|
@ -1,8 +0,0 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'form',
|
||||
classNames: ['filter-bar'],
|
||||
'data-test-acl-filter': true,
|
||||
onchange: function() {},
|
||||
});
|
|
@ -1,4 +0,0 @@
|
|||
{{!<form>}}
|
||||
<FreetextFilter @searchable={{searchable}} @value={{search}} @placeholder="Search" />
|
||||
<RadioGroup @keyboardAccess={{true}} @name="status" @value={{status}} @items={{array (hash label="All (Any Status)" value="") (hash label="Critical Checks" value="critical") (hash label="Warning Checks" value="warning") (hash label="Passing Checks" value="passing")}} @onchange={{action onchange}} />
|
||||
{{!</form>}}
|
|
@ -1,8 +0,0 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'form',
|
||||
classNames: ['filter-bar'],
|
||||
'data-test-catalog-filter': true,
|
||||
onchange: function() {},
|
||||
});
|
|
@ -1,10 +0,0 @@
|
|||
<form class="catalog-toolbar" data-test-catalog-toolbar>
|
||||
<FreetextFilter @searchable={{searchable}} @value={{value}} @placeholder="Search" />
|
||||
<PopoverSelect
|
||||
data-popover-select
|
||||
@selected={{selected}}
|
||||
@options={{options}}
|
||||
@onchange={{onchange}}
|
||||
@title='Sort By'
|
||||
/>
|
||||
</form>
|
|
@ -1,5 +0,0 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -1,19 +1,28 @@
|
|||
import Component from '@ember/component';
|
||||
import { get, set } from '@ember/object';
|
||||
import SlotsMixin from 'block-slots';
|
||||
import WithListeners from 'consul-ui/mixins/with-listeners';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Slotted from 'block-slots';
|
||||
|
||||
export default Component.extend(WithListeners, SlotsMixin, {
|
||||
export default Component.extend(Slotted, {
|
||||
tagName: '',
|
||||
dom: service('dom'),
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
this._listeners = this.dom.listeners();
|
||||
},
|
||||
willDestroyElement: function() {
|
||||
this._listeners.remove();
|
||||
this._super(...arguments);
|
||||
},
|
||||
didReceiveAttrs: function() {
|
||||
this._super(...arguments);
|
||||
this.removeListeners();
|
||||
const dispatcher = this.dispatcher;
|
||||
if (dispatcher) {
|
||||
this.listen(dispatcher, 'change', e => {
|
||||
set(this, 'items', e.target.data);
|
||||
if (this.items !== this.dispatcher.data) {
|
||||
this._listeners.remove();
|
||||
this._listeners.add(this.dispatcher, {
|
||||
change: e => set(this, 'items', e.target.data),
|
||||
});
|
||||
set(this, 'items', get(dispatcher, 'data'));
|
||||
set(this, 'items', get(this.dispatcher, 'data'));
|
||||
}
|
||||
this.dispatcher.search(this.terms);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{!<fieldset>}}
|
||||
<fieldset class="freetext-filter">
|
||||
<label class="type-search">
|
||||
<span>Search</span>
|
||||
<input type="search" onsearch={{action onchange}} oninput={{action onchange}} name="s" value="{{value}}" placeholder="{{placeholder}}" autofocus="autofocus" />
|
||||
<input type="search" onsearch={{action "change"}} oninput={{action "change"}} onkeydown={{action "keydown"}} name="s" value={{value}} placeholder={{placeholder}} autofocus="autofocus" />
|
||||
</label>
|
||||
{{!</fieldset>}}
|
||||
</fieldset>
|
|
@ -1,14 +1,19 @@
|
|||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
const ENTER = 13;
|
||||
export default Component.extend({
|
||||
tagName: 'fieldset',
|
||||
classNames: ['freetext-filter'],
|
||||
onchange: function(e) {
|
||||
let searchable = this.searchable;
|
||||
if (!Array.isArray(searchable)) {
|
||||
searchable = [searchable];
|
||||
}
|
||||
searchable.forEach(function(item) {
|
||||
item.search(e.target.value);
|
||||
});
|
||||
dom: service('dom'),
|
||||
tagName: '',
|
||||
actions: {
|
||||
change: function(e) {
|
||||
this.onsearch(
|
||||
this.dom.setEventTargetProperty(e, 'value', value => (value === '' ? undefined : value))
|
||||
);
|
||||
},
|
||||
keydown: function(e) {
|
||||
if (e.keyCode === ENTER) {
|
||||
e.preventDefault();
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
{{!<form>}}
|
||||
<FreetextFilter @searchable={{searchable}} @value={{search}} @placeholder="Search by Source or Destination" />
|
||||
<RadioGroup @keyboardAccess={{true}} @name="currentFilter" @value={{selected}} @items={{filters}} @onchange={{action onchange}} />
|
||||
{{!</form>}}
|
|
@ -1,8 +0,0 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'form',
|
||||
classNames: ['filter-bar'],
|
||||
'data-test-intention-filter': true,
|
||||
onchange: function() {},
|
||||
});
|
|
@ -1,19 +1,12 @@
|
|||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
dom: service('dom'),
|
||||
actions: {
|
||||
change: function(option, e) {
|
||||
// We fake an event here, which could be a bit of a footbun if we treat
|
||||
// it completely like an event, we should be abe to avoid doing this
|
||||
// when we move to glimmer components (this.args.selected vs this.selected)
|
||||
this.onchange({
|
||||
target: {
|
||||
selected: option,
|
||||
},
|
||||
// make this vaguely event like to avoid
|
||||
// having a separate property
|
||||
preventDefault: function(e) {},
|
||||
});
|
||||
this.onchange(this.dom.setEventTargetProperty(e, 'selected', selected => option));
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
<fieldset>
|
||||
<div role="radiogroup" id="radiogroup_{{name}}" data-test-radiogroup={{name}}>{{! menu?}}
|
||||
{{#each items as |item|}}
|
||||
<label tabindex={{if keyboardAccess '0'}} onkeydown={{if keyboardAccess (action 'keydown')}} class="type-radio value-{{item.value}}" data-test-radiobutton="{{name}}_{{item.value}}"> {{! slugify }}
|
||||
<input type="radio" name={{name}} value={{item.value}} checked={{if (eq (concat value) item.value) 'checked'}} onchange={{action onchange}} />
|
||||
<span>{{item.label}}</span>
|
||||
{{#let (if (not-eq item.key undefined) item.key item.value) (or item.label item.value) as |_key _value|}}
|
||||
<label tabindex={{if keyboardAccess '0'}} onkeydown={{if keyboardAccess (action 'keydown')}} class="type-radio value-{{_key}}" data-test-radiobutton="{{name}}_{{_key}}"> {{! slugify }}
|
||||
<input type="radio" name={{name}} value={{_key}} checked={{if (eq (concat value) _key) 'checked'}} onchange={{action "change"}} />
|
||||
<span>{{_value}}</span>
|
||||
</label>
|
||||
{{/let}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
|
@ -1,14 +1,25 @@
|
|||
import Component from '@ember/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
const ENTER = 13;
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
keyboardAccess: false,
|
||||
dom: service('dom'),
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
this.name = this.dom.guid(this);
|
||||
},
|
||||
actions: {
|
||||
keydown: function(e) {
|
||||
if (e.keyCode === ENTER) {
|
||||
e.target.dispatchEvent(new MouseEvent('click'));
|
||||
}
|
||||
},
|
||||
change: function(e) {
|
||||
this.onchange(
|
||||
this.dom.setEventTargetProperty(e, 'value', value => (value === '' ? undefined : value))
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
## SearchBar
|
||||
|
||||
```handlebars
|
||||
<SearchBar
|
||||
@value={{"search term"}}
|
||||
@onsearch={{action "search"}}
|
||||
/>
|
||||
```
|
||||
|
||||
### Arguments
|
||||
|
||||
| Argument | Type | Default | Description |
|
||||
| --- | --- | --- | --- |
|
||||
| `value` | `String` | | The string `value` of the freetext search bar |
|
||||
| `onsearch` | `Function` | | The action to fire when the freetext search bar changes. Emits a native event with a `target.value` property containing the text typed into the search bar |
|
||||
| `options` | `Array` | | An array of Key/Values pairs to use for options for either a filter interface or a sort interface |
|
||||
| `selected` | `Object` | | An object containing a Key/Value pair of the currently selected option |
|
||||
| `onchange` | `Function` | | The action to fire when the filter/sort changes. Emits an Event-like object, when filtering this has a `target.value` property containg the key of the selected filter, when sorting this has a `target.selected` property containing the selected Key/Value pair |
|
||||
| `secondary` | `string` | | String identifier to signify what type of secondary filter to show. Currently only value here is 'sort' |
|
||||
|
||||
`SearchBar` is used for a variety of searching behaviours, freetext searching, filtering and sorting. It is also slot based to enable you to completely overwrite the secondary search if need be.
|
||||
|
||||
### Examples
|
||||
|
||||
```handlebars
|
||||
{{! Freetext only search bar}}
|
||||
<SearchBar
|
||||
@value={{"search term"}}
|
||||
@onsearch={{action "search"}}
|
||||
/>
|
||||
```
|
||||
|
||||
```handlebars
|
||||
{{! Freetext and filter search bar}}
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value='target.value'}}
|
||||
@selected={{filter.selected}}
|
||||
@options={{filter.items}}
|
||||
@onchange={{action (mut filterBy) value='target.value'}}
|
||||
/>
|
||||
```
|
||||
|
||||
```handlebars
|
||||
{{! Freetext and sort search bar}}
|
||||
<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'}}
|
||||
/>
|
||||
```
|
||||
|
||||
### See
|
||||
|
||||
- [Component Source Code](./index.js)
|
||||
- [Template Source Code](./index.hbs)
|
||||
|
||||
---
|
|
@ -0,0 +1,32 @@
|
|||
{{yield}}
|
||||
<form class={{concat 'filter-bar' (if (eq secondary 'sort') ' with-sort')}} ...attributes>
|
||||
<FreetextFilter
|
||||
@onsearch={{action onsearch}}
|
||||
@value={{value}}
|
||||
@placeholder={{or placeholder 'Search'}}
|
||||
/>
|
||||
{{#yield-slot name="secondary"}}
|
||||
{{yield}}
|
||||
{{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}}
|
||||
@value={{selected.key}}
|
||||
@items={{options}}
|
||||
@onchange={{action onchange}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/yield-slot}}
|
||||
</form>
|
|
@ -0,0 +1,6 @@
|
|||
import Component from '@ember/component';
|
||||
import Slotted from 'block-slots';
|
||||
|
||||
export default Component.extend(Slotted, {
|
||||
tagName: '',
|
||||
});
|
|
@ -1,47 +1,14 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { computed, get } from '@ember/object';
|
||||
import WithFiltering from 'consul-ui/mixins/with-filtering';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
import ucfirst from 'consul-ui/utils/ucfirst';
|
||||
const countType = function(items, type) {
|
||||
return type === '' ? get(items, 'length') : items.filterBy('Type', type).length;
|
||||
};
|
||||
export default Controller.extend(WithSearching, WithFiltering, {
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
type: {
|
||||
filterBy: {
|
||||
as: 'type',
|
||||
},
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
acl: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('filtered', function() {
|
||||
return get(this, 'searchables.acl')
|
||||
.add(get(this, 'filtered'))
|
||||
.search(get(this, this.searchParams.acl));
|
||||
}),
|
||||
typeFilters: computed('items', function() {
|
||||
const items = get(this, 'items');
|
||||
return ['', 'management', 'client'].map(function(item) {
|
||||
return {
|
||||
label: `${item === '' ? 'All' : ucfirst(item)} (${countType(
|
||||
items,
|
||||
item
|
||||
).toLocaleString()})`,
|
||||
value: item,
|
||||
};
|
||||
});
|
||||
}),
|
||||
filter: function(item, { type = '' }) {
|
||||
return type === '' || get(item, 'Type') === type;
|
||||
},
|
||||
actions: {
|
||||
sendClone: function(item) {
|
||||
this.send('clone', item);
|
||||
|
|
|
@ -1,23 +1,9 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, computed } from '@ember/object';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
export default Controller.extend(WithSearching, {
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
policy: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('items', function() {
|
||||
return get(this, 'searchables.policy')
|
||||
.add(this.items)
|
||||
.search(get(this, this.searchParams.policy));
|
||||
}),
|
||||
actions: {},
|
||||
});
|
||||
|
|
|
@ -1,23 +1,9 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, computed } from '@ember/object';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
export default Controller.extend(WithSearching, {
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
role: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('items', function() {
|
||||
return get(this, 'searchables.role')
|
||||
.add(this.items)
|
||||
.search(get(this, this.searchParams.role));
|
||||
}),
|
||||
actions: {},
|
||||
});
|
||||
|
|
|
@ -1,24 +1,11 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { computed, get } from '@ember/object';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
export default Controller.extend(WithSearching, {
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
token: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('items', function() {
|
||||
return get(this, 'searchables.token')
|
||||
.add(get(this, 'items'))
|
||||
.search(get(this, this.searchParams.token));
|
||||
}),
|
||||
actions: {
|
||||
sendClone: function(item) {
|
||||
this.send('clone', item);
|
||||
|
|
|
@ -1,52 +1,15 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { computed, get } from '@ember/object';
|
||||
import WithFiltering from 'consul-ui/mixins/with-filtering';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
import WithEventSource from 'consul-ui/mixins/with-event-source';
|
||||
import ucfirst from 'consul-ui/utils/ucfirst';
|
||||
// TODO: DRY out in acls at least
|
||||
const createCounter = function(prop) {
|
||||
return function(items, val) {
|
||||
return val === '' ? get(items, 'length') : items.filterBy(prop, val).length;
|
||||
};
|
||||
};
|
||||
const countAction = createCounter('Action');
|
||||
export default Controller.extend(WithSearching, WithFiltering, WithEventSource, {
|
||||
export default Controller.extend(WithEventSource, {
|
||||
queryParams: {
|
||||
currentFilter: {
|
||||
filterBy: {
|
||||
as: 'action',
|
||||
},
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
intention: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('filtered', function() {
|
||||
return get(this, 'searchables.intention')
|
||||
.add(get(this, 'filtered'))
|
||||
.search(get(this, this.searchParams.intention));
|
||||
}),
|
||||
actionFilters: computed('items', function() {
|
||||
const items = get(this, 'items');
|
||||
return ['', 'allow', 'deny'].map(function(item) {
|
||||
return {
|
||||
label: `${item === '' ? 'All' : ucfirst(item)} (${countAction(
|
||||
items,
|
||||
item
|
||||
).toLocaleString()})`,
|
||||
value: item,
|
||||
};
|
||||
});
|
||||
}),
|
||||
filter: function(item, { s = '', currentFilter = '' }) {
|
||||
return currentFilter === '' || get(item, 'Action') === currentFilter;
|
||||
},
|
||||
actions: {
|
||||
route: function() {
|
||||
this.send(...arguments);
|
||||
|
|
|
@ -1,22 +1,9 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, computed } from '@ember/object';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
export default Controller.extend(WithSearching, {
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
kv: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('items', function() {
|
||||
return get(this, 'searchables.kv')
|
||||
.add(this.items)
|
||||
.search(get(this, this.searchParams.kv));
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,38 +1,28 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { computed } from '@ember/object';
|
||||
import WithEventSource from 'consul-ui/mixins/with-event-source';
|
||||
import WithHealthFiltering from 'consul-ui/mixins/with-health-filtering';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
import { get } from '@ember/object';
|
||||
export default Controller.extend(WithEventSource, WithSearching, WithHealthFiltering, {
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
healthyNode: 's',
|
||||
unhealthyNode: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
|
||||
export default Controller.extend(WithEventSource, {
|
||||
queryParams: {
|
||||
filterBy: {
|
||||
as: 'status',
|
||||
},
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
searchableHealthy: computed('healthy', function() {
|
||||
return get(this, 'searchables.healthyNode')
|
||||
.add(this.healthy)
|
||||
.search(get(this, this.searchParams.healthyNode));
|
||||
}),
|
||||
searchableUnhealthy: computed('unhealthy', function() {
|
||||
return get(this, 'searchables.unhealthyNode')
|
||||
.add(this.unhealthy)
|
||||
.search(get(this, this.searchParams.unhealthyNode));
|
||||
}),
|
||||
unhealthy: computed('filtered', function() {
|
||||
return this.filtered.filter(function(item) {
|
||||
return get(item, 'isUnhealthy');
|
||||
});
|
||||
}),
|
||||
healthy: computed('filtered', function() {
|
||||
return this.filtered.filter(function(item) {
|
||||
return get(item, 'isHealthy');
|
||||
});
|
||||
}),
|
||||
filter: function(item, { s = '', status = '' }) {
|
||||
return item.hasStatus(status);
|
||||
actions: {
|
||||
hasStatus: function(status, checks) {
|
||||
if (status === '') {
|
||||
return true;
|
||||
}
|
||||
return checks.some(item => item.Status === status);
|
||||
},
|
||||
isHealthy: function(checks) {
|
||||
return !this.actions.isUnhealthy.apply(this, [checks]);
|
||||
},
|
||||
isUnhealthy: function(checks) {
|
||||
return checks.some(item => item.Status === 'critical' || item.Status === 'warning');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,27 +1,12 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { get, computed } from '@ember/object';
|
||||
import { alias } from '@ember/object/computed';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
|
||||
export default Controller.extend(WithSearching, {
|
||||
dom: service('dom'),
|
||||
export default Controller.extend({
|
||||
items: alias('item.Services'),
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
nodeservice: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('items', function() {
|
||||
return get(this, 'searchables.nodeservice')
|
||||
.add(this.items)
|
||||
.search(get(this, this.searchParams.nodeservice));
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,23 +1,10 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, computed } from '@ember/object';
|
||||
|
||||
import WithEventSource from 'consul-ui/mixins/with-event-source';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
export default Controller.extend(WithEventSource, WithSearching, {
|
||||
export default Controller.extend(WithEventSource, {
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
nspace: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('items.[]', function() {
|
||||
return get(this, 'searchables.nspace')
|
||||
.add(this.items)
|
||||
.search(get(this, this.searchParams.nspace));
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -1,25 +1,13 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, computed } from '@ember/object';
|
||||
import { computed } from '@ember/object';
|
||||
import WithEventSource from 'consul-ui/mixins/with-event-source';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
export default Controller.extend(WithEventSource, WithSearching, {
|
||||
export default Controller.extend(WithEventSource, {
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
service: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('services.[]', function() {
|
||||
return get(this, 'searchables.service')
|
||||
.add(this.services)
|
||||
.search(this.terms);
|
||||
}),
|
||||
services: computed('items.[]', function() {
|
||||
return this.items.filter(function(item) {
|
||||
return item.Kind !== 'connect-proxy';
|
||||
|
|
|
@ -1,29 +1,15 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, computed } from '@ember/object';
|
||||
import { computed } from '@ember/object';
|
||||
import { alias } from '@ember/object/computed';
|
||||
import { inject as service } from '@ember/service';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
|
||||
export default Controller.extend(WithSearching, {
|
||||
dom: service('dom'),
|
||||
export default Controller.extend({
|
||||
items: alias('item.Nodes'),
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
serviceInstance: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('items', function() {
|
||||
return get(this, 'searchables.serviceInstance')
|
||||
.add(this.items)
|
||||
.search(get(this, this.searchParams.serviceInstance));
|
||||
}),
|
||||
keyedProxies: computed('proxies.[]', function() {
|
||||
const proxies = {};
|
||||
this.proxies.forEach(item => {
|
||||
|
|
|
@ -1,25 +1,15 @@
|
|||
import Controller from '@ember/controller';
|
||||
import { get, computed } from '@ember/object';
|
||||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
|
||||
export default Controller.extend(WithSearching, {
|
||||
export default Controller.extend({
|
||||
queryParams: {
|
||||
s: {
|
||||
filterBy: {
|
||||
as: 'action',
|
||||
},
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
init: function() {
|
||||
this.searchParams = {
|
||||
intention: 's',
|
||||
};
|
||||
this._super(...arguments);
|
||||
},
|
||||
searchable: computed('intentions', function() {
|
||||
return get(this, 'searchables.intention')
|
||||
.add(this.intentions)
|
||||
.search(get(this, this.searchParams.intention));
|
||||
}),
|
||||
actions: {
|
||||
route: function() {
|
||||
this.send(...arguments);
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import Helper from '@ember/component/helper';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default Helper.extend({
|
||||
search: service('search'),
|
||||
compute([type, items], hash) {
|
||||
return this.search.searchable(type).add(items);
|
||||
},
|
||||
});
|
|
@ -1,49 +0,0 @@
|
|||
import Mixin from '@ember/object/mixin';
|
||||
import { computed, get, set } from '@ember/object';
|
||||
|
||||
const toKeyValue = function(el) {
|
||||
const key = el.name;
|
||||
let value = '';
|
||||
switch (el.type) {
|
||||
case 'radio':
|
||||
value = el.value === 'on' ? '' : el.value;
|
||||
break;
|
||||
case 'search':
|
||||
case 'text':
|
||||
value = el.value;
|
||||
break;
|
||||
}
|
||||
return { [key]: value };
|
||||
};
|
||||
export default Mixin.create({
|
||||
filters: {},
|
||||
filtered: computed('items.[]', 'filters', function() {
|
||||
const filters = get(this, 'filters');
|
||||
return get(this, 'items').filter(item => {
|
||||
return this.filter(item, filters);
|
||||
});
|
||||
}),
|
||||
setProperties: function() {
|
||||
this._super(...arguments);
|
||||
const query = get(this, 'queryParams');
|
||||
query.forEach((item, i, arr) => {
|
||||
const filters = get(this, 'filters');
|
||||
Object.keys(item).forEach(key => {
|
||||
set(filters, key, get(this, key));
|
||||
});
|
||||
set(this, 'filters', filters);
|
||||
});
|
||||
},
|
||||
actions: {
|
||||
filter: function(e) {
|
||||
const obj = toKeyValue(e.target);
|
||||
Object.keys(obj).forEach((key, i, arr) => {
|
||||
set(this, key, obj[key] != '' ? obj[key] : null);
|
||||
});
|
||||
set(this, 'filters', {
|
||||
...this.filters,
|
||||
...obj,
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
import Mixin from '@ember/object/mixin';
|
||||
import WithFiltering from 'consul-ui/mixins/with-filtering';
|
||||
|
||||
export default Mixin.create(WithFiltering, {
|
||||
queryParams: {
|
||||
status: {
|
||||
as: 'status',
|
||||
},
|
||||
s: {
|
||||
as: 'filter',
|
||||
},
|
||||
},
|
||||
});
|
|
@ -1,32 +0,0 @@
|
|||
import Mixin from '@ember/object/mixin';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { set } from '@ember/object';
|
||||
import WithListeners from 'consul-ui/mixins/with-listeners';
|
||||
/**
|
||||
* WithSearching mostly depends on a `searchParams` object which must be set
|
||||
* inside the `init` function. The naming and usage of this is modelled on
|
||||
* `queryParams` but in contrast cannot _yet_ be 'hung' of the Controller
|
||||
* object, it MUST be set in the `init` method.
|
||||
* Reasons: As well as producing a eslint error, it can also be 'shared' amongst
|
||||
* child Classes of the component. It is not clear _yet_ whether mixing this in
|
||||
* avoids this and is something to be looked at in future to slightly improve DX
|
||||
* Please also see:
|
||||
* https://emberjs.com/api/ember/2.12/classes/Ember.Object/properties?anchor=mergedProperties
|
||||
*
|
||||
*/
|
||||
export default Mixin.create(WithListeners, {
|
||||
builder: service('search'),
|
||||
init: function() {
|
||||
this._super(...arguments);
|
||||
const params = this.searchParams || {};
|
||||
this.searchables = {};
|
||||
Object.keys(params).forEach(type => {
|
||||
const key = params[type];
|
||||
this.searchables[type] = this.builder.searchable(type);
|
||||
this.listen(this.searchables[type], 'change', e => {
|
||||
const value = e.target.value;
|
||||
set(this, key, value === '' ? null : value);
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
|
@ -1,8 +1,5 @@
|
|||
import Model from 'ember-data/model';
|
||||
import attr from 'ember-data/attr';
|
||||
import { computed, get } from '@ember/object';
|
||||
import sumOfUnhealthy from 'consul-ui/utils/sumOfUnhealthy';
|
||||
import hasStatus from 'consul-ui/utils/hasStatus';
|
||||
|
||||
export const PRIMARY_KEY = 'uid';
|
||||
export const SLUG_KEY = 'ID';
|
||||
|
@ -23,13 +20,4 @@ export default Model.extend({
|
|||
Coord: attr(),
|
||||
SyncTime: attr('number'),
|
||||
meta: attr(),
|
||||
hasStatus: function(status) {
|
||||
return hasStatus(get(this, 'Checks'), status);
|
||||
},
|
||||
isHealthy: computed('Checks', function() {
|
||||
return sumOfUnhealthy(get(this, 'Checks')) === 0;
|
||||
}),
|
||||
isUnhealthy: computed('Checks', function() {
|
||||
return sumOfUnhealthy(get(this, 'Checks')) > 0;
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ export default Route.extend(WithAclActions, {
|
|||
repo: service('repository/acl'),
|
||||
settings: service('settings'),
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@ import WithPolicyActions from 'consul-ui/mixins/policy/with-actions';
|
|||
export default Route.extend(WithPolicyActions, {
|
||||
repo: service('repository/policy'),
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@ import WithRoleActions from 'consul-ui/mixins/role/with-actions';
|
|||
export default Route.extend(WithRoleActions, {
|
||||
repo: service('repository/role'),
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@ export default Route.extend(WithTokenActions, {
|
|||
repo: service('repository/token'),
|
||||
settings: service('settings'),
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
|
|
|
@ -7,10 +7,10 @@ import WithIntentionActions from 'consul-ui/mixins/intention/with-actions';
|
|||
export default Route.extend(WithIntentionActions, {
|
||||
repo: service('repository/intention'),
|
||||
queryParams: {
|
||||
currentFilter: {
|
||||
filterBy: {
|
||||
as: 'action',
|
||||
},
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@ import WithKvActions from 'consul-ui/mixins/kv/with-actions';
|
|||
|
||||
export default Route.extend(WithKvActions, {
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@ import { hash } from 'rsvp';
|
|||
export default Route.extend({
|
||||
repo: service('repository/node'),
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import Route from '@ember/routing/route';
|
||||
|
||||
export default Route.extend({
|
||||
queryParams: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
model: function() {
|
||||
const parent = this.routeName
|
||||
.split('.')
|
||||
|
|
|
@ -6,7 +6,7 @@ import WithNspaceActions from 'consul-ui/mixins/nspace/with-actions';
|
|||
export default Route.extend(WithNspaceActions, {
|
||||
repo: service('repository/nspace'),
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
|
|
|
@ -5,7 +5,7 @@ import { hash } from 'rsvp';
|
|||
export default Route.extend({
|
||||
repo: service('repository/service'),
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
|
|
|
@ -2,7 +2,7 @@ import Route from '@ember/routing/route';
|
|||
|
||||
export default Route.extend({
|
||||
queryParams: {
|
||||
s: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
|
|
|
@ -3,6 +3,12 @@ import { inject as service } from '@ember/service';
|
|||
import WithIntentionActions from 'consul-ui/mixins/intention/with-actions';
|
||||
|
||||
export default Route.extend(WithIntentionActions, {
|
||||
queryParams: {
|
||||
search: {
|
||||
as: 'filter',
|
||||
replace: true,
|
||||
},
|
||||
},
|
||||
repo: service('repository/intention'),
|
||||
model: function() {
|
||||
const parent = this.routeName
|
||||
|
|
|
@ -51,6 +51,24 @@ export default Service.extend({
|
|||
sibling: sibling,
|
||||
isOutside: isOutside,
|
||||
normalizeEvent: normalizeEvent,
|
||||
setEventTargetProperty: function(e, property, cb) {
|
||||
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 (prop === property) {
|
||||
return cb(e.target[property]);
|
||||
}
|
||||
return target[prop];
|
||||
},
|
||||
});
|
||||
}
|
||||
return Reflect.get(...arguments);
|
||||
},
|
||||
});
|
||||
},
|
||||
listeners: createListeners,
|
||||
root: function() {
|
||||
return this.doc.documentElement;
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
}
|
||||
/* */
|
||||
/* TODO: Think about an %app-form or similar */
|
||||
%app-view-content fieldset:not(.freetext-filter) {
|
||||
%app-view-content form:not(.filter-bar) fieldset {
|
||||
padding-bottom: 0.3em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
%expanded-single-select {
|
||||
border: $decor-border-100;
|
||||
border-color: $gray-300;
|
||||
border-radius: $decor-radius-100;
|
||||
}
|
||||
%expanded-single-select label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -3,11 +3,8 @@
|
|||
.filter-bar {
|
||||
@extend %filter-bar;
|
||||
}
|
||||
.catalog-toolbar {
|
||||
@extend %catalog-toolbar;
|
||||
}
|
||||
%catalog-toolbar {
|
||||
@extend %filter-bar;
|
||||
%filter-bar:not(.with-sort) {
|
||||
@extend %filter-bar-reversed;
|
||||
}
|
||||
%filter-bar [role='radiogroup'] {
|
||||
@extend %expanded-single-select;
|
||||
|
|
|
@ -1,45 +1,38 @@
|
|||
%filter-bar {
|
||||
padding: 4px;
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 4px 8px;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: -12px;
|
||||
}
|
||||
%filter-bar + :not(.notice) {
|
||||
margin-top: 1.8em;
|
||||
}
|
||||
%catalog-toolbar {
|
||||
padding: 4px 8px;
|
||||
display: flex;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: -12px !important;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
%filter-bar-reversed {
|
||||
flex-direction: row-reverse;
|
||||
padding: 4px;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
@media #{$--horizontal-filters} {
|
||||
%filter-bar {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
}
|
||||
%catalog-toolbar {
|
||||
flex-direction: row;
|
||||
}
|
||||
%filter-bar > *:first-child {
|
||||
margin-left: 12px;
|
||||
}
|
||||
%catalog-toolbar > *:first-child {
|
||||
margin-left: 0px;
|
||||
}
|
||||
%filter-bar fieldset {
|
||||
min-width: 210px;
|
||||
width: auto;
|
||||
}
|
||||
%catalog-toolbar fieldset {
|
||||
min-width: none;
|
||||
width: 100%;
|
||||
}
|
||||
%filter-bar fieldset {
|
||||
flex: 0 1 auto;
|
||||
width: auto;
|
||||
}
|
||||
%filter-bar fieldset:first-child:not(:last-child) {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
%filter-bar-reversed fieldset:first-child:not(:last-child) {
|
||||
flex: 0 1 auto;
|
||||
margin-left: auto;
|
||||
}
|
||||
%filter-bar-reversed fieldset {
|
||||
min-width: 210px;
|
||||
width: auto;
|
||||
}
|
||||
%filter-bar-reversed > *:first-child {
|
||||
margin-left: 12px;
|
||||
}
|
||||
@media #{$--lt-horizontal-filters} {
|
||||
%filter-bar > *:first-child {
|
||||
margin: 2px 0;
|
||||
%filter-bar-reversed > *:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// decoration/color
|
||||
%filter-bar > * {
|
||||
border: $decor-border-100;
|
||||
border-radius: $decor-radius-100;
|
||||
%filter-bar {
|
||||
border-bottom: $decor-border-100;
|
||||
border-color: $gray-200;
|
||||
}
|
||||
%catalog-toolbar > div {
|
||||
border: none;
|
||||
%filter-bar-reversed {
|
||||
border-bottom: none;
|
||||
}
|
||||
// TODO: Move this elsewhere
|
||||
@media #{$--horizontal-selects} {
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
%freetext-filter {
|
||||
cursor: pointer;
|
||||
border: $decor-border-100;
|
||||
border-color: $gray-200;
|
||||
border-radius: $decor-radius-100;
|
||||
}
|
||||
%freetext-filter input {
|
||||
-webkit-appearance: none;
|
||||
|
|
|
@ -23,6 +23,9 @@
|
|||
display: grid;
|
||||
grid-auto-rows: 12px;
|
||||
}
|
||||
%card-grid li.empty {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
@media #{$--fixed-grid} {
|
||||
%card-grid > ul,
|
||||
%card-grid > ol {
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
{{#let (filter-by "Type" "client" items) as |client|}}
|
||||
{{#let (selectable-key-values
|
||||
(array "" (concat "All (" items.length ")"))
|
||||
(array "management" (concat "Management (" (sub items.length client.length) ")"))
|
||||
(array "client" (concat "Client (" client.length ")"))
|
||||
selected=filterBy
|
||||
)
|
||||
as |filter|
|
||||
}}
|
||||
<AppView @class="acl list" @loading={{isLoading}}>
|
||||
<BlockSlot @name="notification" as |status type|>
|
||||
{{partial 'dc/acls/notifications'}}
|
||||
|
@ -13,11 +22,18 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<AclFilter @searchable={{searchable}} @filters={{typeFilters}} @search={{filters.s}} @type={{filters.type}} @onchange={{action "filter"}} />
|
||||
<SearchBar
|
||||
data-test-acl-filter="true"
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@selected={{filter.selected}}
|
||||
@options={{filter.items}}
|
||||
@onchange={{action (mut filterBy) value='target.value'}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<ChangeableSet @dispatcher={{searchable 'acl' (if (eq filter.selected.key "") items (filter-by "Type" filter.selected.key items))}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<TabularCollection @items={{sort-by "Name:asc" filtered}} as |item index|>
|
||||
<BlockSlot @name="header">
|
||||
|
@ -130,23 +146,27 @@
|
|||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>No ACLs</h2>
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No ACLs found
|
||||
{{else}}
|
||||
Welcome to ACLs
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There don't seem to be any ACLs yet, or you may not have access to view ACLs yet.
|
||||
{{#if (gt items.length 0)}}
|
||||
No ACLs where found matching that search, or you may not have access to view the ACLs you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any ACLs yet, or you may not have access to view ACLs yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
<li class="docs-link">
|
||||
<a href="{{env 'CONSUL_DOCS_URL'}}" rel="noopener noreferrer" target="_blank">Read the documentation</a>
|
||||
</li>
|
||||
<li class="learn-link">
|
||||
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/" rel="noopener noreferrer" target="_blank">Follow the guide</a>
|
||||
</li>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
</AppView>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
|
|
|
@ -33,11 +33,12 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#if (gt items.length 0) }}
|
||||
<form class="filter-bar">
|
||||
<FreetextFilter @searchable={{searchable}} @value={{s}} @placeholder="Search" />
|
||||
</form>
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
/>
|
||||
{{/if}}
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<ChangeableSet @dispatcher={{searchable 'policy' items}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<TabularCollection @items={{sort-by "CreateIndex:desc" "Name:asc" filtered}} as |item index|>
|
||||
<BlockSlot @name="header">
|
||||
|
@ -103,11 +104,21 @@
|
|||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>Welcome to Policies</h2>
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No policies found
|
||||
{{else}}
|
||||
Welcome to Policies
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There don't seem to be any policies, or you may not have access to view policies yet.
|
||||
{{#if (gt items.length 0)}}
|
||||
No policies where found matching that search, or you may not have access to view the policies you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any policies, or you may not have access to view policies yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
|
|
|
@ -33,11 +33,12 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#if (gt items.length 0) }}
|
||||
<form class="filter-bar">
|
||||
<FreetextFilter @searchable={{searchable}} @value={{s}} @placeholder="Search" />
|
||||
</form>
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
/>
|
||||
{{/if}}
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<ChangeableSet @dispatcher={{searchable 'role' items}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<TabularCollection @items={{sort-by "CreateIndex:desc" "Name:asc" filtered}} as |item index|>
|
||||
<BlockSlot @name="header">
|
||||
|
@ -98,11 +99,21 @@
|
|||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>Welcome to Roles</h2>
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No roles found
|
||||
{{else}}
|
||||
Welcome to Roles
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There don't seem to be any roles, or you may not have access to view roles yet.
|
||||
{{#if (gt items.length 0)}}
|
||||
No roles where found matching that search, or you may not have access to view the roles you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any roles, or you may not have access to view roles yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
|
|
|
@ -33,14 +33,15 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#if (gt items.length 0) }}
|
||||
<form class="filter-bar">
|
||||
<FreetextFilter @searchable={{searchable}} @value={{s}} @placeholder="Search" />
|
||||
</form>
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{#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}}
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<ChangeableSet @dispatcher={{searchable 'token' items}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<TabularCollection @items={{sort-by "CreateTime:desc" filtered}} as |item index|>
|
||||
<BlockSlot @name="header">
|
||||
|
@ -167,9 +168,26 @@
|
|||
</TabularCollection>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<p>
|
||||
There are no Tokens.
|
||||
</p>
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No tokens found
|
||||
{{else}}
|
||||
Welcome to ACL Tokens
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
{{#if (gt items.length 0)}}
|
||||
No tokens where found matching that search, or you may not have access to view the tokens you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any tokens, or you may not have access to view tokens yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
</BlockSlot>
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
{{title 'Intentions'}}
|
||||
{{#let (filter-by "Action" "deny" items) as |denied|}}
|
||||
{{#let (selectable-key-values
|
||||
(array "" (concat "All (" items.length ")"))
|
||||
(array "allow" (concat "Allow (" (sub items.length denied.length) ")"))
|
||||
(array "deny" (concat "Deny (" denied.length ")"))
|
||||
selected=filterBy
|
||||
)
|
||||
as |filter|
|
||||
}}
|
||||
<AppView @class="intention list">
|
||||
<BlockSlot @name="notification" as |status type|>
|
||||
{{partial 'dc/intentions/notifications'}}
|
||||
|
@ -14,11 +23,18 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<IntentionFilter @searchable={{searchable}} @selected={{currentFilter}} @filters={{actionFilters}} @search={{filters.s}} @type={{filters.action}} @onchange={{action "filter"}} />
|
||||
<SearchBar
|
||||
data-test-intention-filter="true"
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@selected={{filter.selected}}
|
||||
@options={{filter.items}}
|
||||
@onchange={{action (mut filterBy) value='target.value'}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<ChangeableSet @dispatcher={{searchable 'intention' (if (eq filter.selected.key "") items (filter-by "Action" filter.selected.key items))}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulIntentionList
|
||||
@items={{filtered}}
|
||||
|
@ -28,11 +44,21 @@
|
|||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>Welcome to Intentions</h2>
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No intentions found
|
||||
{{else}}
|
||||
Welcome to Intentions
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There don't seem to be any intentions, or you may not have access to view intentions yet.
|
||||
{{#if (gt items.length 0)}}
|
||||
No intentions where found matching that search, or you may not have access to view the intentions you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any intentions, or you may not have access to view intentions yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
|
@ -47,4 +73,6 @@
|
|||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
</AppView>
|
||||
{{/let}}
|
||||
{{/let}}
|
|
@ -25,9 +25,11 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<form class="filter-bar">
|
||||
<FreetextFilter @searchable={{searchable}} @value={{s}} @placeholder="Search by name" />
|
||||
</form>
|
||||
<SearchBar
|
||||
@placeholder="Search by name"
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
|
@ -38,7 +40,7 @@
|
|||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<ChangeableSet @dispatcher={{searchable 'kv' items}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<TabularCollection @items={{sort-by "isFolder:desc" "Key:asc" filtered}} as |item index|>
|
||||
<BlockSlot @name="header">
|
||||
|
@ -89,11 +91,21 @@
|
|||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>Welcome to Key/Value</h2>
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No K/V pairs found
|
||||
{{else}}
|
||||
Welcome to Key/Value
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
You don't have any K/V pairs, or you may not have access to view K/V pairs yet.
|
||||
{{#if (gt items.length 0)}}
|
||||
No K/V pairs where found matching that search, or you may not have access to view the K/V pairs you are searching for.
|
||||
{{else}}
|
||||
You don't have any K/V pairs, or you may not have access to view K/V pairs yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
{{title 'Nodes'}}
|
||||
{{#let (selectable-key-values
|
||||
(array "" "All (Any Status)")
|
||||
(array "critical" "Critical Checks")
|
||||
(array "warning" "Warning Checks")
|
||||
(array "passing" "Passing Checks")
|
||||
selected=filterBy
|
||||
)
|
||||
as |filter|
|
||||
}}
|
||||
<AppView @class="node list">
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
|
@ -8,17 +17,35 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<CatalogFilter @searchable={{array searchableHealthy searchableUnhealthy}} @search={{s}} @status={{filters.status}} @onchange={{action "filter"}} />
|
||||
<SearchBar
|
||||
data-test-catalog-filter
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
@selected={{filter.selected}}
|
||||
@options={{filter.items}}
|
||||
@onchange={{action (mut filterBy) value='target.value'}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#if (gt unhealthy.length 0) }}
|
||||
{{#let (filter-by "Checks" (action "isUnhealthy") items) as |unhealthy|}}
|
||||
{{#if (gt unhealthy.length 0) }}
|
||||
<div class="unhealthy">
|
||||
<h2>Unhealthy Nodes</h2>
|
||||
<div>
|
||||
{{! think about 2 differing views here }}
|
||||
<ul>
|
||||
<ChangeableSet @dispatcher={{searchableUnhealthy}}>
|
||||
<ChangeableSet
|
||||
@dispatcher={{
|
||||
searchable
|
||||
'unhealthyNode'
|
||||
(if (eq filter.selected.key "")
|
||||
unhealthy
|
||||
(filter-by "Checks" (action "hasStatus" filter.selected.key) unhealthy filter.selected.key)
|
||||
)
|
||||
}}
|
||||
@terms={{search}}
|
||||
>
|
||||
<BlockSlot @name="set" as |unhealthy|>
|
||||
{{#each unhealthy as |item|}}
|
||||
<HealthcheckedResource @tagName="li" @data-test-node={{item.Node}} @href={{href-to "dc.nodes.show" item.Node}} @name={{item.Node}} @address={{item.Address}} @checks={{item.Checks}}>
|
||||
|
@ -31,19 +58,40 @@
|
|||
{{/each}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<p>
|
||||
There are no unhealthy nodes for that search.
|
||||
</p>
|
||||
<li class="empty">
|
||||
<EmptyState>
|
||||
<BlockSlot @name="header">
|
||||
<h2>No nodes found</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There don't seem to be any nodes matching that search.
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</li>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (gt healthy.length 0) }}
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{#let (filter-by "Checks" (action "isHealthy") items) as |healthy|}}
|
||||
{{#if (gt healthy.length 0) }}
|
||||
<div class="healthy">
|
||||
<h2>Healthy Nodes</h2>
|
||||
<ChangeableSet @dispatcher={{searchableHealthy}}>
|
||||
<ChangeableSet
|
||||
@dispatcher={{
|
||||
searchable
|
||||
'healthyNode'
|
||||
(if (eq filter.selected.key "")
|
||||
healthy
|
||||
(filter-by "Checks" (action "hasStatus" filter.selected.key) healthy filter.selected.key)
|
||||
)
|
||||
}}
|
||||
@terms={{search}}
|
||||
>
|
||||
<BlockSlot @name="set" as |healthy|>
|
||||
<GridCollection @cellHeight={{92}} @items={{healthy}} as |item index|>
|
||||
<HealthcheckedResource @data-test-node={{item.Node}} @href={{href-to "dc.nodes.show" item.Node}} @name={{item.Node}} @address={{item.Address}} @checks={{item.Checks}}>
|
||||
|
@ -56,14 +104,22 @@
|
|||
</GridCollection>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<p>
|
||||
There are no healthy nodes for that search.
|
||||
</p>
|
||||
<EmptyState>
|
||||
<BlockSlot @name="header">
|
||||
<h2>No nodes found</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There don't seem to be any nodes matching that search.
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if (and (eq healthy.length 0) (eq unhealthy.length 0)) }}
|
||||
{{/if}}
|
||||
{{/let}}
|
||||
{{#if (eq items.length 0) }}
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>Welcome to Nodes</h2>
|
||||
|
@ -73,15 +129,8 @@
|
|||
There don't seem to be any nodes, or you may not have access to view nodes yet.
|
||||
</p>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
<li class="docs-link">
|
||||
<a href="{{env 'CONSUL_DOCS_URL'}}" rel="noopener noreferrer" target="_blank">Documentation on nodes</a>
|
||||
</li>
|
||||
<li class="learn-link">
|
||||
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/" rel="noopener noreferrer" target="_blank">Read the guide</a>
|
||||
</li>
|
||||
</BlockSlot>
|
||||
</EmptyState>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
</AppView>
|
||||
{{/let}}
|
|
@ -2,11 +2,13 @@
|
|||
<div role="tabpanel">
|
||||
{{#if (gt items.length 0) }}
|
||||
<input type="checkbox" id="toolbar-toggle" />
|
||||
<form class="filter-bar">
|
||||
<FreetextFilter @searchable={{searchable}} @value={{s}} @placeholder="Search by name/port" />
|
||||
</form>
|
||||
<SearchBar
|
||||
@placeholder="Search by name/port"
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
/>
|
||||
{{/if}}
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<ChangeableSet @dispatcher={{searchable 'nodeservice' items}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<TabularCollection
|
||||
data-test-services
|
||||
|
|
|
@ -13,11 +13,13 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#if (gt items.length 0) }}
|
||||
<form class="filter-bar">
|
||||
<FreetextFilter @searchable={{searchable}} @value={{s}} @placeholder="Search" />
|
||||
</form>
|
||||
<SearchBar
|
||||
@placeholder="Search by name"
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
/>
|
||||
{{/if}}
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<ChangeableSet @dispatcher={{searchable 'nspace' items}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<TabularCollection @items={{filtered}} as |item index|>
|
||||
<BlockSlot @name="header">
|
||||
|
@ -92,11 +94,21 @@
|
|||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>Welcome to Namespaces</h2>
|
||||
<h2>
|
||||
{{#if (gt items.length 0)}}
|
||||
No namespaces found
|
||||
{{else}}
|
||||
Welcome to Namespaces
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There don't seem to be any namespaces, or you may not have access to view namespaces yet.
|
||||
{{#if (gt items.length 0)}}
|
||||
No namespaces where found matching that search, or you may not have access to view the namespaces you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any namespaces, or you may not have access to view namespaces yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
|
|
|
@ -12,15 +12,16 @@
|
|||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
Services <em>{{format-number items.length}} total</em>
|
||||
Services <em>{{format-number services.length}} total</em>
|
||||
</h1>
|
||||
<label for="toolbar-toggle"></label>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<CatalogToolbar
|
||||
@searchable={{searchable}}
|
||||
{{#if (gt services.length 0) }}
|
||||
<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'}}
|
||||
|
@ -28,18 +29,28 @@
|
|||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<ChangeableSet @dispatcher={{searchable 'service' (sort-by sort.selected.key services)}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulServiceList @routeName="dc.services.show" @items={{sort-by sort.selected.key filtered}} @proxies={{proxies}}/>
|
||||
<ConsulServiceList @routeName="dc.services.show" @items={{filtered}} @proxies={{proxies}}/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<EmptyState @allowLogin={{true}}>
|
||||
<BlockSlot @name="header">
|
||||
<h2>Welcome to Services</h2>
|
||||
<h2>
|
||||
{{#if (gt services.length 0)}}
|
||||
No services found
|
||||
{{else}}
|
||||
Welcome to Services
|
||||
{{/if}}
|
||||
</h2>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="body">
|
||||
<p>
|
||||
There don't seem to be any registered services, or you may not have access to view services yet.
|
||||
{{#if (gt services.length 0)}}
|
||||
No services where found matching that search, or you may not have access to view the services you are searching for.
|
||||
{{else}}
|
||||
There don't seem to be any registered services, or you may not have access to view services yet.
|
||||
{{/if}}
|
||||
</p>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="actions">
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
<div role="tabpanel">
|
||||
{{#if (gt items.length 0) }}
|
||||
<input type="checkbox" id="toolbar-toggle" />
|
||||
<form class="filter-bar">
|
||||
<FreetextFilter @searchable={{searchable}} @value={{s}} @placeholder="Search" />
|
||||
</form>
|
||||
<SearchBar
|
||||
@value={{search}}
|
||||
@onsearch={{action (mut search) value="target.value"}}
|
||||
/>
|
||||
{{/if}}
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<ChangeableSet @dispatcher={{searchable 'serviceInstance' items}} @terms={{search}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulServiceInstanceList @routeName="dc.services.instance" @items={{filtered}} @proxies={{keyedProxies}}/>
|
||||
</BlockSlot>
|
||||
|
|
|
@ -1,12 +1,32 @@
|
|||
{{#let (filter-by "Action" "deny" intentions) as |denied|}}
|
||||
{{#let (selectable-key-values
|
||||
(array "" (concat "All (" intentions.length ")"))
|
||||
(array "allow" (concat "Allow (" (sub intentions.length denied.length) ")"))
|
||||
(array "deny" (concat "Deny (" denied.length ")"))
|
||||
selected=filterBy
|
||||
)
|
||||
as |filter|
|
||||
}}
|
||||
<div id="intentions" class="tab-section">
|
||||
<div role="tabpanel">
|
||||
{{#if (gt intentions.length 0) }}
|
||||
<input type="checkbox" id="toolbar-toggle" />
|
||||
<form class="filter-bar">
|
||||
<FreetextFilter @searchable={{searchable}} @value={{s}} @placeholder="Search" />
|
||||
</form>
|
||||
<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}}>
|
||||
<ChangeableSet
|
||||
@dispatcher={{
|
||||
searchable
|
||||
'intention'
|
||||
(if (eq filter.selected.key "") intentions (filter-by "Action" filter.selected.key intentions))
|
||||
}}
|
||||
@terms={{search}}
|
||||
>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulIntentionList
|
||||
@items={{filtered}}
|
||||
|
@ -21,3 +41,5 @@
|
|||
</ChangeableSet>
|
||||
</div>
|
||||
</div>
|
||||
{{/let}}
|
||||
{{/let}}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import { get } from '@ember/object';
|
||||
export default function(checks, status) {
|
||||
let num = 0;
|
||||
switch (status) {
|
||||
case 'passing':
|
||||
case 'critical':
|
||||
case 'warning':
|
||||
num = get(checks.filterBy('Status', status), 'length');
|
||||
break;
|
||||
case '': // all
|
||||
num = 1;
|
||||
break;
|
||||
}
|
||||
return num > 0;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { get } from '@ember/object';
|
||||
export default function(items) {
|
||||
return items.reduce(function(sum, check) {
|
||||
const status = get(check, 'Status');
|
||||
return status === 'critical' || status === 'warning' ? sum + 1 : sum;
|
||||
}, 0);
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | acl filter', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`{{acl-filter}}`);
|
||||
|
||||
assert.dom('*').hasText('Search');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
{{#acl-filter}}{{/acl-filter}}
|
||||
`);
|
||||
|
||||
assert.dom('*').hasText('Search');
|
||||
});
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | catalog filter', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`{{catalog-filter}}`);
|
||||
|
||||
assert.equal(this.$().find('form').length, 1);
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
{{#catalog-filter}}{{/catalog-filter}}
|
||||
`);
|
||||
|
||||
assert.equal(this.$().find('form').length, 1);
|
||||
});
|
||||
});
|
|
@ -1,26 +0,0 @@
|
|||
import { module, skip } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | catalog-toolbar', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
skip('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`<CatalogToolbar />`);
|
||||
|
||||
assert.equal(this.element.querySelector('form').length, 1);
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<CatalogToolbar>
|
||||
template block text
|
||||
</CatalogToolbar>
|
||||
`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), 'template block text');
|
||||
});
|
||||
});
|
|
@ -1,4 +1,4 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { module, skip } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
@ -6,7 +6,7 @@ import hbs from 'htmlbars-inline-precompile';
|
|||
module('Integration | Component | changeable set', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
skip('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import hbs from 'htmlbars-inline-precompile';
|
||||
|
||||
module('Integration | Component | intention filter', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.on('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`{{intention-filter}}`);
|
||||
|
||||
assert.dom('*').hasText('Search');
|
||||
|
||||
// // Template block usage:
|
||||
// this.render(hbs`
|
||||
// {{#intention-filter}}
|
||||
// template block text
|
||||
// {{/intention-filter}}
|
||||
// `);
|
||||
|
||||
// assert.equal(this.$().text().trim(), 'template block text');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | search-bar', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
this.set('search', function(e) {});
|
||||
|
||||
await render(hbs`<SearchBar @onsearch={{action search}}/>`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), 'Search');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<SearchBar @onsearch={{action search}}></SearchBar>
|
||||
`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), 'Search');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
import { module, skip } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Helper | searchable', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
// Replace this with your real tests.
|
||||
skip('it renders', async function(assert) {
|
||||
this.set('inputValue', '1234');
|
||||
|
||||
await render(hbs`{{searchable inputValue}}`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), '1234');
|
||||
});
|
||||
});
|
|
@ -1,5 +1,7 @@
|
|||
import { is, clickable } from 'ember-cli-page-object';
|
||||
import ucfirst from 'consul-ui/utils/ucfirst';
|
||||
// TODO: We no longer need to use name here
|
||||
// remove the arg in all objects
|
||||
export default function(name, items, blankKey = 'all') {
|
||||
return items.reduce(function(prev, item, i, arr) {
|
||||
// if item is empty then it means 'all'
|
||||
|
@ -18,9 +20,9 @@ export default function(name, items, blankKey = 'all') {
|
|||
...{
|
||||
[`${key}IsSelected`]: is(
|
||||
':checked',
|
||||
`[data-test-radiobutton="${name}_${item}"] > input[type="radio"]`
|
||||
`[data-test-radiobutton$="_${item}"] > input[type="radio"]`
|
||||
),
|
||||
[key]: clickable(`[data-test-radiobutton="${name}_${item}"]`),
|
||||
[key]: clickable(`[data-test-radiobutton$="_${item}"]`),
|
||||
},
|
||||
};
|
||||
}, {});
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
import EmberObject from '@ember/object';
|
||||
import WithFilteringMixin from 'consul-ui/mixins/with-filtering';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Mixin | with filtering', function() {
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
let WithFilteringObject = EmberObject.extend(WithFilteringMixin);
|
||||
let subject = WithFilteringObject.create();
|
||||
assert.ok(subject);
|
||||
});
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
import EmberObject from '@ember/object';
|
||||
import WithHealthFilteringMixin from 'consul-ui/mixins/with-health-filtering';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Mixin | with health filtering', function() {
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
let WithHealthFilteringObject = EmberObject.extend(WithHealthFilteringMixin);
|
||||
let subject = WithHealthFilteringObject.create();
|
||||
assert.ok(subject);
|
||||
});
|
||||
});
|
|
@ -1,23 +0,0 @@
|
|||
import { module } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import test from 'ember-sinon-qunit/test-support/test';
|
||||
import Controller from '@ember/controller';
|
||||
import Mixin from 'consul-ui/mixins/with-searching';
|
||||
|
||||
module('Unit | Mixin | with searching', function(hooks) {
|
||||
setupTest(hooks);
|
||||
|
||||
hooks.beforeEach(function() {
|
||||
this.subject = function() {
|
||||
const MixedIn = Controller.extend(Mixin);
|
||||
this.owner.register('test-container:with-searching-object', MixedIn);
|
||||
return this.owner.lookup('test-container:with-searching-object');
|
||||
};
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
const subject = this.subject();
|
||||
assert.ok(subject);
|
||||
});
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
import hasStatus from 'consul-ui/utils/hasStatus';
|
||||
import { module, test, skip } from 'qunit';
|
||||
|
||||
module('Unit | Utility | has status', function() {
|
||||
const checks = {
|
||||
filterBy: function(prop, value) {
|
||||
return { length: 0 };
|
||||
},
|
||||
};
|
||||
test('it returns true when passing an empty string (therefore "all")', function(assert) {
|
||||
assert.ok(hasStatus(checks, ''));
|
||||
});
|
||||
test('it returns false when passing an actual status', function(assert) {
|
||||
['passing', 'critical', 'warning'].forEach(function(item) {
|
||||
assert.ok(!hasStatus(checks, item), `, with ${item}`);
|
||||
});
|
||||
});
|
||||
skip('it works as a factory, passing ember `get` in to create the function');
|
||||
});
|
|
@ -1,93 +0,0 @@
|
|||
import sumOfUnhealthy from 'consul-ui/utils/sumOfUnhealthy';
|
||||
import { module, test, skip } from 'qunit';
|
||||
|
||||
module('Unit | Utility | sum of unhealthy', function() {
|
||||
test('it returns the correct single count', function(assert) {
|
||||
const expected = 1;
|
||||
[
|
||||
[
|
||||
{
|
||||
Status: 'critical',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
Status: 'warning',
|
||||
},
|
||||
],
|
||||
].forEach(function(checks) {
|
||||
const actual = sumOfUnhealthy(checks);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
});
|
||||
test('it returns the correct single count when there are none', function(assert) {
|
||||
const expected = 0;
|
||||
[
|
||||
[
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
],
|
||||
].forEach(function(checks) {
|
||||
const actual = sumOfUnhealthy(checks);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
});
|
||||
test('it returns the correct multiple count', function(assert) {
|
||||
const expected = 3;
|
||||
[
|
||||
[
|
||||
{
|
||||
Status: 'critical',
|
||||
},
|
||||
{
|
||||
Status: 'warning',
|
||||
},
|
||||
{
|
||||
Status: 'warning',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
{
|
||||
Status: 'critical',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
{
|
||||
Status: 'warning',
|
||||
},
|
||||
{
|
||||
Status: 'warning',
|
||||
},
|
||||
{
|
||||
Status: 'passing',
|
||||
},
|
||||
],
|
||||
].forEach(function(checks) {
|
||||
const actual = sumOfUnhealthy(checks);
|
||||
assert.equal(actual, expected);
|
||||
});
|
||||
});
|
||||
skip('it works as a factory, passing ember `get` in to create the function');
|
||||
});
|
Loading…
Reference in New Issue