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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
/>
</div>
<div class="filters">
<PopoverSelect
@position="left"
@onchange={{action @onfilter.kind}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
</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>
<: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}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<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>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@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 @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{t "components.consul.acl.search-bar.kind.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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>
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
))
as |selectable|
}}
{{get selectable @sort.value}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
<: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={{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 @filter.status.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.consul.status"}}
</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 (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>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
class="type-status"
@position="left"
@onchange={{action @onfilter.status}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Health Status
</span>
</BlockSlot>
<BlockSlot @name="options">
</search.Select>
{{#if @filter.kind}}
<search.Select
class="type-kind"
@position="left"
@onchange={{action @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{t "components.consul.health-check.search-bar.kind.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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>
</search.Select>
{{/if}}
<search.Select
class="type-check"
@position="left"
@onchange={{action @filter.check.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{t "components.consul.health-check.search-bar.check.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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>
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(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.value}}
{{/let}}
</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>
{{/let}}
</BlockSlot>
</PopoverSelect>
<PopoverSelect
class="type-kind"
@position="left"
@onchange={{action @onfilter.kind}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Kind
</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>
{{/let}}
</BlockSlot>
</PopoverSelect>
<PopoverSelect
class="type-check"
@position="left"
@onchange={{action @onfilter.check}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
</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>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
(array "Status:asc" "Unhealthy to Healthy")
(array "Status:desc" "Healthy to Unhealthy")
(array "Kind:asc" "Service to Node")
(array "Kind:desc" "Node to Service")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Health Status">
<Option @value="Status:asc" @selected={{eq "Status:asc" @sort}}>Unhealthy to Healthy</Option>
<Option @value="Status:desc" @selected={{eq "Status:desc" @sort}}>Healthy to Unhealthy</Option>
<Optgroup @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>
</BlockSlot>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
<: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={{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-access"
@position="left"
@onchange={{action @filter.access.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "components.consul.intention.search-bar.access.name"}}
</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 (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>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
@position="left"
@onchange={{action @onfilter.access}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Permission
</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>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Action:asc" "Allow to Deny")
(array "Action:desc" "Deny to Allow")
(array "SourceName:asc" "Source: A to Z")
(array "SourceName:desc" "Source: Z to A")
(array "DestinationName:asc" "Destination: A to Z")
(array "DestinationName:desc" "Destination: Z to A")
(array "Precedence:asc" "Precedence: Ascending")
(array "Precedence:desc" "Precedence: Descending")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Permission">
<Option @value="Action:asc" @selected={{eq "Action:asc" @sort}}>Allow to Deny</Option>
<Option @value="Action:desc" @selected={{eq "Action:desc" @sort}}>Deny to Allow</Option>
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(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.value}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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>
{{/let}}
</BlockSlot>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
/>
</div>
<div class="filters">
<PopoverSelect
class="type-kind"
@position="left"
@onchange={{action @onfilter.kind}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
</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>
<: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}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Key:asc" "A to Z")
(array "Key:desc" "Z to A")
(array "Kind:asc" "Folders to Keys")
(array "Kind:desc" "Keys to Folders")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Name">
<Option @value="Key:asc" @selected={{eq "Key:asc" @sort}}>A to Z</Option>
<Option @value="Key:desc" @selected={{eq "Key:desc" @sort}}>Z to A</Option>
</Optgroup>
<Optgroup @label="Type">
<Option @value="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>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</:status>
<:search as |search|>
<search.Search
@onsearch={{action @onsearch}}
@value={{@search}}
@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 @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{t "components.consul.kv.search-bar.kind.name"}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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>
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(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.value}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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={{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>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
<: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={{t "common.search.search"}}
>
<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>
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-status"
@position="left"
@onchange={{action @filter.status.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.consul.status"}}
</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 (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>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
class="type-status"
@position="left"
@onchange={{action @onfilter.status}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Health 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>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
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")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Health Status">
<Option @value="Status:asc" @selected={{eq "Status:asc" @sort}}>Unhealthy to Healthy</Option>
<Option @value="Status:desc" @selected={{eq "Status:desc" @sort}}>Healthy to Unhealthy</Option>
</Optgroup>
<Optgroup @label="Service Name">
<Option @value="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>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(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.value}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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={{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>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
<: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={{t "common.search.search"}}
>
<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>
</search.Search>
</:search>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onfilter.searchproperty}}
@multiple={{true}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{#let (from-entries (array
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
))
as |selectable|
}}
{{get selectable @sort.value}}
{{/let}}
</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>
<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>
</FreetextFilter>
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Name">
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort}}>A to Z</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort}}>Z to A</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
<: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={{t "common.search.search"}}
>
<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>
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-datacenter"
@position="left"
@onchange={{action @filter.datacenter.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{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.datacenter.value}}>{{dc.Name}}</Option>
{{/each}}
<DataSource @src="/*/*/datacenters" @loading="lazy" @onchange={{action (mut this.dcs) value="data"}} />
{{/let}}
</BlockSlot>
</search.Select>
<search.Select
class="type-kind"
@position="left"
@onchange={{action @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{t "components.consul.policy.search-bar.kind.name"}}
</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 (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>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
class="select-dcs"
@position="left"
@onchange={{action @onfilter.dc}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Datacenters
</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>
{{/each}}
{{/let}}
<DataSource @src="/*/*/datacenters" @loading="lazy" @onchange={{action (mut this.dcs) value="data"}} />
</BlockSlot>
</PopoverSelect>
<PopoverSelect
class="select-type"
@position="left"
@onchange={{action @onfilter.kind}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
</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>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Name">
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort}}>A to Z</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort}}>Z to A</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" (t "common.sort.alpha.asc"))
(array "Name:desc" (t "common.sort.alpha.desc"))
))
as |selectable|
}}
{{get selectable @sort.value}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
<: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={{t "common.search.search"}}
>
<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>
</search.Search>
</:search>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onfilter.searchproperty}}
@multiple={{true}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{#let (from-entries (array
(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.value}}
{{/let}}
</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>
<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={{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>
</FreetextFilter>
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
(array "CreateIndex:desc" "Newest to oldest")
(array "CreateIndex:asc" "Oldest to newest")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Name">
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort}}>A to Z</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort}}>Z to A</Option>
</Optgroup>
<Optgroup @label="Creation">
<Option @value="CreateIndex:desc" @selected={{eq "CreateIndex:desc" @sort}}>Newest to oldest</Option>
<Option @value="CreateIndex:asc" @selected={{eq "CreateIndex:asc" @sort}}>Oldest to newest</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
<: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={{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 @filter.status.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.consul.status"}}
</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 (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>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
class="type-status"
@position="left"
@onchange={{action @onfilter.status}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Health Status
</span>
</BlockSlot>
<BlockSlot @name="options">
</search.Select>
{{#if (gt @sources.length 0)}}
<search.Select
class="type-source"
@position="left"
@onchange={{action @filter.source.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{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.source.value}}>
{{t (concat "common.brand." source)}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</search.Select>
{{/if}}
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(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.value}}
{{/let}}
</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>
{{/let}}
</BlockSlot>
</PopoverSelect>
{{#if (gt @sources.length 0)}}
<PopoverSelect
class="type-source"
@position="left"
@onchange={{action @onfilter.source}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
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>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
{{/if}}
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
(array "Status:asc" "Unhealthy to Healthy")
(array "Status:desc" "Healthy to Unhealthy")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Health Status">
<Option @value="Status:asc" @selected={{eq "Status:asc" @sort}}>Unhealthy to Healthy</Option>
<Option @value="Status:desc" @selected={{eq "Status:desc" @sort}}>Healthy to Unhealthy</Option>
<Optgroup @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>
</BlockSlot>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
<: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={{t "common.search.search"}}
>
<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>
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-status"
@position="left"
@onchange={{action @filter.status.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "common.consul.status"}}
</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 (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>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
class="type-status"
@position="left"
@onchange={{action @onfilter.status}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Health 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>
{{/let}}
</BlockSlot>
</PopoverSelect>
<PopoverSelect
@position="left"
@onchange={{action @onfilter.kind}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Service Type
</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>
</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>
{{/let}}
</BlockSlot>
</PopoverSelect>
{{#if (gt @sources.length 0)}}
<PopoverSelect
class="type-source"
@position="left"
@onchange={{action @onfilter.source}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
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>
{{/each}}
{{/let}}
</BlockSlot>
</PopoverSelect>
{{/if}}
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
(array "Status:asc" "Unhealthy to Healthy")
(array "Status:desc" "Healthy to Unhealthy")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Health Status">
<Option @value="Status:asc" @selected={{eq "Status:asc" @sort}}>Unhealthy to Healthy</Option>
<Option @value="Status:desc" @selected={{eq "Status:desc" @sort}}>Healthy to Unhealthy</Option>
</Optgroup>
<Optgroup @label="Service Name">
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort}}>A to Z</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort}}>Z to A</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
<search.Select
@position="left"
@onchange={{action @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{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.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={{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>
</search.Select>
{{#if (gt @sources.length 0)}}
<search.Select
class="type-source"
@position="left"
@onchange={{action @filter.source.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{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.source.value}}>
{{t (concat "common.brand." source)}}
</Option>
{{/each}}
{{/let}}
</BlockSlot>
</search.Select>
{{/if}}
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(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.value}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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={{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>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
<: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={{t "common.search.search"}}
>
<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>
</search.Search>
</:search>
<:filter as |search|>
<search.Select
class="type-status"
@position="left"
@onchange={{action @filter.kind.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "components.consul.token.search-bar.kind.name"}}
</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 (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>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
@position="left"
@onchange={{action @onfilter.kind}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
</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>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "CreateTime:desc" "Newest to oldest")
(array "CreateTime:asc" "Oldest to newest")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Creation">
<Option @value="CreateTime:desc" @selected={{eq "CreateTime:desc" @sort}}>Newest to oldest</Option>
<Option @value="CreateTime:asc" @selected={{eq "CreateTime:asc" @sort}}>Oldest to newest</Option>
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "CreateTime:desc" (t "common.sort.age.desc"))
(array "CreateTime:asc" (t "common.sort.age.asc"))
))
as |selectable|
}}
{{get selectable @sort.value}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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>
{{/let}}
</BlockSlot>
</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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
<: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={{t "common.search.search"}}
>
<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>
</search.Search>
</:search>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onfilter.searchproperty}}
@multiple={{true}}
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{#let (from-entries (array
(array "DestinationName:asc" (t "common.sort.alpha.asc"))
(array "DestinationName:desc" (t "common.sort.alpha.desc"))
))
as |selectable|
}}
{{get selectable @sort.value}}
{{/let}}
</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}}
<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>
</FreetextFilter>
</div>
<div class="sort">
{{#let (or @sort 'DestinationName:asc') as |sort|}}
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "DestinationName:asc" "A to Z")
(array "DestinationName:desc" "Z to A")
))
as |selectable|}}
{{get selectable sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="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>
{{/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
@onsearch={{action @onsearch}}
@value={{@search}}
@placeholder="Search"
>
<PopoverSelect
class="type-search-properties"
@position="right"
@onchange={{action @onfilter.searchproperty}}
<: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={{t "common.search.search"}}
>
<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>
</search.Search>
</:search>
<:filter as |search|>
<search.Select
@position="left"
@onchange={{action @filter.instance.change}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Search across
{{t "components.consul.upstream.search-bar.instance.name"}}
</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 (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>
</FreetextFilter>
</div>
<div class="filters">
<PopoverSelect
@position="left"
@onchange={{action @onfilter.instance}}
@multiple={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
Type
</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>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
<div class="sort">
<PopoverSelect
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @onsort}}
@multiple={{false}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(array "Name:asc" "A to Z")
(array "Name:desc" "Z to A")
(array "Status:asc" "Unhealthy to Healthy")
(array "Status:desc" "Healthy to Unhealthy")
))
as |selectable|
}}
{{get selectable @sort}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup Option|}}
<Optgroup @label="Health Status">
<Option @value="Status:asc" @selected={{eq "Status:asc" @sort}}>Unhealthy to Healthy</Option>
<Option @value="Status:desc" @selected={{eq "Status:desc" @sort}}>Healthy to Unhealthy</Option>
</Optgroup>
<Optgroup @label="Service Name">
<Option @value="Name:asc" @selected={{eq "Name:asc" @sort}}>A to Z</Option>
<Option @value="Name:desc" @selected={{eq "Name:desc" @sort}}>Z to A</Option>
</Optgroup>
{{/let}}
</BlockSlot>
</PopoverSelect>
</div>
</form>
</search.Select>
</:filter>
<:sort as |search|>
<search.Select
class="type-sort"
data-test-sort-control
@position="right"
@onchange={{action @sort.change}}
@multiple={{false}}
@required={{true}}
as |components|>
<BlockSlot @name="selected">
<span>
{{#let (from-entries (array
(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.value}}
{{/let}}
</span>
</BlockSlot>
<BlockSlot @name="options">
{{#let components.Optgroup components.Option as |Optgroup 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={{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>
</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);
} else {
this._options.add(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 {
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}}
</form>
<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,24 +17,25 @@ export default class IndexRoute extends Route.extend(WithAclActions) {
},
};
beforeModel(transition) {
return this.settings.findBySlug('token').then(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
if (!token || get(token, 'AccessorID') !== null) {
// If you return here, you get a TransitionAborted error in the tests only
// everything works fine either way checking things manually
this.replaceWith('dc.acls.tokens');
}
});
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
if (!token || get(token, 'AccessorID') !== null) {
// If you return here, you get a TransitionAborted error in the tests only
// 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,16 +21,15 @@ export default class IndexRoute extends Route.extend(WithTokenActions) {
},
};
beforeModel(transition) {
return this.settings.findBySlug('token').then(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) {
// If you return here, you get a TransitionAborted error in the tests only
// everything works fine either way checking things manually
this.replaceWith('dc.acls');
}
});
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) {
// If you return here, you get a TransitionAborted error in the tests only
// 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,83 +1,93 @@
{{page-title 'ACLs'}}
{{#let (hash
kinds=(if kind (split kind ',') undefined)
) as |filters|}}
{{#let (or sortBy "Name:asc") as |sort|}}
<AppView>
<BlockSlot @name="notification" as |status type item error|>
<Consul::Acl::Notifications
@status={{status}}
@type={{type}}
@error={{error}}
/>
</BlockSlot>
<BlockSlot @name="header">
<h1>
ACL Tokens <em>{{format-number items.length}} total</em>
</h1>
<label for="toolbar-toggle"></label>
</BlockSlot>
<BlockSlot @name="actions">
<a data-test-create href="{{href-to 'dc.acls.create'}}" class="type-create">Create</a>
</BlockSlot>
<BlockSlot @name="toolbar">
{{#if (gt items.length 0) }}
<Consul::Acl::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
{{#let
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
(hash
value=(or sortBy "Name:asc")
change=(action (mut sortBy) value="target.selected")
)
@filter={{filters}}
@onfilter={{hash
kind=(action (mut kind) value="target.selectedItems")
}}
(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
@status={{status}}
@type={{type}}
@error={{error}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
<DataCollection
@type="acl"
@sort={{sort}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Acl::List
@items={{collection.items}}
</BlockSlot>
<BlockSlot @name="header">
<h1>
ACL Tokens <em>{{format-number items.length}} total</em>
</h1>
<label for="toolbar-toggle"></label>
</BlockSlot>
<BlockSlot @name="actions">
<a data-test-create href="{{href-to 'dc.acls.create'}}" class="type-create">Create</a>
</BlockSlot>
<BlockSlot @name="toolbar">
{{#if (gt items.length 0) }}
<Consul::Acl::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@filter={{filters}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
<DataCollection
@type="acl"
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Acl::List
@items={{collection.items}}
@ondelete={{route-action 'delete'}}
@onuse={{route-action 'use'}}
@onclone={{route-action 'clone'}}
>
</Consul::Acl::List>
</collection.Collection>
<collection.Empty>
<EmptyState @allowLogin={{true}}>
<BlockSlot @name="header">
<h2>
{{#if (gt items.length 0)}}
No ACLs found
{{else}}
Welcome to ACLs
{{/if}}
</h2>
</BlockSlot>
<BlockSlot @name="body">
<p>
{{#if (gt items.length 0)}}
No ACLs where found matching that search, or you may not have access to view the ACLs you are searching for.
{{else}}
There don't seem to be any ACLs yet, or you may not have access to view ACLs yet.
{{/if}}
</p>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
</BlockSlot>
</AppView>
@ondelete={{route-action 'delete'}}
@onuse={{route-action 'use'}}
@onclone={{route-action 'clone'}}
>
</Consul::Acl::List>
</collection.Collection>
<collection.Empty>
<EmptyState @allowLogin={{true}}>
<BlockSlot @name="header">
<h2>
{{#if (gt items.length 0)}}
No ACLs found
{{else}}
Welcome to ACLs
{{/if}}
</h2>
</BlockSlot>
<BlockSlot @name="body">
<p>
{{#if (gt items.length 0)}}
No ACLs where found matching that search, or you may not have access to view the ACLs you are searching for.
{{else}}
There don't seem to be any ACLs yet, or you may not have access to view ACLs yet.
{{/if}}
</p>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
</BlockSlot>
</AppView>
{{/let}}
{{/let}}

View File

@ -3,104 +3,118 @@
{{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|}}
<AppView
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
>
<BlockSlot @name="notification" as |status type item error|>
<Consul::Policy::Notifications
@type={{type}}
@status={{status}}
@item={{item}}
@error={{error}}
/>
</BlockSlot>
<BlockSlot @name="header">
<h1>
Access Controls
</h1>
</BlockSlot>
<BlockSlot @name="nav">
{{#if isAuthorized }}
{{partial 'dc/acls/nav'}}
(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}}
>
<BlockSlot @name="notification" as |status type item error|>
<Consul::Policy::Notifications
@type={{type}}
@status={{status}}
@item={{item}}
@error={{error}}
/>
</BlockSlot>
<BlockSlot @name="header">
<h1>
Access Controls
</h1>
</BlockSlot>
<BlockSlot @name="nav">
{{#if isAuthorized }}
{{partial 'dc/acls/nav'}}
{{/if}}
</BlockSlot>
<BlockSlot @name="actions">
<a data-test-create href="{{href-to 'dc.acls.policies.create'}}" class="type-create">Create</a>
</BlockSlot>
<BlockSlot @name="toolbar">
{{#if (gt items.length 0) }}
<Consul::Policy::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@filter={{filters}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="actions">
<a data-test-create href="{{href-to 'dc.acls.policies.create'}}" class="type-create">Create</a>
</BlockSlot>
<BlockSlot @name="toolbar">
{{#if (gt items.length 0) }}
<Consul::Policy::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
</BlockSlot>
<BlockSlot @name="content">
<DataCollection
@type="policy"
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Policy::List
@items={{collection.items}}
@ondelete={{route-action 'delete'}}
/>
</collection.Collection>
<collection.Empty>
<EmptyState @allowLogin={{true}}>
<BlockSlot @name="header">
<h2>
{{#if (gt items.length 0)}}
No policies found
{{else}}
Welcome to Policies
{{/if}}
</h2>
</BlockSlot>
<BlockSlot @name="body">
<p>
{{#if (gt items.length 0)}}
No policies where found matching that search, or you may not have access to view the policies you are searching for.
{{else}}
There don't seem to be any policies, or you may not have access to view policies yet.
{{/if}}
</p>
</BlockSlot>
<BlockSlot @name="actions">
<li class="docs-link">
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/acl/policy" rel="noopener noreferrer" target="_blank">Documentation on policies</a>
</li>
<li class="learn-link">
<a href="{{env 'CONSUL_LEARN_URL'}}/consul/security-networking/managing-acl-policies" rel="noopener noreferrer" target="_blank">Read the guide</a>
</li>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
</BlockSlot>
</AppView>
@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}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Policy::List
@items={{collection.items}}
@ondelete={{route-action 'delete'}}
/>
</collection.Collection>
<collection.Empty>
<EmptyState @allowLogin={{true}}>
<BlockSlot @name="header">
<h2>
{{#if (gt items.length 0)}}
No policies found
{{else}}
Welcome to Policies
{{/if}}
</h2>
</BlockSlot>
<BlockSlot @name="body">
<p>
{{#if (gt items.length 0)}}
No policies where found matching that search, or you may not have access to view the policies you are searching for.
{{else}}
There don't seem to be any policies, or you may not have access to view policies yet.
{{/if}}
</p>
</BlockSlot>
<BlockSlot @name="actions">
<li class="docs-link">
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/acl/policy" rel="noopener noreferrer" target="_blank">Documentation on policies</a>
</li>
<li class="learn-link">
<a href="{{env 'CONSUL_LEARN_URL'}}/consul/security-networking/managing-acl-policies" rel="noopener noreferrer" target="_blank">Read the guide</a>
</li>
</BlockSlot>
</EmptyState>
</collection.Empty>
</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}}
@ -99,5 +110,4 @@
</DataCollection>
</BlockSlot>
</AppView>
{{/let}}
{{/let}}

View File

@ -4,110 +4,122 @@
{{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|}}
<AppView
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
>
<BlockSlot @name="notification" as |status type item error|>
<Consul::Token::Notifications
@type={{type}}
@status={{status}}
@item={{item}}
@error={{error}}
/>
</BlockSlot>
<BlockSlot @name="header">
<h1>
Access Controls
</h1>
</BlockSlot>
<BlockSlot @name="nav">
{{#if isAuthorized }}
{{partial 'dc/acls/nav'}}
{{/if}}
</BlockSlot>
<BlockSlot @name="actions">
<a data-test-create href="{{href-to 'dc.acls.tokens.create'}}" class="type-create">Create</a>
</BlockSlot>
<BlockSlot @name="toolbar">
{{#if (gt items.length 0)}}
<Consul::Token::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
(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
)
)
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
kind=(action (mut kind) value="target.selectedItems")
}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
{{#if (token/is-legacy items)}}
<Notice
@type="info"
as |notice|>
<notice.Header>
<h2>Update</h2>
</notice.Header>
<notice.Body>
<p data-test-notification-update>We have upgraded our ACL System to allow the creation of reusable policies that can be applied to tokens. Read more about the changes and how to upgrade legacy tokens in our <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
</notice.Body>
</Notice>
{{/if}}
<DataCollection
@type="token"
@sort={{sort}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Token::List
@items={{collection.items}}
@token={{token}}
@onuse={{route-action 'use'}}
@ondelete={{route-action 'delete'}}
@onlogout={{route-action 'logout'}}
@onclone={{route-action 'clone'}}
/>
</collection.Collection>
<collection.Empty>
<EmptyState @allowLogin={{true}}>
<BlockSlot @name="header">
<h2>
{{#if (gt items.length 0)}}
No tokens found
{{else}}
Welcome to ACL Tokens
{{/if}}
</h2>
</BlockSlot>
<BlockSlot @name="body">
<p>
{{#if (gt items.length 0)}}
No tokens where found matching that search, or you may not have access to view the tokens you are searching for.
{{else}}
There don't seem to be any tokens, or you may not have access to view tokens yet.
{{/if}}
</p>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
</BlockSlot>
</AppView>
{{/let}}
items
as |sort filters items|}}
<AppView
@authorized={{isAuthorized}}
@enabled={{isEnabled}}
>
<BlockSlot @name="notification" as |status type item error|>
<Consul::Token::Notifications
@type={{type}}
@status={{status}}
@item={{item}}
@error={{error}}
/>
</BlockSlot>
<BlockSlot @name="header">
<h1>
Access Controls
</h1>
</BlockSlot>
<BlockSlot @name="nav">
{{#if isAuthorized }}
{{partial 'dc/acls/nav'}}
{{/if}}
</BlockSlot>
<BlockSlot @name="actions">
<a data-test-create href="{{href-to 'dc.acls.tokens.create'}}" class="type-create">Create</a>
</BlockSlot>
<BlockSlot @name="toolbar">
{{#if (gt items.length 0)}}
<Consul::Token::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@filter={{filters}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
{{#if (token/is-legacy items)}}
<Notice
@type="info"
as |notice|>
<notice.Header>
<h2>Update</h2>
</notice.Header>
<notice.Body>
<p data-test-notification-update>We have upgraded our ACL System to allow the creation of reusable policies that can be applied to tokens. Read more about the changes and how to upgrade legacy tokens in our <a href="{{env 'CONSUL_DOCS_URL'}}/guides/acl-migrate-tokens.html" target="_blank" rel="noopener noreferrer">documentation</a>.</p>
</notice.Body>
</Notice>
{{/if}}
<DataCollection
@type="token"
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Token::List
@items={{collection.items}}
@token={{token}}
@onuse={{route-action 'use'}}
@ondelete={{route-action 'delete'}}
@onlogout={{route-action 'logout'}}
@onclone={{route-action 'clone'}}
/>
</collection.Collection>
<collection.Empty>
<EmptyState @allowLogin={{true}}>
<BlockSlot @name="header">
<h2>
{{#if (gt items.length 0)}}
No tokens found
{{else}}
Welcome to ACL Tokens
{{/if}}
</h2>
</BlockSlot>
<BlockSlot @name="body">
<p>
{{#if (gt items.length 0)}}
No tokens where found matching that search, or you may not have access to view the tokens you are searching for.
{{else}}
There don't seem to be any tokens, or you may not have access to view tokens yet.
{{/if}}
</p>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
</BlockSlot>
</AppView>
{{/let}}

View File

@ -6,104 +6,115 @@
</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")
)
(hash
access=(hash
value=(if access (split access ',') undefined)
change=(action (mut access) value="target.selectedItems")
)
) as |filters|}}
{{#let (or sortBy "Action:asc") as |sort|}}
<AppView>
<BlockSlot @name="header">
<h1>
Intentions <em>{{format-number items.length}} total</em>
</h1>
<label for="toolbar-toggle"></label>
</BlockSlot>
<BlockSlot @name="actions">
<a data-test-create href="{{href-to 'dc.intentions.create'}}" class="type-create">Create</a>
</BlockSlot>
<BlockSlot @name="toolbar">
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
change=(action (mut searchproperty) value="target.selectedItems")
default=searchProperties
)
)
{{#if (gt items.length 0) }}
<Consul::Intention::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
api.data
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
as |sort filters items|}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
access=(action (mut access) value="target.selectedItems")
}}
/>
{{/if}}
<AppView>
<BlockSlot @name="header">
<h1>
Intentions <em>{{format-number items.length}} total</em>
</h1>
<label for="toolbar-toggle"></label>
</BlockSlot>
<BlockSlot @name="actions">
<a data-test-create href="{{href-to 'dc.intentions.create'}}" class="type-create">Create</a>
</BlockSlot>
<BlockSlot @name="toolbar">
</BlockSlot>
{{#if (gt items.length 0) }}
<Consul::Intention::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@filter={{filters}}
/>
{{/if}}
</BlockSlot>
<BlockSlot @name="content">
<DataWriter
@sink={{concat '/' dc '/' nspace '/intention/'}}
@type="intention"
@ondelete={{refresh-route}}
as |writer|>
<BlockSlot @name="content">
<DataWriter
@sink={{concat '/' dc '/' nspace '/intention/'}}
<DataCollection
@type="intention"
@ondelete={{refresh-route}}
as |writer|>
<BlockSlot @name="content">
<DataCollection
@type="intention"
@sort={{sort}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Intention::List
@items={{collection.items}}
@delete={{writer.delete}}
as |list|>
<list.CustomResourceNotice />
<list.Table />
</Consul::Intention::List>
</collection.Collection>
<collection.Empty>
<EmptyState @allowLogin={{true}}>
<BlockSlot @name="header">
<h2>
{{#if (gt items.length 0)}}
No intentions found
{{else}}
Welcome to Intentions
{{/if}}
</h2>
</BlockSlot>
<BlockSlot @name="body">
<p>
{{#if (gt items.length 0)}}
No intentions where found matching that search, or you may not have access to view the intentions you are searching for.
{{else}}
There don't seem to be any intentions, or you may not have access to view intentions yet.
{{/if}}
</p>
</BlockSlot>
<BlockSlot @name="actions">
<li class="docs-link">
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/intention" rel="noopener noreferrer" target="_blank">Documentation on intentions</a>
</li>
<li class="learn-link">
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/getting-started/connect" rel="noopener noreferrer" target="_blank">Read the guide</a>
</li>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
</BlockSlot>
</DataWriter>
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Intention::List
@items={{collection.items}}
@delete={{writer.delete}}
as |list|>
<list.CustomResourceNotice />
<list.Table />
</Consul::Intention::List>
</collection.Collection>
<collection.Empty>
<EmptyState @allowLogin={{true}}>
<BlockSlot @name="header">
<h2>
{{#if (gt items.length 0)}}
No intentions found
{{else}}
Welcome to Intentions
{{/if}}
</h2>
</BlockSlot>
<BlockSlot @name="body">
<p>
{{#if (gt items.length 0)}}
No intentions where found matching that search, or you may not have access to view the intentions you are searching for.
{{else}}
There don't seem to be any intentions, or you may not have access to view intentions yet.
{{/if}}
</p>
</BlockSlot>
<BlockSlot @name="actions">
<li class="docs-link">
<a href="{{env 'CONSUL_DOCS_URL'}}/commands/intention" rel="noopener noreferrer" target="_blank">Documentation on intentions</a>
</li>
<li class="learn-link">
<a href="{{env 'CONSUL_DOCS_LEARN_URL'}}/consul/getting-started/connect" rel="noopener noreferrer" target="_blank">Read the guide</a>
</li>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
</BlockSlot>
</AppView>
{{/let}}
{{/let}}
</DataWriter>
</BlockSlot>
</AppView>
{{/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}}
@ -104,5 +113,4 @@
</DataWriter>
</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}}
@ -59,5 +71,4 @@
</DataCollection>
</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)
(split searchproperty ',')
searchProperties
{{#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")
)
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
)
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,12 +60,11 @@
<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>
</collection.Empty>
</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)
(split searchproperty ',')
searchProperties
{{#let
(hash
value=(or sortBy "Status:asc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "Status:asc") as |sort|}}
{{#let (reject-by 'Service.Kind' 'connect-proxy' item.Services) as |items|}}
(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
)
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}}
@ -55,6 +68,4 @@
</collection.Empty>
</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')
<EventSource @src={{items}} />
{{#let
(hash
value=(or sortBy "Name:asc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "Name:asc") as |sort|}}
<EventSource @src={{items}} />
(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}}
@ -86,5 +97,4 @@
</DataCollection>
</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)
(split searchproperty ',')
(array 'Name' 'Tags')
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 ',')
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)
(split searchproperty ',')
searchProperties
{{#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")
)
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
)
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>
@ -54,5 +65,4 @@
</DataCollection>
</div>
{{/let}}
{{/let}}

View File

@ -1,12 +1,26 @@
<div class="tab-section">
{{#let (hash
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
{{#let
(hash
value=(or sortBy "DestinationName:asc")
change=(action (mut sortBy) value="target.selected")
)
) as |filters|}}
{{#let (or sortBy "DestinationName:asc") as |sort|}}
{{#if (gt proxy.Service.Proxy.Upstreams.length 0)}}
(hash
searchproperty=(hash
value=(if (not-eq searchproperty undefined)
(split searchproperty ',')
searchProperties
)
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)
(split searchproperty ',')
searchProperties
{{#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")
)
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
)
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}}
@ -51,6 +64,5 @@
</EmptyState>
</collection.Empty>
</DataCollection>
{{/let}}
{{/let}}
</div>

View File

@ -12,74 +12,83 @@ 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")
)
(hash
access=(hash
value=(if access (split access ',') undefined)
change=(action (mut access) value="target.selectedItems")
)
) as |filters|}}
{{#let (or sortBy "Action:asc") as |sort|}}
<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>
</Portal>
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>
</Portal>
{{#if (gt items.length 0) }}
<Consul::Intention::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
<Consul::Intention::SearchBar
@search={{search}}
@onsearch={{action (mut search) value="target.value"}}
@sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@sort={{sort}}
@filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
access=(action (mut access) value="target.selectedItems")
}}
/>
@filter={{filters}}
/>
{{/if}}
<DataWriter
@sink={{concat '/' dc '/' nspace '/intention/'}}
<DataWriter
@sink={{concat '/' dc '/' nspace '/intention/'}}
@type="intention"
@ondelete={{refresh-route}}
as |writer|>
<BlockSlot @name="content">
<DataCollection
@type="intention"
@ondelete={{refresh-route}}
as |writer|>
<BlockSlot @name="content">
<DataCollection
@type="intention"
@sort={{sort}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Intention::List
@items={{collection.items}}
@check={{search}}
@delete={{writer.delete}}
as |list|>
<list.CustomResourceNotice />
<list.CheckNotice />
<list.Table @routeName="dc.services.show.intentions.edit" />
</Consul::Intention::List>
</collection.Collection>
<collection.Empty>
<EmptyState>
<BlockSlot @name="body">
<p>
There are no intentions {{if (gt items.length 0) 'found '}} for this service.
</p>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
</BlockSlot>
</DataWriter>
</div>
{{/let}}
{{/let}}
@sort={{sort.value}}
@filters={{filters}}
@search={{search}}
@items={{items}}
as |collection|>
<collection.Collection>
<Consul::Intention::List
@items={{collection.items}}
@check={{search}}
@delete={{writer.delete}}
as |list|>
<list.CustomResourceNotice />
<list.CheckNotice />
<list.Table @routeName="dc.services.show.intentions.edit" />
</Consul::Intention::List>
</collection.Collection>
<collection.Empty>
<EmptyState>
<BlockSlot @name="body">
<p>
There are no intentions {{if (gt items.length 0) 'found '}} for this service.
</p>
</BlockSlot>
</EmptyState>
</collection.Empty>
</DataCollection>
</BlockSlot>
</DataWriter>
</div>
{{/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