ui: Search/filtering 'Filtered by:' search status (#9442)

Adds a 'status' for the filtering/searching in the UI, without this its not super clear that you are filtering a recordset due to the menu selections being hidden once closed. You can also use the pills in this status view to delete individual filters.
This commit is contained in:
John Cowen 2021-01-25 18:13:54 +00:00 committed by GitHub
parent e3f5a77ade
commit 148b18b28c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
82 changed files with 3117 additions and 1946 deletions

4
.changelog/9442.txt Normal file
View File

@ -0,0 +1,4 @@
```release-note:feature
ui: Add additional search/filter status pills for viewing and removing current
filters in listing views
```

View File

@ -31,6 +31,8 @@ module.exports = {
'no-unnecessary-component-helper': false,
'link-href-attributes': false,
// we need to be able to say tabindex={{@tabindex}}
'no-positive-tabindex': false,
'no-bare-strings': false,
},

View File

@ -19,9 +19,9 @@
{{~/if~}}
{{~else~}}
<button
tabindex="-1"
type="button"
{{on 'click' (optional @onclick)}}
tabindex={{@tabindex}}
...attributes
>{{yield}}</button>
{{~/if}}

View File

@ -14,6 +14,7 @@
<YieldSlot @name="confirm" @params={{
block-params (component 'action'
onclick=(action @onclick)
tabindex="-1"
)
}}
>

View File

@ -1,59 +1,127 @@
<form
class="consul-acl-search-bar filter-bar"
<SearchBar
class="consul-acl-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.acl.search-bar." search.status.key)
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.acl.search-bar." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
(concat "common.brand." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
/>
</div>
<div class="filters">
<PopoverSelect
@placeholder={{t "common.search.search"}}
>
{{#if @filter.searchproperty}}
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</search.Select>
{{/if}}
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-status"
@position="left"
@onchange={{action @onfilter.kind}}
@onchange={{action @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
{{t "components.consul.acl.search-bar.kind.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="management" @selected={{contains 'management' @filter.kinds}}>Management</Option>
<Option @value="client" @selected={{contains 'service' @filter.kinds}}>Client</Option>
{{#each (array "management" "client") as |state|}}
<Option class="value-{{state}}" @value={{state}} @selected={{contains state @filter.status.value}}>
{{t (concat "components.acl.search-bar.kind.options." state)
default=(array
(concat "common.search." state)
)
}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<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 @label={{t "common.consul.name"}}>
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,140 +1,190 @@
<form
class="consul-health-check-search-bar filter-bar"
<SearchBar
class="consul-healthcheck-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.health-check.search-bar." search.status.key ".name")
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.health-check.search-bar." search.status.key ".options." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
{{#if @filter.searchproperty}}
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
{{#each @searchproperties as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperties}}>{{prop}}</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
</search.Select>
{{/if}}
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-status"
@position="left"
@onchange={{action @onfilter.status}}
@onchange={{action @filter.status.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Health Status
{{t "common.consul.status"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option class="value-passing" @value="passing" @selected={{contains 'passing' @filter.statuses}}>Passing</Option>
<Option class="value-warning" @value="warning" @selected={{contains 'warning' @filter.statuses}}>Warning</Option>
<Option class="value-critical" @value="critical" @selected={{contains 'critical' @filter.statuses}}>Failing</Option>
<Option class="value-empty" @value="empty" @selected={{contains 'empty' @filter.statuses}}>No checks</Option>
{{#each (array "passing" "warning" "critical" "empty") as |state|}}
<Option class="value-{{state}}" @value={{state}} @selected={{contains state @filter.status.value}}>
{{t (concat "common.consul." state)
default=(array
(concat "common.search." state)
)
}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
<PopoverSelect
</search.Select>
{{#if @filter.kind}}
<search.Select
class="type-kind"
@position="left"
@onchange={{action @onfilter.kind}}
@onchange={{action @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Kind
{{t "components.consul.health-check.search-bar.kind.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="service" @selected={{contains 'service' @filter.kinds}}>Service Check</Option>
<Option @value="node" @selected={{contains 'node' @filter.kinds}}>Node Check</Option>
{{#each (array "service" "node") as |item|}}
<Option @value={{item}} @selected={{contains item @filter.kind.value}}>
{{t (concat "components.consul.health-check.search-bar.kind.options." item)
default=(array
(concat "common.search." item)
)
}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
<PopoverSelect
</search.Select>
{{/if}}
<search.Select
class="type-check"
@position="left"
@onchange={{action @onfilter.check}}
@onchange={{action @filter.check.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
{{t "components.consul.health-check.search-bar.check.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="alias" @selected={{contains 'alias' @filter.checks}}>alias</Option>
<Option @value="docker" @selected={{contains 'docker' @filter.checks}}>Docker</Option>
<Option @value="grpc" @selected={{contains 'grpc' @filter.checks}}>gRPC</Option>
<Option @value="http" @selected={{contains 'http' @filter.checks}}>HTTP</Option>
<Option @value="serf" @selected={{contains 'serf' @filter.checks}}>Serf</Option>
<Option @value="tcp" @selected={{contains 'tcp' @filter.checks}}>TCP</Option>
<Option @value="ttl" @selected={{contains 'ttl' @filter.checks}}>TTL</Option>
{{#each (array "alias" "docker" "grpc" "http" "script" "serf" "tcp" "ttl") as |item|}}
<Option @value={{item}} @selected={{contains item @filter.check.value}}>
{{t (concat "components.consul.health-check.search-bar.check.options." item)
default=(array
(concat "common.search." item)
)
}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
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")
(array "Kind:asc" "Service to Node")
(array "Kind:desc" "Node to Service")
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
(array "Status:asc" (t "common.sort.status.asc"))
(array "Status:desc" (t "common.sort.status.desc"))
(array "Kind:asc" (t "components.consul.health-check.search-bar.sort.kind.asc"))
(array "Kind:desc" (t "components.consul.health-check.search-bar.sort.kind.desc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "common.consul.status"}}>
<Option @value="Status:asc" @selected={{eq "Status:asc" @sort.value}}>{{t "common.sort.status.asc"}}</Option>
<Option @value="Status:desc" @selected={{eq "Status:desc" @sort.value}}>{{t "common.sort.status.desc"}}</Option>
</Optgroup>
<Optgroup @label="Check 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 @label={{t "components.consul.health-check.search-bar.sort.name.name"}}>
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
</Optgroup>
<Optgroup @label="Check Type">
<Optgroup @label={{t "components.consul.health-check.search-bar.sort.kind.name"}}>
<Option @value="Kind:asc" @selected={{eq "Kind:asc" @sort}}>Service to Node</Option>
<Option @value="Kind:desc" @selected={{eq "Kind:desc" @sort}}>Node to Service</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,102 +1,140 @@
<form
class="consul-intention-search-bar filter-bar"
<SearchBar
class="consul-intention-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.intention.search-bar." search.status.key)
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.intention.search-bar." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
{{#if @filter.searchproperty}}
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="SourceName" @selected={{contains 'SourceName' @filter.searchproperties}}>Source Name</Option>
<Option @value="DestinationName" @selected={{contains 'DestinationName' @filter.searchproperties}}>Destination Name</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
</search.Select>
{{/if}}
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-access"
@position="left"
@onchange={{action @onfilter.access}}
@onchange={{action @filter.access.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Permission
{{t "components.consul.intention.search-bar.access.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option class="value-allow" @value="allow" @selected={{contains 'allow'
@filter.accesses}}><span>Allow</span></Option>
<Option class="value-deny" @value="deny" @selected={{contains 'deny'
@filter.accesses}}><span>Deny</span></Option>
<Option class="value-" @value="app-aware" @selected={{contains
'app-aware' @filter.accesses}}><span>App aware</span></Option>
{{#each (array "allow" "deny" "") as |item|}}
<Option class={{concat 'value-' item}} @value={{or item 'app-aware'}} @selected={{contains (or item 'app-aware') @filter.access.value}}>
<span>{{t (concat "components.consul.intention.search-bar.access.options." (or item 'app-aware'))}}</span>
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
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")
(array "Action:asc" (t "components.consul.intention.search-bar.sort.access.asc"))
(array "Action:desc" (t "components.consul.intention.search-bar.sort.access.desc"))
(array "SourceName:asc" (t "components.consul.intention.search-bar.sort.source-name.asc"))
(array "SourceName:desc" (t "components.consul.intention.search-bar.sort.source-name.desc"))
(array "DestinationName:asc" (t "components.consul.intention.search-bar.sort.destination-name.asc"))
(array "DestinationName:desc" (t "components.consul.intention.search-bar.sort.destination-name.desc"))
(array "Precedence:asc" (t "common.sort.numeric.asc"))
(array "Precedence:desc" (t "common.sort.numeric.desc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "components.consul.intention.search-bar.sort.access.name"}}>
<Option @value="Action:asc" @selected={{eq "Action:asc" @sort.value}}>{{t "components.consul.intention.search-bar.sort.access.asc"}}</Option>
<Option @value="Action:desc" @selected={{eq "Action:desc" @sort.value}}>{{t "components.consul.intention.search-bar.sort.access.desc"}}</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 @label={{t "components.consul.intention.search-bar.sort.source-name.name"}}>
<Option @value="SourceName:asc" @selected={{eq "SourceName:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="SourceName:desc" @selected={{eq "SourceName:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</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 @label={{t "components.consul.intention.search-bar.sort.destination-name.name"}}>
<Option @value="DestinationName:asc" @selected={{eq "DestinationName:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="DestinationName:desc" @selected={{eq "DestinationName:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</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 @label={{t "components.consul.intention.search-bar.sort.precedence.name"}}>
<Option @value="Precedence:asc" @selected={{eq "Precedence:asc" @sort.value}}>{{t "common.sort.numeric.asc"}}</Option>
<Option @value="Precedence:desc" @selected={{eq "Precedence:desc" @sort.value}}>{{t "common.sort.numeric.desc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,68 +1,128 @@
<form
class="consul-kv-search-bar filter-bar"
<SearchBar
class="consul-kv-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.kv.search-bar." search.status.key)
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.kv.search-bar." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
/>
</div>
<div class="filters">
<PopoverSelect
@placeholder={{t "common.search.search"}}
>
{{#if @filter.searchproperty}}
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</search.Select>
{{/if}}
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-kind"
@position="left"
@onchange={{action @onfilter.kind}}
@onchange={{action @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
{{t "components.consul.kv.search-bar.kind.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="folder" @selected={{contains 'folder' @filter.kinds}}>Folder</Option>
<Option @value="key" @selected={{contains 'key' @filter.kinds}}>Key</Option>
{{#each (array "folder" "key") as |item|}}
<Option class="value-{item}}" @value={{item}} @selected={{contains item @filter.kind.value}}>
{{t (concat "components.consul.kv.search-bar.kind.options." item)}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Key:asc" "A to Z")
(array "Key:desc" "Z to A")
(array "Kind:asc" "Folders to Keys")
(array "Kind:desc" "Keys to Folders")
(array "Key:asc" (t "common.sort.alpha.asc"))
(array "Key:desc" (t "common.sort.alpha.desc"))
(array "Kind:asc" (t "components.consul.kv.search-bar.sort.kind.asc"))
(array "Kind:desc" (t "components.consul.kv.search-bar.sort.kind.desc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "common.consul.name"}}>
<Option @value="Key:asc" @selected={{eq "Key:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="Key:desc" @selected={{eq "Key:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
</Optgroup>
<Optgroup @label="Type">
<Option @value="Kind:asc" @selected={{eq "Kind:asc" @sort}}>Folders to Keys</Option>
<Option @value="Kind:desc" @selected={{eq "Kind:desc" @sort}}>Keys to Folders</Option>
<Optgroup @label={{t "components.consul.kv.search-bar.kind.name"}}>
<Option @value="Kind:asc" @selected={{eq "Kind:asc" @sort.value}}>{{t "components.consul.kv.search-bar.sort.kind.asc"}}</Option>
<Option @value="Kind:desc" @selected={{eq "Kind:desc" @sort.value}}>{{t "components.consul.kv.search-bar.sort.kind.desc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,90 +1,131 @@
<form
class="consul-node-search-bar filter-bar"
<SearchBar
class="consul-node-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.node.search-bar." search.status.key)
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.node.search-bar." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
(concat "common.brand." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="Node" @selected={{contains 'Node' @filter.searchproperties}}>Node Name</Option>
<Option @value="Address" @selected={{contains 'Address' @filter.searchproperties}}>Address</Option>
<Option @value="Meta" @selected={{contains 'Meta' @filter.searchproperties}}>Node Meta</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
</search.Select>
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-status"
@position="left"
@onchange={{action @onfilter.status}}
@onchange={{action @filter.status.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Health Status
{{t "common.consul.status"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option class="value-passing" @value="passing" @selected={{contains 'passing' @filter.statuses}}>Passing</Option>
<Option class="value-warning" @value="warning" @selected={{contains 'warning' @filter.statuses}}>Warning</Option>
<Option class="value-critical" @value="critical" @selected={{contains 'critical' @filter.statuses}}>Failing</Option>
<Option class="value-empty" @value="empty" @selected={{contains 'empty' @filter.statuses}}>No checks</Option>
{{#each (array "passing" "warning" "critical") as |state|}}
<Option class="value-{{state}}" @value={{state}} @selected={{contains state @filter.status.value}}>
{{t (concat "common.consul." state)
default=(array
(concat "common.search." state)
)
}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Node:asc" "A to Z")
(array "Node:desc" "Z to A")
(array "Status:asc" "Unhealthy to Healthy")
(array "Status:desc" "Healthy to Unhealthy")
(array "Node:asc" (t "common.sort.alpha.asc"))
(array "Node:desc" (t "common.sort.alpha.desc"))
(array "Status:asc" (t "common.sort.status.asc"))
(array "Status:desc" (t "common.sort.status.desc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "common.consul.status"}}>
<Option @value="Status:asc" @selected={{eq "Status:asc" @sort.value}}>{{t "common.sort.status.asc"}}</Option>
<Option @value="Status:desc" @selected={{eq "Status:desc" @sort.value}}>{{t "common.sort.status.desc"}}</Option>
</Optgroup>
<Optgroup @label="Service Name">
<Option @value="Node:asc" @selected={{eq "Node:asc" @sort}}>A to Z</Option>
<Option @value="Node:desc" @selected={{eq "Node:desc" @sort}}>Z to A</Option>
<Optgroup @label={{t "common.consul.node-name"}}>
<Option @value="Node:asc" @selected={{eq "Node:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="Node:desc" @selected={{eq "Node:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,63 +1,98 @@
<form
class="consul-nspace-search-bar filter-bar"
<SearchBar
class="consul-nspace-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.nspace.search-bar." search.status.key)
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.nspace.search-bar." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
(concat "common.brand." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="Name" @selected={{contains 'Name' @filter.searchproperties}}>Name</Option>
<Option @value="Description" @selected={{contains 'Description' @filter.searchproperties}}>Description</Option>
<Option @value="Policy" @selected={{contains 'Policy' @filter.searchproperties}}>Policy</Option>
<Option @value="Role" @selected={{contains 'Role' @filter.searchproperties}}>Role</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="sort">
<PopoverSelect
</search.Select>
</search.Search>
</:search>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "common.consul.name"}}>
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,101 +1,145 @@
<form
class="consul-policy-search-bar filter-bar"
<SearchBar
class="consul-policy-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.policy.search-bar." search.status.key ".name")
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.policy.search-bar." search.status.key ".options." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
(concat "common.brand." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{if (eq search.status.key 'datacenter') search.status.value value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="Name" @selected={{contains 'Name' @filter.searchproperties}}>Name</Option>
<Option @value="Description" @selected={{contains 'Description' @filter.searchproperties}}>Description</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
class="select-dcs"
</search.Select>
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-datacenter"
@position="left"
@onchange={{action @onfilter.dc}}
@onchange={{action @filter.datacenter.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Datacenters
{{t "common.consul.datacenter"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
{{#each dcs as |dc|}}
<Option @value={{@dc.Name}} @selected={{contains dc.Name @filter.dcs}}>{{dc.Name}}</Option>
<Option @value={{dc.Name}} @selected={{contains dc.Name @filter.datacenter.value}}>{{dc.Name}}</Option>
{{/each}}
{{/let}}
<DataSource @src="/*/*/datacenters" @loading="lazy" @onchange={{action (mut this.dcs) value="data"}} />
{{/let}}
</BlockSlot>
</PopoverSelect>
<PopoverSelect
class="select-type"
</search.Select>
<search.Select
class="type-kind"
@position="left"
@onchange={{action @onfilter.kind}}
@onchange={{action @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
{{t "components.consul.policy.search-bar.kind.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="global-management" @selected={{contains 'global-management' @filter.kinds}}>Global Management</Option>
<Option @value="standard" @selected={{contains 'standard' @filter.kinds}}>Standard</Option>
{{#each (array "global-management" "standard") as |state|}}
<Option class="value-{{state}}" @value={{state}} @selected={{contains state @filter.kind.value}}>
{{t (concat "components.consul.policy.search-bar.kind.options." state)
default=(array
(concat "common.search." state)
)
}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "common.ui.name"}}>
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,68 +1,104 @@
<form
class="consul-role-search-bar filter-bar"
<SearchBar
class="consul-role-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.role.search-bar." search.status.key ".name")
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.role.search-bar." search.status.key ".options." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
(concat "common.brand." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="Name" @selected={{contains 'Name' @filter.searchproperties}}>Name</Option>
<Option @value="Description" @selected={{contains 'Description' @filter.searchproperties}}>Description</Option>
<Option @value="Policy" @selected={{contains 'Policy' @filter.searchproperties}}>Policy</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="sort">
<PopoverSelect
</search.Select>
</search.Search>
</:search>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
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")
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
(array "CreateIndex:desc" (t "common.sort.age.desc"))
(array "CreateIndex:asc" (t "common.sort.age.asc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "common.ui.name"}}>
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</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 @label={{t "common.ui.creation"}}>
<Option @value="CreateIndex:desc" @selected={{eq "CreateIndex:desc" @sort.value}}>{{t "common.sort.age.desc"}}</Option>
<Option @value="CreateIndex:asc" @selected={{eq "CreateIndex:asc" @sort.value}}>{{t "common.sort.age.asc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,111 +1,156 @@
<form
class="consul-service-instance-search-bar filter-bar"
<SearchBar
class="consul-service-instance-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.service-instance.search-bar." search.status.key ".name")
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.service-instance.search-bar." search.status.key ".options." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
(concat "common.brand." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
{{#if @filter.searchproperty}}
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
{{#each @searchproperties as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperties}}>{{string-replace-all prop '\.' ' '}}</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
</search.Select>
{{/if}}
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-status"
@position="left"
@onchange={{action @onfilter.status}}
@onchange={{action @filter.status.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Health Status
{{t "common.consul.status"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option class="value-passing" @value="passing" @selected={{contains 'passing' @filter.statuses}}>Passing</Option>
<Option class="value-warning" @value="warning" @selected={{contains 'warning' @filter.statuses}}>Warning</Option>
<Option class="value-critical" @value="critical" @selected={{contains 'critical' @filter.statuses}}>Failing</Option>
<Option class="value-empty" @value="empty" @selected={{contains 'empty' @filter.statuses}}>No checks</Option>
{{#each (array "passing" "warning" "critical" "empty") as |state|}}
<Option class="value-{{state}}" @value={{state}} @selected={{contains state @filter.status.value}}>
{{t (concat "common.consul." state)
default=(array
(concat "common.search." state)
)
}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</search.Select>
{{#if (gt @sources.length 0)}}
<PopoverSelect
<search.Select
class="type-source"
@position="left"
@onchange={{action @onfilter.source}}
@onchange={{action @filter.source.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Source
{{t "common.search.source"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
{{#each @sources as |source|}}
<Option class={{source}} @value={{source}} @selected={{contains source @filter.sources}}>{{source}}</Option>
<Option class={{source}} @value={{source}} @selected={{contains source @filter.source.value}}>
{{t (concat "common.brand." source)}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</search.Select>
{{/if}}
</div>
<div class="sort">
<PopoverSelect
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
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")
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
(array "Status:asc" (t "common.sort.status.asc"))
(array "Status:desc" (t "common.sort.status.desc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "common.consul.status"}}>
<Option @value="Status:asc" @selected={{eq "Status:asc" @sort.value}}>{{t "common.sort.status.asc"}}</Option>
<Option @value="Status:desc" @selected={{eq "Status:desc" @sort.value}}>{{t "common.sort.status.desc"}}</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 @label={{t "components.consul.service-instance.search-bar.sort.name.name"}}>
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,135 +1,190 @@
<form
class="consul-service-search-bar filter-bar"
<SearchBar
class="consul-service-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.service.search-bar." search.status.key)
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.service.search-bar." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
(concat "common.brand." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="Name" @selected={{contains 'Name' @filter.searchproperties}}>Name</Option>
<Option @value="Tags" @selected={{contains 'Tags' @filter.searchproperties}}>Tags</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
</search.Select>
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-status"
@position="left"
@onchange={{action @onfilter.status}}
@onchange={{action @filter.status.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Health Status
{{t "common.consul.status"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option class="value-passing" @value="passing" @selected={{contains 'passing' @filter.statuses}}>Passing</Option>
<Option class="value-warning" @value="warning" @selected={{contains 'warning' @filter.statuses}}>Warning</Option>
<Option class="value-critical" @value="critical" @selected={{contains 'critical' @filter.statuses}}>Failing</Option>
<Option class="value-empty" @value="empty" @selected={{contains 'empty' @filter.statuses}}>No checks</Option>
{{#each (array "passing" "warning" "critical" "empty") as |state|}}
<Option class="value-{{state}}" @value={{state}} @selected={{contains state @filter.status.value}}>
{{t (concat "common.consul." state)
default=(array
(concat "common.search." state)
)
}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
<PopoverSelect
</search.Select>
<search.Select
@position="left"
@onchange={{action @onfilter.kind}}
@onchange={{action @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Service Type
{{t "components.consul.service.search-bar.kind"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="service" @selected={{contains 'service' @filter.kinds}}>Service</Option>
<Optgroup @label="Gateway">
<Option @value="ingress-gateway" @selected={{contains 'ingress-gateway' @filter.kinds}}>Ingress Gateway</Option>
<Option @value="terminating-gateway" @selected={{contains 'terminating-gateway' @filter.kinds}}>Terminating Gateway</Option>
<Option @value="mesh-gateway" @selected={{contains 'mesh-gateway' @filter.kinds}}>Mesh Gateway</Option>
<Option @value="service" @selected={{contains 'service' @filter.kind.value}}>
{{t "common.consul.service"}}
</Option>
<Optgroup
@label={{t "common.consul.gateway"}}
>
{{#each (array "ingress-gateway" "terminating-gateway" "mesh-gateway") as |kind|}}
<Option @value={{kind}} @selected={{contains kind @filter.kind.value}}>
{{t (concat "common.consul." kind)}}
</Option>
{{/each}}
</Optgroup>
<Optgroup @label="Mesh">
<Option @value="in-mesh" @selected={{contains 'in-mesh' @filter.kinds}}>In service mesh</Option>
<Option @value="not-in-mesh" @selected={{contains 'not-in-mesh' @filter.kinds}}>Not in service mesh</Option>
<Optgroup
@label={{t "common.consul.mesh"}}
>
{{#each (array "in-mesh" "not-in-mesh") as |state|}}
<Option @value={{state}} @selected={{contains state @filter.kind.value}}>
{{t (concat "common.search." state)}}
</Option>
{{/each}}
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</search.Select>
{{#if (gt @sources.length 0)}}
<PopoverSelect
<search.Select
class="type-source"
@position="left"
@onchange={{action @onfilter.source}}
@onchange={{action @filter.source.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Source
{{t "common.search.source"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
{{#each @sources as |source|}}
<Option class={{source}} @value={{source}} @selected={{contains source @filter.sources}}>{{source}}</Option>
<Option class={{source}} @value={{source}} @selected={{contains source @filter.source.value}}>
{{t (concat "common.brand." source)}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</search.Select>
{{/if}}
</div>
<div class="sort">
<PopoverSelect
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
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")
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
(array "Status:asc" (t "common.sort.status.asc"))
(array "Status:desc" (t "common.sort.status.desc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "common.consul.status"}}>
<Option @value="Status:asc" @selected={{eq "Status:asc" @sort.value}}>{{t "common.sort.status.asc"}}</Option>
<Option @value="Status:desc" @selected={{eq "Status:desc" @sort.value}}>{{t "common.sort.status.desc"}}</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 @label={{t "common.consul.service-name"}}>
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,83 +1,125 @@
<form
class="consul-token-search-bar filter-bar"
<SearchBar
class="consul-token-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.token.search-bar." search.status.key ".name")
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.token.search-bar." search.status.key ".options." search.status.value)
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
(concat "common.brand." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="AccessorID" @selected={{contains 'AccessorID' @filter.searchproperties}}>AccessorID</Option>
<Option @value="Description" @selected={{contains 'Description' @filter.searchproperties}}>Description</Option>
<Option @value="Policy" @selected={{contains 'Policy' @filter.searchproperties}}>Policy</Option>
<Option @value="Role" @selected={{contains 'Role' @filter.searchproperties}}>Role</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
</search.Select>
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-status"
@position="left"
@onchange={{action @onfilter.kind}}
@onchange={{action @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
{{t "components.consul.token.search-bar.kind.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="global-management" @selected={{contains 'global-management' @filter.kinds}}>Global Management</Option>
<Option @value="global" @selected={{contains 'global' @filter.kinds}}>Global Scope</Option>
<Option @value="local" @selected={{contains 'local' @filter.kinds}}>Local Scope</Option>
{{#each (array "global-management" "global" "local") as |state|}}
<Option class="value-{{state}}" @value={{state}} @selected={{contains state @filter.kind.value}}>
{{t (concat "components.consul.token.search-bar.kind.options." state)
default=(array
(concat "common.search." state)
)
}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "CreateTime:desc" "Newest to oldest")
(array "CreateTime:asc" "Oldest to newest")
(array "CreateTime:desc" (t "common.sort.age.desc"))
(array "CreateTime:asc" (t "common.sort.age.asc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "common.ui.creation"}}>
<Option @value="CreateTime:desc" @selected={{eq "CreateTime:desc" @sort.value}}>{{t "common.sort.age.desc"}}</Option>
<Option @value="CreateTime:asc" @selected={{eq "CreateTime:asc" @sort.value}}>{{t "common.sort.age.asc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,63 +1,96 @@
<form
class="consul-upstream-instance-search-bar filter-bar"
<SearchBar
class="consul-upstream-instance-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.upstream-instance.search-bar." search.status.key ".name")
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.upstream-instance.search-bar." search.status.value ".name")
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
(concat "common.brand." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
{{#each @searchproperties as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperties}}>{{prop}}</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="sort">
{{#let (or @sort 'DestinationName:asc') as |sort|}}
<PopoverSelect
</search.Select>
</search.Search>
</:search>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "DestinationName:asc" "A to Z")
(array "DestinationName:desc" "Z to A")
(array "DestinationName:asc" (t "common.sort.alpha.asc"))
(array "DestinationName:desc" (t "common.sort.alpha.desc"))
))
as |selectable|}}
{{get selectable sort}}
as |selectable|
}}
{{get selectable @sort.value}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Service Name">
<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>
<Option @value="DestinationName:asc" @selected={{eq "DestinationName:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="DestinationName:desc" @selected={{eq "DestinationName:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
{{/let}}
</BlockSlot>
</PopoverSelect>
{{/let}}
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,86 +1,126 @@
<form
class="consul-upstream-search-bar filter-bar"
<SearchBar
class="consul-upstream-search-bar"
...attributes
@filter={{@filter}}
>
<div class="search">
<FreetextFilter
<:status as |search|>
{{#let
(t (concat "components.consul.upstream.search-bar." search.status.key ".name")
default=(array
(concat "common.search." search.status.key)
(concat "common.consul." search.status.key)
)
)
(t (concat "components.consul.upstream.search-bar." search.status.value ".name")
default=(array
(concat "common.search." search.status.value)
(concat "common.consul." search.status.value)
(concat "common.brand." search.status.value)
)
)
as |key value|}}
<search.RemoveFilter
aria-label={{t "common.ui.remove" item=(concat key " " value)}}
>
<dl>
<dt>{{key}}</dt>
<dd>{{value}}</dd>
</dl>
</search.RemoveFilter>
{{/let}}
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
@placeholder={{t "common.search.search"}}
>
<PopoverSelect
<search.Select
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
@onchange={{action @filter.searchproperty.change}}
@multiple={{true}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.search.searchproperty"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="Name" @selected={{contains 'Name' @filter.searchproperties}}>Name</Option>
<Option @value="Tags" @selected={{contains 'Tags' @filter.searchproperties}}>Tags</Option>
{{#each @filter.searchproperty.default as |prop|}}
<Option @value={{prop}} @selected={{contains prop @filter.searchproperty.value}}>
{{t (concat "common.consul." (lowercase prop))}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
</search.Select>
</search.Search>
</:search>
<:filter as |search|>
<search.Select
@position="left"
@onchange={{action @onfilter.instance}}
@onchange={{action @filter.instance.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
{{t "components.consul.upstream.search-bar.instance.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Option @value="registered" @selected={{contains 'registered' @filter.instances}}>Registered</Option>
<Option @value="not-registered" @selected={{contains 'not-registered' @filter.instances}}>Not Registered</Option>
{{#each (array "registered" "not-registered") as |item|}}
<Option @value={{item}} @selected={{contains item @filter.instance.value}}>
{{t (concat "common.consul." item)}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
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")
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
(array "Status:asc" (t "common.sort.status.asc"))
(array "Status:desc" (t "common.sort.status.desc"))
))
as |selectable|
}}
{{get selectable @sort}}
{{get selectable @sort.value}}
{{/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 @label={{t "common.consul.status"}}>
<Option @value="Status:asc" @selected={{eq "Status:asc" @sort.value}}>{{t "common.sort.status.asc"}}</Option>
<Option @value="Status:desc" @selected={{eq "Status:desc" @sort.value}}>{{t "common.sort.status.desc"}}</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 @label={{t "common.consul.service-name"}}>
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort.value}}>{{t "common.sort.alpha.asc"}}</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort.value}}>{{t "common.sort.alpha.desc"}}</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:sort>
</SearchBar>

View File

@ -1,6 +1,6 @@
import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { computed, action } from '@ember/object';
import { computed, get, action } from '@ember/object';
import { alias } from '@ember/object/computed';
import { tracked } from '@glimmer/tracking';
import { sort } from '@ember/object/computed';
@ -32,18 +32,19 @@ export default class DataCollectionComponent extends Component {
return this.term || this.args.search || '';
}
@computed('type', 'searchMethod', 'filtered', 'searchProperties')
@computed('type', 'searchMethod', 'filtered', 'args.filters')
get searchable() {
const searchproperties =
get(this, 'args.filters.searchproperty.value') || get(this, 'args.filters.searchproperty');
const Searchable =
typeof this.searchMethod === 'string'
? this.searchableMap[this.searchMethod]
: this.args.searchable;
return new Searchable(this.filtered, {
finders: Object.fromEntries(
Object.entries(this.searchService.predicate(this.type)).filter(([key, value]) => {
return typeof this.searchProperties === 'undefined'
? true
: this.searchProperties.includes(key);
return typeof searchproperties === 'undefined' ? true : searchproperties.includes(key);
})
),
});
@ -89,7 +90,13 @@ export default class DataCollectionComponent extends Component {
if (typeof predicate === 'undefined') {
return this.content.slice();
}
return this.content.filter(predicate(this.args.filters));
const filters = Object.entries(this.args.filters)
.filter(([key, value]) => Boolean(value))
.map(([key, value]) => {
const val = typeof value !== 'string' ? value.value : value;
return [key, val];
});
return this.content.filter(predicate(Object.fromEntries(filters)));
}
@computed('args.{items.[],items.content.[]}')

View File

@ -9,7 +9,7 @@
(component 'popover-select/optgroup' components=components)
(component 'popover-select/option'
select=this components=components
onclick=(queue
onclick=(pipe
(action "click")
(if multiple (noop) menu.toggle)
)

View File

@ -6,47 +6,50 @@ export default Component.extend(Slotted, {
tagName: '',
dom: service('dom'),
multiple: false,
subtractive: false,
required: false,
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);
}
}
this._options.add(option);
},
removeOption: function(option) {
this._options.delete(option.value);
this._options.delete(option);
},
actions: {
click: function(e, value) {
let options = [value];
if (this.multiple) {
if (this._options.has(value)) {
this._options.delete(value);
click: function(option, e) {
// required={{true}} ?
if (!this.multiple) {
if (option.selected && this.required) {
return e;
}
[...this._options]
.filter(item => item !== option)
.forEach(item => {
item.selected = false;
});
} else {
this._options.add(value);
if (option.selected && this.required) {
const other = [...this._options].find(item => item !== option && item.selected);
if (!other) {
return e;
}
options = this._options;
}
}
option.selected = !option.selected;
this.onchange(
this.dom.setEventTargetProperties(e, {
selected: target => value,
selected: target => option.args.value,
selectedItems: target => {
return [...options].join(',');
return [...this._options]
.filter(item => item.selected)
.map(item => item.args.value)
.join(',');
},
})
);
},
change: function(option, e) {
this.onchange(this.dom.setEventTargetProperty(e, 'selected', selected => option));
return e;
},
},
});

View File

@ -1,7 +1,7 @@
{{#let components.MenuSeparator as |MenuSeparator|}}
{{#let @components.MenuSeparator as |MenuSeparator|}}
<MenuSeparator>
<BlockSlot @name="label">
{{label}}
{{@label}}
</BlockSlot>
</MenuSeparator>
{{yield}}

View File

@ -1,5 +0,0 @@
import Component from '@ember/component';
export default Component.extend({
tagName: '',
});

View File

@ -1,9 +1,13 @@
{{#let components.MenuItem as |MenuItem|}}
{{#let @components.MenuItem as |MenuItem|}}
<MenuItem
class={{if selected 'is-active'}}
class={{if this.selected 'is-active'}}
{{did-insert this.connect}}
{{did-insert (set this "selected" @selected)}}
{{did-update (set this "selected" @selected)}}
{{will-destroy this.disconnect}}
...attributes
@onclick={{action 'click'}}
@selected={{selected}}
@onclick={{action @onclick this}}
@selected={{this.selected}}
>
<BlockSlot @name="label">
{{yield}}

View File

@ -1,20 +1,16 @@
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
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);
},
},
});
export default class Option extends Component {
@tracked selected;
@action
connect() {
this.args.select.addOption(this);
}
@action
disconnect() {
this.args.select.removeOption(this);
}
}

View File

@ -1,61 +0,0 @@
## 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)
---

View File

@ -1,31 +1,68 @@
{{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')}}
{{else}}
<RadioGroup
@keyboardAccess={{true}}
@value={{selected.key}}
@items={{options}}
@onchange={{action onchange}}
/>
{{/if}}
{{/if}}
{{/yield-slot}}
<div
class="search-bar"
...attributes
>
<form
class="filter-bar"
>
<div class="search">
{{yield (hash
Search=(component "freetext-filter")
Select=(component "popover-select")
) to="search"}}
</div>
<div class="filters">
{{yield (hash
Search=(component "freetext-filter")
Select=(component "popover-select")
) to="filter"}}
</div>
<div class="sort">
{{yield (hash
Search=(component "freetext-filter")
Select=(component "popover-select")
) to="sort"}}
</div>
</form>
{{#if this.isFiltered}}
<div class="search-bar-status">
<dl>
<dt>{{string-trim
(t "component.search-bar.header"
default="common.ui.filtered-by"
item=""
)
}}</dt>
<dd>
<ul>
{{#each this.filters as |filter|}}
{{yield (hash
RemoveFilter=(component "search-bar/remove-filter" onclick=(action
(get (get @filter filter.key) "change")
(hash
target=(hash
selectedItems=(join filter.selected ',')
)
))
)
status=(hash
key=filter.key
value=(lowercase filter.value)
)
)
to="status"
}}
{{/each}}
<li class="remove-all">
<Action
{{on "click" this.removeAllFilters}}
>
Remove filters
</Action>
</li>
</ul>
</dd>
</dl>
</div>
{{/if}}
</div>

View File

@ -1,6 +1,34 @@
import Component from '@ember/component';
import Slotted from 'block-slots';
import Component from '@glimmer/component';
import { action } from '@ember/object';
import { diff, filters } from './utils';
export default Component.extend(Slotted, {
tagName: '',
export default class SearchBar extends Component {
// only show the filter status bar if we have searchproperty filters or
// normal types of filters, and we are currently filtering by either of those
get isFiltered() {
const searchproperty = this.args.filter.searchproperty || { default: [], value: [] };
return (
diff(searchproperty.default, searchproperty.value).length > 0 ||
Object.entries(this.args.filter).some(([key, value]) => {
return key !== 'searchproperty' && typeof value.value !== 'undefined';
})
);
}
// convert the object based filters to an array of iterable filters ready for
// rendering
get filters() {
return filters(this.args.filter);
}
@action
removeAllFilters() {
Object.values(this.args.filter).forEach((value, i) => {
// put in a little queue to ensure query params are unset properly
// ideally this would be done outside of the component
// TODO: Look to see if this can be moved to serializeQueryParam
// so we we aren't polluting components with queryParam related things
setTimeout(() => value.change(value.default || []), 1 * i);
});
}
}

View File

@ -0,0 +1,60 @@
.search-bar {
&-status {
& {
border-bottom: $decor-border-100;
border-bottom-color: $gray-200;
}
.remove-all button {
@extend %anchor;
}
li:not(.remove-all) {
& {
@extend %pill-200;
border: $decor-border-100;
border-color: $gray-200;
color: $gray-600;
}
button {
cursor: pointer;
}
button::before {
@extend %with-cancel-plain-mask, %as-pseudo;
color: $gray-600;
margin-top: 1px;
margin-right: 0.2rem;
}
}
& {
padding: .5rem 0;
padding-left: .5rem;
}
dt::after {
content: ':';
padding-right: 0.3rem;
}
& > dl > dt {
float: left;
}
dt {
white-space: nowrap;
}
li {
display: inline-flex;
}
li:not(:last-child) {
margin-right: 0.3rem;
margin-bottom: 0.3rem;
}
li:not(.remove-all) {
& {
padding: 0 0.2rem;
}
dl {
display: flex;
}
button {
padding: 0;
}
}
}
}

View File

@ -1,7 +0,0 @@
export default (search, secondary = () => {}) => scope => {
return {
scope: scope,
...search(),
...secondary(),
};
};

View File

@ -0,0 +1,7 @@
<li>
<Action
...attributes
{{on 'click' @onclick}}
/>
{{yield}}
</li>

View File

@ -0,0 +1,38 @@
export const diff = (a, b) => {
return a.filter(item => !b.includes(item));
};
/**
* filters accepts the args.filter @attribute which is shaped like
* {filterName: {default: ['Node', 'Address'], value: ['Address']}, ...}
* It will turn this into an array of 'filters' shaped like
* [{key: 'filterName', value: 'Address', selected: ["Node"]}]
* importantly 'selected' isn't what is currently 'selected' it is what selected
* will be once you remove this filter
* There is more explanation in the unit tests for this function so thats worthwhile
* checking if you are in amongst this
*/
export const filters = filters => {
return Object.entries(filters)
.filter(([key, value]) => {
if (key === 'searchproperty') {
return diff(value.default, value.value).length > 0;
}
return (value.value || []).length > 0;
})
.reduce((prev, [key, value]) => {
return prev.concat(
value.value.map(item => {
const obj = {
key: key,
value: item,
};
if (key !== 'searchproperty') {
obj.selected = diff(value.value, [item]);
} else {
obj.selected = value.value.length === 1 ? value.default : diff(value.value, [item]);
}
return obj;
})
);
}, []);
};

View File

@ -1,5 +1,5 @@
export default {
kinds: {
kind: {
management: (item, value) => item.Type === value,
client: (item, value) => item.Type === value,
},

View File

@ -1,14 +1,14 @@
export default {
statuses: {
status: {
passing: (item, value) => item.Status === value,
warning: (item, value) => item.Status === value,
critical: (item, value) => item.Status === value,
},
kinds: {
kind: {
service: (item, value) => item.Kind === value,
node: (item, value) => item.Kind === value,
},
checks: {
check: {
serf: (item, value) => item.Type === '',
script: (item, value) => item.Type === value,
http: (item, value) => item.Type === value,

View File

@ -1,5 +1,5 @@
export default {
accesses: {
access: {
allow: (item, value) => item.Action === value,
deny: (item, value) => item.Action === value,
'app-aware': (item, value) => typeof item.Action === 'undefined',

View File

@ -1,5 +1,5 @@
export default {
kinds: {
kind: {
folder: (item, value) => item.isFolder,
key: (item, value) => !item.isFolder,
},

View File

@ -1,5 +1,5 @@
export default {
statuses: {
status: {
passing: (item, value) => item.Status === value,
warning: (item, value) => item.Status === value,
critical: (item, value) => item.Status === value,

View File

@ -1,11 +1,11 @@
import setHelpers from 'mnemonist/set';
export default {
kinds: {
kind: {
'global-management': (item, value) => item.isGlobalManagement,
standard: (item, value) => !item.isGlobalManagement,
},
dcs: (item, values) => {
datacenter: (item, values) => {
return (
typeof item.Datacenters === 'undefined' ||
setHelpers.intersectionSize(values, new Set(item.Datacenters)) > 0

View File

@ -1,12 +1,13 @@
import setHelpers from 'mnemonist/set';
export default {
statuses: {
status: {
passing: (item, value) => item.Status === value,
warning: (item, value) => item.Status === value,
critical: (item, value) => item.Status === value,
empty: (item, value) => item.MeshChecks.length === 0,
},
sources: (item, values) => {
source: (item, values) => {
return setHelpers.intersectionSize(values, new Set(item.ExternalSources || [])) !== 0;
},
};

View File

@ -1,7 +1,7 @@
import setHelpers from 'mnemonist/set';
export default {
kinds: {
kind: {
'ingress-gateway': (item, value) => item.Kind === value,
'terminating-gateway': (item, value) => item.Kind === value,
'mesh-gateway': (item, value) => item.Kind === value,
@ -9,16 +9,17 @@ export default {
'in-mesh': (item, value) => item.InMesh,
'not-in-mesh': (item, value) => !item.InMesh,
},
statuses: {
status: {
passing: (item, value) => item.MeshStatus === value,
warning: (item, value) => item.MeshStatus === value,
critical: (item, value) => item.MeshStatus === value,
empty: (item, value) => item.MeshChecksTotal === 0,
},
instances: {
instance: {
registered: (item, value) => item.InstanceCount > 0,
'not-registered': (item, value) => item.InstanceCount === 0,
},
sources: (item, values) => {
source: (item, values) => {
return setHelpers.intersectionSize(values, new Set(item.ExternalSources || [])) !== 0;
},
};

View File

@ -1,5 +1,5 @@
export default {
kinds: {
kind: {
'global-management': (item, value) => item.isGlobalManagement,
global: (item, value) => !item.Local,
local: (item, value) => item.Local,

View File

@ -17,7 +17,9 @@ export default class Role extends Model {
@attr('number') SyncTime;
@attr('number') CreateIndex;
@attr('number') ModifyIndex;
// frontend only for ordering where CreateIndex can't be used
// frontend only for ordering where CreateIndex can't be used i.e. for when
// we need to order items that aren't yet saved to the backend, for example
// in the role-selector
@attr('number') CreateTime;
// TODO: Figure out whether we need this or not
@attr() Datacenters; // string[]

View File

@ -48,6 +48,16 @@ export default class Service extends Model {
@attr() meta; // {}
@computed('ChecksPassing', 'ChecksWarning', 'ChecksCritical')
get ChecksTotal() {
return this.ChecksPassing + this.ChecksWarning + this.ChecksCritical;
}
@computed('MeshChecksPassing', 'MeshChecksWarning', 'MeshChecksCritical')
get MeshChecksTotal() {
return this.MeshChecksPassing + this.MeshChecksWarning + this.MeshChecksCritical;
}
/* Mesh properties involve both the service and the associated proxy */
@computed('ConnectedWithProxy', 'ConnectedWithGateway')
get MeshEnabled() {

View File

@ -1,13 +1,11 @@
import { inject as service } from '@ember/service';
import Route from 'consul-ui/routing/route';
import { hash } from 'rsvp';
import { get } from '@ember/object';
import WithAclActions from 'consul-ui/mixins/acl/with-actions';
export default class IndexRoute extends Route.extend(WithAclActions) {
@service('repository/acl') repo;
@service('settings') settings;
queryParams = {
@ -19,8 +17,8 @@ export default class IndexRoute extends Route.extend(WithAclActions) {
},
};
beforeModel(transition) {
return this.settings.findBySlug('token').then(token => {
async beforeModel(transition) {
const token = await this.settings.findBySlug('token');
// If you don't have a token set or you have a
// token set with AccessorID set to not null (new ACL mode)
// then rewrite to the new acls
@ -29,14 +27,15 @@ export default class IndexRoute extends Route.extend(WithAclActions) {
// everything works fine either way checking things manually
this.replaceWith('dc.acls.tokens');
}
});
}
model(params) {
return hash({
items: this.repo.findAllByDatacenter(this.modelFor('dc').dc.Name),
token: this.settings.findBySlug('token'),
});
async model(params) {
const _items = this.repo.findAllByDatacenter(this.modelFor('dc').dc.Name);
const _token = this.settings.findBySlug('token');
return {
items: await _items,
token: await _token,
};
}
setupController(controller, model) {

View File

@ -9,7 +9,9 @@ export default class IndexRoute extends Route.extend(WithPolicyActions) {
queryParams = {
sortBy: 'sort',
dc: 'dc',
datacenter: {
as: 'dc',
},
kind: 'kind',
searchproperty: {
as: 'searchproperty',
@ -29,6 +31,7 @@ export default class IndexRoute extends Route.extend(WithPolicyActions) {
this.modelFor('nspace').nspace.substr(1)
),
}),
searchProperties: this.queryParams.searchproperty.empty[0],
});
}

View File

@ -27,6 +27,7 @@ export default class IndexRoute extends Route.extend(WithRoleActions) {
this.modelFor('nspace').nspace.substr(1)
),
}),
searchProperties: this.queryParams.searchproperty.empty[0],
});
}

View File

@ -21,8 +21,8 @@ export default class IndexRoute extends Route.extend(WithTokenActions) {
},
};
beforeModel(transition) {
return this.settings.findBySlug('token').then(token => {
async beforeModel(transition) {
const token = await this.settings.findBySlug('token');
// If you have a token set with AccessorID set to null (legacy mode)
// then rewrite to the old acls
if (token && get(token, 'AccessorID') === null) {
@ -30,7 +30,6 @@ export default class IndexRoute extends Route.extend(WithTokenActions) {
// everything works fine either way checking things manually
this.replaceWith('dc.acls');
}
});
}
model(params) {
@ -43,6 +42,7 @@ export default class IndexRoute extends Route.extend(WithTokenActions) {
}),
nspace: this.modelFor('nspace').nspace.substr(1),
token: this.settings.findBySlug('token'),
searchProperties: this.queryParams.searchproperty.empty[0],
});
}

View File

@ -14,10 +14,11 @@ export default class IndexRoute extends Route {
},
};
model(params) {
async model(params) {
return {
dc: this.modelFor('dc').dc.Name,
nspace: this.modelFor('nspace').nspace.substr(1),
searchProperties: this.queryParams.searchproperty.empty[0],
};
}

View File

@ -1,6 +1,5 @@
import { inject as service } from '@ember/service';
import Route from 'consul-ui/routing/route';
import { hash } from 'rsvp';
export default class IndexRoute extends Route {
@service('data-source/service') data;
@ -18,13 +17,16 @@ export default class IndexRoute extends Route {
},
};
model(params) {
async model(params) {
const dc = this.modelFor('dc').dc.Name;
const nspace = this.modelFor('nspace').nspace.substr(1);
return hash({
items: this.data.source(uri => uri`/${nspace}/${dc}/nodes`),
leader: this.data.source(uri => uri`/${nspace}/${dc}/leader`),
});
const items = this.data.source(uri => uri`/${nspace}/${dc}/nodes`);
const leader = this.data.source(uri => uri`/${nspace}/${dc}/leader`);
return {
items: await items,
leader: await leader,
searchProperties: this.queryParams.searchproperty.empty[0],
};
}
setupController(controller, model) {

View File

@ -1,6 +1,5 @@
import { inject as service } from '@ember/service';
import Route from 'consul-ui/routing/route';
import { hash } from 'rsvp';
import WithNspaceActions from 'consul-ui/mixins/nspace/with-actions';
export default class IndexRoute extends Route.extend(WithNspaceActions) {
@ -19,10 +18,11 @@ export default class IndexRoute extends Route.extend(WithNspaceActions) {
},
};
model(params) {
return hash({
items: this.data.source(uri => uri`/*/*/namespaces`),
});
async model(params) {
return {
items: await this.data.source(uri => uri`/*/*/namespaces`),
searchProperties: this.queryParams.searchproperty.empty[0],
};
}
setupController(controller, model) {

View File

@ -27,6 +27,7 @@ export default class IndexRoute extends Route {
dc,
nspace,
items,
searchProperties: this.queryParams.searchproperty.empty[0],
};
}

View File

@ -4,7 +4,6 @@ export default class HealthchecksRoute extends Route {
queryParams = {
sortBy: 'sort',
status: 'status',
kind: 'kind',
check: 'check',
searchproperty: {
as: 'searchproperty',

View File

@ -14,11 +14,12 @@ export default class IndexRoute extends Route {
},
};
model(params) {
async model(params) {
return {
dc: this.modelFor('dc').dc.Name,
nspace: this.modelFor('nspace').nspace.substr(1) || 'default',
slug: this.paramsFor('dc.services.show').name,
searchProperties: this.queryParams.searchproperty.empty[0],
};
}

View File

@ -25,13 +25,12 @@ export default class ServicesRoute extends Route {
.slice(0, -1)
.join('.');
const name = this.modelFor(parent).slug;
const gatewayServices = await this.data.source(
uri => uri`/${nspace}/${dc}/gateways/for-service/${name}`
);
const items = await this.data.source(uri => uri`/${nspace}/${dc}/gateways/for-service/${name}`);
return {
dc,
nspace,
gatewayServices,
items,
searchProperties: this.queryParams.searchproperty.empty[0],
};
}

View File

@ -53,6 +53,7 @@
@import 'consul-ui/components/freetext-filter';
@import 'consul-ui/components/informed-action';
@import 'consul-ui/components/tab-nav';
@import 'consul-ui/components/search-bar';
@import 'consul-ui/components/consul/tomography/graph';
@import 'consul-ui/components/consul/discovery-chain';

View File

@ -18,7 +18,7 @@ html[data-route$='edit'] .app-view > header + div > *:first-child {
/* if it is a filter bar and the thing after the filter bar is a p then it also */
/* needs a top margun :S */
%app-view-content .tab-section > *:first-child:not(.filter-bar):not(table),
%app-view-content .tab-section > .filter-bar + p,
%app-view-content .tab-section > .search-bar + p,
%app-view-content .tab-section .consul-health-check-list {
margin-top: 1.25em;
}

View File

@ -17,7 +17,7 @@ h3 {
%radio-card header,
fieldset > header,
%main-nav-horizontal-action,
%app-view-content div > dl > dt,
%definition-table dt,
%table caption,
%tbody-th,
%form-element > span {

View File

@ -1,8 +1,22 @@
{{page-title 'ACLs'}}
{{#let (hash
kinds=(if kind (split kind ',') undefined)
) as |filters|}}
{{#let (or sortBy "Name:asc") as |sort|}}
{{#let
(hash
value=(or sortBy "Name:asc")
change=(action (mut sortBy) value="target.selected")
)
(hash
kind=(hash
value=(if kind (split kind ',') undefined)
change=(action (mut kind) value="target.selectedItems")
)
)
items
as |sort filters items|}}
<AppView>
<BlockSlot @name="notification" as |status type item error|>
<Consul::Acl::Notifications
@ -27,19 +41,15 @@
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
kind=(action (mut kind) value="target.selectedItems")
}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
<DataCollection
@type="acl"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -79,5 +89,5 @@
</DataCollection>
</BlockSlot>
</AppView>
{{/let}}
{{/let}}

View File

@ -3,15 +3,35 @@
{{else}}
{{page-title 'Access Controls'}}
{{/if}}
{{#let (hash
kinds=(if kind (split kind ',') undefined)
dcs=(if dc (split dc ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',')
(array 'Name' 'Description')
{{#let
(hash
value=(or sortBy "Name:asc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "Name:asc") as |sort|}}
(hash
kind=(hash
value=(if kind (split kind ',') undefined)
change=(action (mut kind) value="target.selectedItems")
)
datacenter=(hash
value=(if datacenter (split datacenter ',') undefined)
change=(action (mut datacenter) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
items
as |sort filters items|}}
<AppView
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
@ -44,21 +64,15 @@
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
dc=(action (mut dc) value="target.selectedItems")
kind=(action (mut kind) value="target.selectedItems")
}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
<DataCollection
@type="role"
@sort={{sort}}
@type="policy"
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -102,5 +116,5 @@
</DataCollection>
</BlockSlot>
</AppView>
{{/let}}
{{/let}}

View File

@ -4,13 +4,28 @@
{{page-title 'Access Controls'}}
{{/if}}
{{#let (hash
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',')
(array 'Name' 'Description' 'Policy')
{{#let
(hash
value=(or sortBy "Name:asc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "Name:asc") as |sort|}}
(hash
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
items
as |sort filters items|}}
<AppView
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
@ -43,19 +58,15 @@
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
<DataCollection
@type="role"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -100,4 +111,3 @@
</BlockSlot>
</AppView>
{{/let}}
{{/let}}

View File

@ -4,14 +4,32 @@
{{page-title 'Access Controls'}}
{{/if}}
{{#let (hash
kinds=(if kind (split kind ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',')
(array 'Description' 'Policy' 'Role')
{{#let
(hash
value=(or sortBy "CreateTime:desc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "CreateTime:desc") as |sort|}}
(hash
kind=(hash
value=(if kind (split kind ',') undefined)
change=(action (mut kind) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
items
as |sort filters items|}}
<AppView
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
@ -44,13 +62,8 @@
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
kind=(action (mut kind) value="target.selectedItems")
}}
/>
{{/if}}
</BlockSlot>
@ -69,7 +82,7 @@
{{/if}}
<DataCollection
@type="token"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -110,4 +123,3 @@
</BlockSlot>
</AppView>
{{/let}}
{{/let}}

View File

@ -6,15 +6,32 @@
</BlockSlot>
<BlockSlot @name="loaded">
{{#let api.data as |items|}}
{{#let (hash
accesses=(if access (split access ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',')
(array 'SourceName' 'DestinationName')
{{#let
(hash
value=(or sortBy "Action:asc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "Action:asc") as |sort|}}
(hash
access=(hash
value=(if access (split access ',') undefined)
change=(action (mut access) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
api.data
as |sort filters items|}}
<AppView>
<BlockSlot @name="header">
<h1>
@ -33,13 +50,8 @@
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
access=(action (mut access) value="target.selectedItems")
}}
/>
{{/if}}
@ -53,7 +65,7 @@
<BlockSlot @name="content">
<DataCollection
@type="intention"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -102,8 +114,7 @@
</DataWriter>
</BlockSlot>
</AppView>
{{/let}}
{{/let}}
{{/let}}
</BlockSlot>
</DataLoader>

View File

@ -1,8 +1,21 @@
{{page-title 'Key/Value'}}
{{#let (hash
kinds=(if kind (split kind ',') undefined)
) as |filters|}}
{{#let (or sortBy "Kind:asc") as |sort|}}
{{#let
(hash
value=(or sortBy "Kind:asc")
change=(action (mut sortBy) value="target.selected")
)
(hash
kind=(hash
value=(if kind (split kind ',') undefined)
change=(action (mut kind) value="target.selectedItems")
)
)
items
as |sort filters items|}}
<AppView>
{{#if (not-eq parent.Key '/') }}
<BlockSlot @name="breadcrumbs">
@ -31,12 +44,8 @@
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
kind=(action (mut kind) value="target.selectedItems")
}}
/>
{{/if}}
</BlockSlot>
@ -57,7 +66,7 @@
<BlockSlot @name="content">
<DataCollection
@type="kv"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -105,4 +114,3 @@
</BlockSlot>
</AppView>
{{/let}}
{{/let}}

View File

@ -1,14 +1,31 @@
{{page-title 'Nodes'}}
<EventSource @src={{items}} />
<EventSource @src={{leader}} />
{{#let (hash
statuses=(if status (split status ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',')
(array 'Node' 'Address' 'Meta')
{{#let
(hash
value=(or sortBy "Status:asc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "Status:asc") as |sort|}}
(hash
status=(hash
value=(if status (split status ',') undefined)
change=(action (mut status) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
items
as |sort filters items|}}
<AppView>
<BlockSlot @name="header">
<h1>
@ -23,20 +40,15 @@
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
status=(action (mut status) value="target.selectedItems")
}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
<DataCollection
@type="node"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -60,4 +72,3 @@
</BlockSlot>
</AppView>
{{/let}}
{{/let}}

View File

@ -1,40 +1,55 @@
{{#let (hash
statuses=(if status (split status ',') undefined)
kinds=(if kind (split kind ',') undefined)
checks=(if check (split check ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
{{#let
(hash
value=(or sortBy "Status:asc")
change=(action (mut sortBy) value="target.selected")
)
(hash
status=(hash
value=(if status (split status ',') undefined)
change=(action (mut status) value="target.selectedItems")
)
kind=(hash
value=(if kind (split kind ',') undefined)
change=(action (mut kind) value="target.selectedItems")
)
check=(hash
value=(if check (split check ',') undefined)
change=(action (mut check) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
) as |filters|}}
{{#let (or sortBy "Status:asc") as |sort|}}
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
item.Checks
as |sort filters items|}}
<div class="tab-section">
{{#if (gt item.Checks.length 0) }}
{{#if (gt items.length 0) }}
<input type="checkbox" id="toolbar-toggle" />
<Consul::HealthCheck::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@searchproperties={{searchProperties}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
status=(action (mut status) value="target.selectedItems")
kind=(action (mut kind) value="target.selectedItems")
check=(action (mut check) value="target.selectedItems")
}}
/>
{{/if}}
<DataCollection
@type="health-check"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{item.Checks}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::HealthCheck::List
@ -45,7 +60,7 @@
<EmptyState>
<BlockSlot @name="body">
<p>
This node has no health checks{{#if (gt item.Checks.length 0)}} matching that search{{/if}}.
This node has no health checks{{#if (gt items.length 0)}} matching that search{{/if}}.
</p>
</BlockSlot>
</EmptyState>
@ -53,4 +68,3 @@
</DataCollection>
</div>
{{/let}}
{{/let}}

View File

@ -1,13 +1,32 @@
{{#let (hash
statuses=(if status (split status ',') undefined)
sources=(if source (split source ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
{{#let
(hash
value=(or sortBy "Status:asc")
change=(action (mut sortBy) value="target.selected")
)
(hash
status=(hash
value=(if status (split status ',') undefined)
change=(action (mut status) value="target.selectedItems")
)
source=(hash
value=(if source (split source ',') undefined)
change=(action (mut source) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
) as |filters|}}
{{#let (or sortBy "Status:asc") as |sort|}}
{{#let (reject-by 'Service.Kind' 'connect-proxy' item.Services) as |items|}}
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
(reject-by 'Service.Kind' 'connect-proxy' item.Services)
as |sort filters items|}}
<div class="tab-section">
{{#if (gt items.length 0) }}
<input type="checkbox" id="toolbar-toggle" />
@ -18,20 +37,14 @@
@searchproperties={{searchProperties}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
status=(action (mut status) value="target.selectedItems")
source=(action (mut source) value="target.selectedItems")
}}
/>
{{/if}}
{{! filter out any sidecar proxies }}
<DataCollection
@type="service-instance"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -56,5 +69,3 @@
</DataCollection>
</div>
{{/let}}
{{/let}}
{{/let}}

View File

@ -1,12 +1,27 @@
{{page-title 'Namespaces'}}
{{#let (hash
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',')
(array 'Name' 'Description' 'Policy' 'Role')
)
) as |filters|}}
{{#let (or sortBy "Name:asc") as |sort|}}
<EventSource @src={{items}} />
{{#let
(hash
value=(or sortBy "Name:asc")
change=(action (mut sortBy) value="target.selected")
)
(hash
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
items
as |sort filters items|}}
<AppView>
<BlockSlot @name="notification" as |status type item error|>
<Consul::Nspace::Notifications
@ -30,19 +45,15 @@
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
<DataCollection
@type="nspace"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -87,4 +98,3 @@
</BlockSlot>
</AppView>
{{/let}}
{{/let}}

View File

@ -4,15 +4,31 @@
{{#let
(or sortBy "Status:asc")
(hash
value=(or sortBy "Status:asc")
change=(action (mut sortBy) value="target.selected")
)
(hash
statuses=(if status (split status ',') undefined)
kinds=(if kind (split kind ',') undefined)
sources=(if source (split source ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
status=(hash
value=(if status (split status ',') undefined)
change=(action (mut status) value="target.selectedItems")
)
kind=(hash
value=(if kind (split kind ',') undefined)
change=(action (mut kind) value="target.selectedItems")
)
source=(hash
value=(if source (split source ',') undefined)
change=(action (mut source) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
(array 'Name' 'Tags')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
@ -36,22 +52,16 @@ as |sort filters items|}}
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
status=(action (mut status) value="target.selectedItems")
kind=(action (mut kind) value="target.selectedItems")
source=(action (mut source) value="target.selectedItems")
}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
<DataCollection
@type="service"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}

View File

@ -1,41 +1,52 @@
{{#let (hash
statuses=(if status (split status ',') undefined)
kinds=(if kind (split kind ',') undefined)
checks=(if check (split check ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
{{#let
(hash
value=(or sortBy "Status:asc")
change=(action (mut sortBy) value="target.selected")
)
(hash
status=(hash
value=(if status (split status ',') undefined)
change=(action (mut status) value="target.selectedItems")
)
check=(hash
value=(if check (split check ',') undefined)
change=(action (mut check) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
) as |filters|}}
{{#let (or sortBy "Status:asc") as |sort|}}
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
item.MeshChecks
as |sort filters items|}}
<div class="tab-section">
{{#if (gt item.MeshChecks.length 0) }}
{{#if (gt items.length 0) }}
<input type="checkbox" id="toolbar-toggle" />
<Consul::HealthCheck::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@searchproperties={{searchProperties}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
status=(action (mut status) value="target.selectedItems")
kind=(action (mut kind) value="target.selectedItems")
check=(action (mut check) value="target.selectedItems")
}}
/>
{{/if}}
<DataCollection
@type="health-check"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{item.MeshChecks}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::HealthCheck::List
@ -46,7 +57,7 @@
<EmptyState>
<BlockSlot @name="body">
<p>
This instance has no health checks{{#if (gt item.MeshChecks.length 0)}} matching that search{{/if}}.
This instance has no health checks{{#if (gt items.length 0)}} matching that search{{/if}}.
</p>
</BlockSlot>
</EmptyState>
@ -55,4 +66,3 @@
</div>
{{/let}}
{{/let}}

View File

@ -1,12 +1,26 @@
<div class="tab-section">
{{#let (hash
searchproperties=(if (not-eq searchproperty undefined)
{{#let
(hash
value=(or sortBy "DestinationName:asc")
change=(action (mut sortBy) value="target.selected")
)
(hash
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
) as |filters|}}
{{#let (or sortBy "DestinationName:asc") as |sort|}}
{{#if (gt proxy.Service.Proxy.Upstreams.length 0)}}
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
proxy.Service.Proxy.Upstreams
as |sort filters items|}}
{{#if (gt items.length 0)}}
<input type="checkbox" id="toolbar-toggle" />
<Consul::UpstreamInstance::SearchBar
@search={{search}}
@ -14,20 +28,16 @@
@searchproperties={{searchProperties}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
}}
/>
{{/if}}
<DataCollection
@type="upstream-instance"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{proxy.Service.Proxy.Upstreams}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::UpstreamInstance::List
@ -40,12 +50,11 @@
<EmptyState>
<BlockSlot @name="body">
<p>
This service has no upstreams{{#if (gt proxy.Service.Proxy.Upstreams.length 0)}} matching that search{{/if}}.
This service has no upstreams{{#if (gt items.length 0)}} matching that search{{/if}}.
</p>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
{{/let}}
{{/let}}
</div>

View File

@ -1,36 +1,49 @@
<div class="tab-section">
{{#let (hash
statuses=(if status (split status ',') undefined)
sources=(if source (split source ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
{{#let
(hash
value=(or sortBy "Status:asc")
change=(action (mut sortBy) value="target.selected")
)
(hash
status=(hash
value=(if status (split status ',') undefined)
change=(action (mut status) value="target.selectedItems")
)
source=(hash
value=(if source (split source ',') undefined)
change=(action (mut source) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
) as |filters|}}
{{#let (or sortBy "Status:asc") as |sort|}}
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
items
as |sort filters items|}}
{{#if (gt items.length 0) }}
<input type="checkbox" id="toolbar-toggle" />
<Consul::ServiceInstance::SearchBar
@sources={{get (collection items) 'ExternalSources'}}
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@searchproperties={{searchProperties}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
status=(action (mut status) value="target.selectedItems")
source=(action (mut source) value="target.selectedItems")
}}
/>
{{/if}}
{{! Service > Service Instance view doesn't require filtering of proxies }}
<DataCollection
@type="service-instance"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -52,5 +65,4 @@
</collection.Empty>
</DataCollection>
{{/let}}
{{/let}}
</div>

View File

@ -12,15 +12,31 @@ as |api|>
<ErrorState @error={{api.error}} />
</BlockSlot>
<BlockSlot @name="loaded">
{{#let api.data as |items|}}
{{#let (hash
accesses=(if access (split access ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',')
(array 'SourceName' 'DestinationName')
{{#let
(hash
value=(or sortBy "Action:asc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "Action:asc") as |sort|}}
(hash
access=(hash
value=(if access (split access ',') undefined)
change=(action (mut access) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
api.data
as |sort filters items|}}
<div class="tab-section">
<Portal @target="app-view-actions">
<a data-test-create href={{href-to 'dc.services.show.intentions.create'}} class="type-create">Create</a>
@ -31,13 +47,8 @@ as |api|>
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
access=(action (mut access) value="target.selectedItems")
}}
/>
{{/if}}
@ -49,7 +60,7 @@ as |api|>
<BlockSlot @name="content">
<DataCollection
@type="intention"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
@ -79,7 +90,5 @@ as |api|>
</DataWriter>
</div>
{{/let}}
{{/let}}
{{/let}}
</BlockSlot>
</DataLoader>

View File

@ -1,27 +1,39 @@
<EventSource @src={{gatewayServices}} />
<EventSource @src={{items}} />
<div class="tab-section">
{{#let (hash
instances=(if instance (split instance ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',')
(array 'Name' 'Tags')
{{#let
(hash
value=(or sortBy "Status:asc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "Name:asc") as |sort|}}
{{#if (gt gatewayServices.length 0)}}
(hash
instance=(hash
value=(if instance (split instance ',') undefined)
change=(action (mut instance) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
items
as |sort filters items|}}
{{#if (gt items.length 0)}}
<input type="checkbox" id="toolbar-toggle" />
<Consul::Upstream::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
instance=(action (mut instance) value="target.selectedItems")
}}
/>
{{/if}}
<p>
@ -30,10 +42,10 @@
</p>
<DataCollection
@type="service"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{gatewayServices}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Service::List
@ -46,12 +58,11 @@
<EmptyState>
<BlockSlot @name="body">
<p>
There are no linked services{{#if (gt gatewayServices.length 0)}} matching that search{{/if}}.
There are no linked services{{#if (gt items.length 0)}} matching that search{{/if}}.
</p>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
{{/let}}
{{/let}}
</div>

View File

@ -1,27 +1,39 @@
<EventSource @src={{gatewayServices}} />
<EventSource @src={{items}} />
<div class="tab-section">
{{#let (hash
instances=(if instance (split instance ',') undefined)
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',')
(array 'Name' 'Tags')
{{#let
(hash
value=(or sortBy "Status:asc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "Status:asc") as |sort|}}
{{#if (gt gatewayServices.length 0)}}
(hash
instance=(hash
value=(if instance (split instance ',') undefined)
change=(action (mut instance) value="target.selectedItems")
)
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
items
as |sort filters items|}}
{{#if (gt items.length 0)}}
<input type="checkbox" id="toolbar-toggle" />
<Consul::Upstream::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
instance=(action (mut instance) value="target.selectedItems")
}}
/>
{{/if}}
<p>
@ -29,10 +41,10 @@
</p>
<DataCollection
@type="service"
@sort={{sort}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{gatewayServices}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Upstream::List
@ -46,12 +58,11 @@
<EmptyState>
<BlockSlot @name="body">
<p>
There are no upstreams{{#if (gt gatewayServices.length 0)}} matching that search{{/if}}.
There are no upstreams{{#if (gt items.length 0)}} matching that search{{/if}}.
</p>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
{{/let}}
{{/let}}
</div>

View File

@ -0,0 +1,9 @@
// if we can't find the message, take the last part of the identifier and
// ucfirst it so it looks human
export default function missingMessage(key, locales) {
const last = key
.split('.')
.pop()
.replaceAll('-', ' ');
return `${last.substr(0, 1).toUpperCase()}${last.substr(1)}`;
}

View File

@ -1,24 +0,0 @@
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');
});
});

View File

@ -6,7 +6,6 @@ import {
collection,
text,
isPresent,
triggerable,
} from 'ember-cli-page-object';
import { alias } from 'ember-cli-page-object/macros';
@ -26,9 +25,7 @@ import pageFactory from 'consul-ui/components/hashicorp-consul/pageobject';
import radiogroup from 'consul-ui/components/radio-group/pageobject';
import tabgroup from 'consul-ui/components/tab-nav/pageobject';
import authFormFactory from 'consul-ui/components/auth-form/pageobject';
import freetextFilterFactory from 'consul-ui/components/freetext-filter/pageobject';
import searchBarFactory from 'consul-ui/components/search-bar/pageobject';
import emptyStateFactory from 'consul-ui/components/empty-state/pageobject';
import policyFormFactory from 'consul-ui/components/policy-form/pageobject';
@ -82,11 +79,6 @@ const cancelable = createCancelable(clickable, is);
// components
const tokenList = tokenListFactory(clickable, attribute, collection, deletable);
const authForm = authFormFactory(submitable, clickable, attribute);
const freetextFilter = freetextFilterFactory(triggerable);
const catalogToolbar = searchBarFactory(freetextFilter);
const aclFilter = searchBarFactory(freetextFilter, () =>
radiogroup('type', ['', 'management', 'client'])
);
const policyForm = policyFormFactory(submitable, cancelable, radiogroup, text);
const policySelector = policySelectorFactory(clickable, deletable, collection, alias, policyForm);
const roleForm = roleFormFactory(submitable, cancelable, policySelector);
@ -160,18 +152,7 @@ export default {
radiogroup
)
),
service: create(
service(
visitable,
clickable,
attribute,
collection,
text,
consulIntentionList,
catalogToolbar,
tabgroup
)
),
service: create(service(visitable, clickable, attribute, collection, text, consulIntentionList, tabgroup)),
instance: create(
instance(
visitable,
@ -199,7 +180,7 @@ export default {
),
kvs: create(kvs(visitable, creatable, consulKvList)),
kv: create(kv(visitable, attribute, submitable, deletable, cancelable, clickable)),
acls: create(acls(visitable, deletable, creatable, clickable, attribute, collection, aclFilter)),
acls: create(acls(visitable, deletable, creatable, clickable, attribute, collection)),
acl: create(acl(visitable, submitable, deletable, cancelable, clickable)),
policies: create(policies(visitable, creatable, consulPolicyList, popoverSelect)),
policy: create(policy(visitable, submitable, deletable, cancelable, clickable, tokenList)),

View File

@ -1,4 +1,4 @@
export default function(visitable, deletable, creatable, clickable, attribute, collection, filter) {
export default function(visitable, deletable, creatable, clickable, attribute, collection) {
return creatable({
visit: visitable('/:dc/acls'),
acls: collection(
@ -11,6 +11,5 @@ export default function(visitable, deletable, creatable, clickable, attribute, c
confirmUse: clickable('[data-test-confirm-use]'),
})
),
filter: filter('[data-test-acl-filter]'),
});
}

View File

@ -1,13 +1,4 @@
export default function(
visitable,
clickable,
attribute,
collection,
text,
intentions,
filter,
tabs
) {
export default function(visitable, clickable, attribute, collection, text, intentions, tabs) {
const page = {
visit: visitable('/:dc/services/:service'),
externalSource: attribute('data-test-external-source', '[data-test-external-source]', {
@ -28,7 +19,6 @@ export default function(
'routing',
'tags',
]),
filter: filter(),
// TODO: These need to somehow move to subpages
instances: collection('.consul-service-instance-list > ul > li:not(:first-child)', {
address: text('[data-test-address]'),

View File

@ -0,0 +1,154 @@
import { filters } from 'consul-ui/components/search-bar/utils';
import { module, test } from 'qunit';
module('Unit | Component | search-bar/filters', function() {
test('it correctly reshapes the filter data', function(assert) {
[
// basic filter, returns a single filter button when clicked
// resets selected/queryparam to empty
{
filters: {
status: {
value: ['passing'],
},
},
expected: [
{
key: 'status',
value: 'passing',
selected: [],
},
],
},
// basic filters, returns multiple filter button when clicked
// sets selected/queryparam to the left over single filter
{
filters: {
status: {
value: ['passing', 'warning'],
},
},
expected: [
{
key: 'status',
value: 'passing',
selected: ['warning'],
},
{
key: 'status',
value: 'warning',
selected: ['passing'],
},
],
},
// basic filters, returns multiple filter button when clicked
// sets selected/queryparam to the left over multiple filters
{
filters: {
status: {
value: ['passing', 'warning', 'critical'],
},
},
expected: [
{
key: 'status',
value: 'passing',
selected: ['warning', 'critical'],
},
{
key: 'status',
value: 'warning',
selected: ['passing', 'critical'],
},
{
key: 'status',
value: 'critical',
selected: ['passing', 'warning'],
},
],
},
// basic filters, returns multiple filter button when clicked
// sets selected/queryparam to the left over multiple filters
// also search property multiple filter, sets the selected/queryparam to
// the left of single searchproperty filter
{
filters: {
status: {
value: ['passing', 'warning', 'critical'],
},
searchproperties: {
default: ['Node', 'Address', 'Meta'],
value: ['Node', 'Address'],
},
},
expected: [
{
key: 'status',
value: 'passing',
selected: ['warning', 'critical'],
},
{
key: 'status',
value: 'warning',
selected: ['passing', 'critical'],
},
{
key: 'status',
value: 'critical',
selected: ['passing', 'warning'],
},
{
key: 'searchproperties',
value: 'Node',
selected: ['Address'],
},
{
key: 'searchproperties',
value: 'Address',
selected: ['Node'],
},
],
},
// basic filters, returns multiple filter button when clicked
// sets selected/queryparam to the left over multiple filters
// also search property single filter, resets the selected/queryparam to
// empty
{
filters: {
status: {
value: ['passing', 'warning', 'critical'],
},
searchproperties: {
default: ['Node', 'Address', 'Meta'],
value: ['Node'],
},
},
expected: [
{
key: 'status',
value: 'passing',
selected: ['warning', 'critical'],
},
{
key: 'status',
value: 'warning',
selected: ['passing', 'critical'],
},
{
key: 'status',
value: 'critical',
selected: ['passing', 'warning'],
},
{
key: 'searchproperties',
value: 'Node',
selected: [],
},
],
},
].forEach(item => {
const actual = filters(item.filters);
assert.deepEqual(actual, item.expected);
});
});
});

View File

@ -20,7 +20,7 @@ module('Unit | Filter | Predicates | intention', function() {
expected = [items[0]];
actual = items.filter(
predicate({
accesses: ['allow'],
access: ['allow'],
})
);
assert.deepEqual(actual, expected);
@ -28,7 +28,7 @@ module('Unit | Filter | Predicates | intention', function() {
expected = [items[1]];
actual = items.filter(
predicate({
accesses: ['deny'],
access: ['deny'],
})
);
assert.deepEqual(actual, expected);
@ -36,7 +36,7 @@ module('Unit | Filter | Predicates | intention', function() {
expected = items;
actual = items.filter(
predicate({
accesses: ['allow', 'deny'],
access: ['allow', 'deny'],
})
);
assert.deepEqual(actual, expected);

View File

@ -20,7 +20,7 @@ module('Unit | Filter | Predicates | service', function() {
expected = [items[0]];
actual = items.filter(
predicate({
instances: ['registered'],
instance: ['registered'],
})
);
assert.deepEqual(actual, expected);
@ -28,7 +28,7 @@ module('Unit | Filter | Predicates | service', function() {
expected = [items[1]];
actual = items.filter(
predicate({
instances: ['not-registered'],
instance: ['not-registered'],
})
);
assert.deepEqual(actual, expected);
@ -36,7 +36,7 @@ module('Unit | Filter | Predicates | service', function() {
expected = items;
actual = items.filter(
predicate({
instances: ['registered', 'not-registered'],
instance: ['registered', 'not-registered'],
})
);
assert.deepEqual(actual, expected);
@ -60,7 +60,7 @@ module('Unit | Filter | Predicates | service', function() {
expected = [items[0]];
actual = items.filter(
predicate({
statuses: ['passing'],
status: ['passing'],
})
);
assert.deepEqual(actual, expected);
@ -68,7 +68,7 @@ module('Unit | Filter | Predicates | service', function() {
expected = [items[1]];
actual = items.filter(
predicate({
statuses: ['warning'],
status: ['warning'],
})
);
assert.deepEqual(actual, expected);
@ -76,7 +76,7 @@ module('Unit | Filter | Predicates | service', function() {
expected = items;
actual = items.filter(
predicate({
statuses: ['passing', 'warning', 'critical'],
status: ['passing', 'warning', 'critical'],
})
);
assert.deepEqual(actual, expected);
@ -98,7 +98,7 @@ module('Unit | Filter | Predicates | service', function() {
expected = [items[0]];
actual = items.filter(
predicate({
kinds: ['ingress-gateway'],
kind: ['ingress-gateway'],
})
);
assert.deepEqual(actual, expected);
@ -106,7 +106,7 @@ module('Unit | Filter | Predicates | service', function() {
expected = [items[1]];
actual = items.filter(
predicate({
kinds: ['mesh-gateway'],
kind: ['mesh-gateway'],
})
);
assert.deepEqual(actual, expected);
@ -114,7 +114,7 @@ module('Unit | Filter | Predicates | service', function() {
expected = items;
actual = items.filter(
predicate({
kinds: ['ingress-gateway', 'mesh-gateway', 'service'],
kind: ['ingress-gateway', 'mesh-gateway', 'service'],
})
);
assert.deepEqual(actual, expected);
@ -142,9 +142,9 @@ module('Unit | Filter | Predicates | service', function() {
expected = [items[0]];
actual = items.filter(
predicate({
kinds: ['ingress-gateway'],
statuses: ['passing'],
instances: ['registered'],
kind: ['ingress-gateway'],
status: ['passing'],
instance: ['registered'],
})
);
assert.deepEqual(actual, expected);
@ -152,9 +152,9 @@ module('Unit | Filter | Predicates | service', function() {
expected = [items[1]];
actual = items.filter(
predicate({
kinds: ['mesh-gateway'],
statuses: ['warning'],
instances: ['registered'],
kind: ['mesh-gateway'],
status: ['warning'],
instance: ['registered'],
})
);
assert.deepEqual(actual, expected);
@ -162,9 +162,9 @@ module('Unit | Filter | Predicates | service', function() {
expected = items;
actual = items.filter(
predicate({
kinds: ['ingress-gateway', 'mesh-gateway', 'service'],
statuses: ['passing', 'warning', 'critical'],
instances: ['registered', 'not-registered'],
kind: ['ingress-gateway', 'mesh-gateway', 'service'],
status: ['passing', 'warning', 'critical'],
instance: ['registered', 'not-registered'],
})
);
assert.deepEqual(actual, expected);

View File

@ -1 +1,158 @@
common:
brand:
consul: Consul
terraform: Terraform
nomad: Nomad
vault: Vault
aws: AWS
kubernetes: Kubernetes
ui:
remove: Remove {item}
filtered-by: Filtered by {item}
name: Name
creation: Creation
consul:
name: Name
passing: Passing
warning: Warning
critical: Critical
registered: Registered
not-registered: Not Registered
empty: No checks
tags: Tags
service: Service
gateway: Gateway
mesh: Mesh
ingress-gateway: Ingress Gateway
terminating-gateway: Terminating Gateway
mesh-gateway: Mesh Gateway
status: Health Status
service-name: Service Name
node-name: Node Name
accessorid: AccessorID
datacenter: Datacenter
localbindaddress: Local Bind Address
localbindport: Local Bind Port
destinationname: Destination Name
sourcename: Source Name
search:
search: Search
searchproperty: Search Across
source: Source
critical: Failing
in-mesh: In service mesh
not-in-mesh: Not in service mesh
sort:
alpha:
asc: A to Z
desc: Z to A
numeric:
asc: Ascending
desc: Descending
age:
asc: Oldest to Newest
desc: Newest to Oldest
status:
asc: Unhealthy to Healthy
desc: Healthy to Unhealthy
components:
consul:
service:
search-bar:
kind: Service Type
in-mesh: In service mesh
not-in-mesh: Not in service mesh
upstream:
search-bar:
instance:
name: Type
service-instance:
search-bar:
sort:
name:
name: Service Name
health-check:
search-bar:
kind:
name: Kind
options:
service: Service Check
node: Node Check
check:
name: Type
options:
alias: alias
docker: docker
grpc: grpc
http: http
script: script
serf: serf
tcp: tcp
ttl: ttl
sort:
name:
name: Check Name
kind:
name: Check Type
asc: Service to Node
desc: Node to Service
acl:
search-bar:
kind:
name: Type
options:
management: Management
client: Client
token:
search-bar:
kind:
name: Type
options:
global-management: Global Management
global: Global Scope
local: Local Scope
policy:
search-bar:
kind:
name: Type
options:
global-management: Global Management
standard: Standard
kv:
search-bar:
kind:
name: Type
options:
folder: Folder
key: Key
sort:
kind:
asc: Folders to Keys
desc: Keys to Folders
intention:
search-bar:
access:
name: Permission
options:
allow: Allow
deny: Deny
app-aware: App aware
sort:
access:
name: Permission
asc: Allow to Deny
desc: Deny to Allow
source-name:
name: Source
asc: "Source: A to Z"
desc: "Source: Z to A"
destination-name:
name: Destination
asc: "Destination: A to Z"
desc: "Destination: Z to A"
precedence:
name: Precedence
asc: Ascending
desc: Descending