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, 'no-unnecessary-component-helper': false,
'link-href-attributes': false, 'link-href-attributes': false,
// we need to be able to say tabindex={{@tabindex}}
'no-positive-tabindex': false,
'no-bare-strings': false, 'no-bare-strings': false,
}, },

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
{{#let components.MenuSeparator as |MenuSeparator|}} {{#let @components.MenuSeparator as |MenuSeparator|}}
<MenuSeparator> <MenuSeparator>
<BlockSlot @name="label"> <BlockSlot @name="label">
{{label}} {{@label}}
</BlockSlot> </BlockSlot>
</MenuSeparator> </MenuSeparator>
{{yield}} {{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 <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 ...attributes
@onclick={{action 'click'}} @onclick={{action @onclick this}}
@selected={{selected}} @selected={{this.selected}}
> >
<BlockSlot @name="label"> <BlockSlot @name="label">
{{yield}} {{yield}}

View File

@ -1,20 +1,16 @@
import Component from '@ember/component'; import Component from '@glimmer/component';
import { inject as service } from '@ember/service'; import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default Component.extend({ export default class Option extends Component {
tagName: '', @tracked selected;
dom: service('dom'),
didInsertElement: function() { @action
this._super(...arguments); connect() {
this.select.addOption(this); this.args.select.addOption(this);
}, }
willDestroyElement: function() { @action
this._super(...arguments); disconnect() {
this.select.removeOption(this); this.args.select.removeOption(this);
}, }
actions: { }
click: function(e) {
this.onclick(e, this.value);
},
},
});

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}} <div
<form class={{concat 'filter-bar' (if (eq secondary 'sort') ' with-sort')}} ...attributes> class="search-bar"
{{#yield-slot name="primary"}} ...attributes
<fieldset> >
{{yield}} <form
</fieldset> class="filter-bar"
{{else}} >
<FreetextFilter <div class="search">
@onsearch={{action onsearch}} {{yield (hash
@value={{value}} Search=(component "freetext-filter")
@placeholder={{or placeholder 'Search'}} Select=(component "popover-select")
/> ) to="search"}}
{{/yield-slot}} </div>
{{#yield-slot name="secondary"}} <div class="filters">
<fieldset> {{yield (hash
{{yield}} Search=(component "freetext-filter")
</fieldset> Select=(component "popover-select")
{{else}} ) to="filter"}}
{{#if options}} </div>
{{#if (eq secondary 'sort')}} <div class="sort">
{{else}} {{yield (hash
<RadioGroup Search=(component "freetext-filter")
@keyboardAccess={{true}} Select=(component "popover-select")
@value={{selected.key}} ) to="sort"}}
@items={{options}} </div>
@onchange={{action onchange}} </form>
/> {{#if this.isFiltered}}
{{/if}} <div class="search-bar-status">
{{/if}} <dl>
{{/yield-slot}} <dt>{{string-trim
</form> (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 Component from '@glimmer/component';
import Slotted from 'block-slots'; import { action } from '@ember/object';
import { diff, filters } from './utils';
export default Component.extend(Slotted, { export default class SearchBar extends Component {
tagName: '', // 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 { export default {
kinds: { kind: {
management: (item, value) => item.Type === value, management: (item, value) => item.Type === value,
client: (item, value) => item.Type === value, client: (item, value) => item.Type === value,
}, },

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
export default { export default {
kinds: { kind: {
'global-management': (item, value) => item.isGlobalManagement, 'global-management': (item, value) => item.isGlobalManagement,
global: (item, value) => !item.Local, global: (item, value) => !item.Local,
local: (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') SyncTime;
@attr('number') CreateIndex; @attr('number') CreateIndex;
@attr('number') ModifyIndex; @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; @attr('number') CreateTime;
// TODO: Figure out whether we need this or not // TODO: Figure out whether we need this or not
@attr() Datacenters; // string[] @attr() Datacenters; // string[]

View File

@ -48,6 +48,16 @@ export default class Service extends Model {
@attr() meta; // {} @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 */ /* Mesh properties involve both the service and the associated proxy */
@computed('ConnectedWithProxy', 'ConnectedWithGateway') @computed('ConnectedWithProxy', 'ConnectedWithGateway')
get MeshEnabled() { get MeshEnabled() {

View File

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

View File

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

View File

@ -21,8 +21,8 @@ export default class IndexRoute extends Route.extend(WithTokenActions) {
}, },
}; };
beforeModel(transition) { async beforeModel(transition) {
return this.settings.findBySlug('token').then(token => { const token = await this.settings.findBySlug('token');
// If you have a token set with AccessorID set to null (legacy mode) // If you have a token set with AccessorID set to null (legacy mode)
// then rewrite to the old acls // then rewrite to the old acls
if (token && get(token, 'AccessorID') === null) { if (token && get(token, 'AccessorID') === null) {
@ -30,7 +30,6 @@ export default class IndexRoute extends Route.extend(WithTokenActions) {
// everything works fine either way checking things manually // everything works fine either way checking things manually
this.replaceWith('dc.acls'); this.replaceWith('dc.acls');
} }
});
} }
model(params) { model(params) {
@ -43,6 +42,7 @@ export default class IndexRoute extends Route.extend(WithTokenActions) {
}), }),
nspace: this.modelFor('nspace').nspace.substr(1), nspace: this.modelFor('nspace').nspace.substr(1),
token: this.settings.findBySlug('token'), 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 { return {
dc: this.modelFor('dc').dc.Name, dc: this.modelFor('dc').dc.Name,
nspace: this.modelFor('nspace').nspace.substr(1), 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 { inject as service } from '@ember/service';
import Route from 'consul-ui/routing/route'; import Route from 'consul-ui/routing/route';
import { hash } from 'rsvp';
export default class IndexRoute extends Route { export default class IndexRoute extends Route {
@service('data-source/service') data; @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 dc = this.modelFor('dc').dc.Name;
const nspace = this.modelFor('nspace').nspace.substr(1); const nspace = this.modelFor('nspace').nspace.substr(1);
return hash({ const items = this.data.source(uri => uri`/${nspace}/${dc}/nodes`);
items: this.data.source(uri => uri`/${nspace}/${dc}/nodes`), const leader = this.data.source(uri => uri`/${nspace}/${dc}/leader`);
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) { setupController(controller, model) {

View File

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

View File

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

View File

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

View File

@ -14,11 +14,12 @@ export default class IndexRoute extends Route {
}, },
}; };
model(params) { async model(params) {
return { return {
dc: this.modelFor('dc').dc.Name, dc: this.modelFor('dc').dc.Name,
nspace: this.modelFor('nspace').nspace.substr(1) || 'default', nspace: this.modelFor('nspace').nspace.substr(1) || 'default',
slug: this.paramsFor('dc.services.show').name, 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) .slice(0, -1)
.join('.'); .join('.');
const name = this.modelFor(parent).slug; const name = this.modelFor(parent).slug;
const gatewayServices = await this.data.source( const items = await this.data.source(uri => uri`/${nspace}/${dc}/gateways/for-service/${name}`);
uri => uri`/${nspace}/${dc}/gateways/for-service/${name}`
);
return { return {
dc, dc,
nspace, 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/freetext-filter';
@import 'consul-ui/components/informed-action'; @import 'consul-ui/components/informed-action';
@import 'consul-ui/components/tab-nav'; @import 'consul-ui/components/tab-nav';
@import 'consul-ui/components/search-bar';
@import 'consul-ui/components/consul/tomography/graph'; @import 'consul-ui/components/consul/tomography/graph';
@import 'consul-ui/components/consul/discovery-chain'; @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 */ /* if it is a filter bar and the thing after the filter bar is a p then it also */
/* needs a top margun :S */ /* needs a top margun :S */
%app-view-content .tab-section > *:first-child:not(.filter-bar):not(table), %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 { %app-view-content .tab-section .consul-health-check-list {
margin-top: 1.25em; margin-top: 1.25em;
} }

View File

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

View File

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

View File

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

View File

@ -4,13 +4,28 @@
{{page-title 'Access Controls'}} {{page-title 'Access Controls'}}
{{/if}} {{/if}}
{{#let (hash {{#let
searchproperties=(if (not-eq searchproperty undefined)
(split searchproperty ',') (hash
(array 'Name' 'Description' 'Policy') 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 <AppView
@authorized={{isAuthorized}} @authorized={{isAuthorized}}
@enabled={{isEnabled}} @enabled={{isEnabled}}
@ -43,19 +58,15 @@
@onsearch={{action (mut search) value="target.value"}} @onsearch={{action (mut search) value="target.value"}}
@sort={{sort}} @sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}} @filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
}}
/> />
{{/if}} {{/if}}
</BlockSlot> </BlockSlot>
<BlockSlot @name="content"> <BlockSlot @name="content">
<DataCollection <DataCollection
@type="role" @type="role"
@sort={{sort}} @sort={{sort.value}}
@filters={{filters}} @filters={{filters}}
@search={{search}} @search={{search}}
@items={{items}} @items={{items}}
@ -99,5 +110,4 @@
</DataCollection> </DataCollection>
</BlockSlot> </BlockSlot>
</AppView> </AppView>
{{/let}}
{{/let}} {{/let}}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,27 +1,39 @@
<EventSource @src={{gatewayServices}} /> <EventSource @src={{items}} />
<div class="tab-section"> <div class="tab-section">
{{#let (hash {{#let
instances=(if instance (split instance ',') undefined)
searchproperties=(if (not-eq searchproperty undefined) (hash
(split searchproperty ',') value=(or sortBy "Status:asc")
(array 'Name' 'Tags') change=(action (mut sortBy) value="target.selected")
) )
) as |filters|}}
{{#let (or sortBy "Status:asc") as |sort|}} (hash
{{#if (gt gatewayServices.length 0)}} 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" /> <input type="checkbox" id="toolbar-toggle" />
<Consul::Upstream::SearchBar <Consul::Upstream::SearchBar
@search={{search}} @search={{search}}
@onsearch={{action (mut search) value="target.value"}} @onsearch={{action (mut search) value="target.value"}}
@sort={{sort}} @sort={{sort}}
@onsort={{action (mut sortBy) value="target.selected"}}
@filter={{filters}} @filter={{filters}}
@onfilter={{hash
searchproperty=(action (mut searchproperty) value="target.selectedItems")
instance=(action (mut instance) value="target.selectedItems")
}}
/> />
{{/if}} {{/if}}
<p> <p>
@ -29,10 +41,10 @@
</p> </p>
<DataCollection <DataCollection
@type="service" @type="service"
@sort={{sort}} @sort={{sort.value}}
@filters={{filters}} @filters={{filters}}
@search={{search}} @search={{search}}
@items={{gatewayServices}} @items={{items}}
as |collection|> as |collection|>
<collection.Collection> <collection.Collection>
<Consul::Upstream::List <Consul::Upstream::List
@ -46,12 +58,11 @@
<EmptyState> <EmptyState>
<BlockSlot @name="body"> <BlockSlot @name="body">
<p> <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> </p>
</BlockSlot> </BlockSlot>
</EmptyState> </EmptyState>
</collection.Empty> </collection.Empty>
</DataCollection> </DataCollection>
{{/let}}
{{/let}} {{/let}}
</div> </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, collection,
text, text,
isPresent, isPresent,
triggerable,
} from 'ember-cli-page-object'; } from 'ember-cli-page-object';
import { alias } from 'ember-cli-page-object/macros'; 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 radiogroup from 'consul-ui/components/radio-group/pageobject';
import tabgroup from 'consul-ui/components/tab-nav/pageobject'; import tabgroup from 'consul-ui/components/tab-nav/pageobject';
import authFormFactory from 'consul-ui/components/auth-form/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 emptyStateFactory from 'consul-ui/components/empty-state/pageobject';
import policyFormFactory from 'consul-ui/components/policy-form/pageobject'; import policyFormFactory from 'consul-ui/components/policy-form/pageobject';
@ -82,11 +79,6 @@ const cancelable = createCancelable(clickable, is);
// components // components
const tokenList = tokenListFactory(clickable, attribute, collection, deletable); const tokenList = tokenListFactory(clickable, attribute, collection, deletable);
const authForm = authFormFactory(submitable, clickable, attribute); 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 policyForm = policyFormFactory(submitable, cancelable, radiogroup, text);
const policySelector = policySelectorFactory(clickable, deletable, collection, alias, policyForm); const policySelector = policySelectorFactory(clickable, deletable, collection, alias, policyForm);
const roleForm = roleFormFactory(submitable, cancelable, policySelector); const roleForm = roleFormFactory(submitable, cancelable, policySelector);
@ -160,18 +152,7 @@ export default {
radiogroup radiogroup
) )
), ),
service: create( service: create(service(visitable, clickable, attribute, collection, text, consulIntentionList, tabgroup)),
service(
visitable,
clickable,
attribute,
collection,
text,
consulIntentionList,
catalogToolbar,
tabgroup
)
),
instance: create( instance: create(
instance( instance(
visitable, visitable,
@ -199,7 +180,7 @@ export default {
), ),
kvs: create(kvs(visitable, creatable, consulKvList)), kvs: create(kvs(visitable, creatable, consulKvList)),
kv: create(kv(visitable, attribute, submitable, deletable, cancelable, clickable)), 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)), acl: create(acl(visitable, submitable, deletable, cancelable, clickable)),
policies: create(policies(visitable, creatable, consulPolicyList, popoverSelect)), policies: create(policies(visitable, creatable, consulPolicyList, popoverSelect)),
policy: create(policy(visitable, submitable, deletable, cancelable, clickable, tokenList)), 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({ return creatable({
visit: visitable('/:dc/acls'), visit: visitable('/:dc/acls'),
acls: collection( acls: collection(
@ -11,6 +11,5 @@ export default function(visitable, deletable, creatable, clickable, attribute, c
confirmUse: clickable('[data-test-confirm-use]'), confirmUse: clickable('[data-test-confirm-use]'),
}) })
), ),
filter: filter('[data-test-acl-filter]'),
}); });
} }

View File

@ -1,13 +1,4 @@
export default function( export default function(visitable, clickable, attribute, collection, text, intentions, tabs) {
visitable,
clickable,
attribute,
collection,
text,
intentions,
filter,
tabs
) {
const page = { const page = {
visit: visitable('/:dc/services/:service'), visit: visitable('/:dc/services/:service'),
externalSource: attribute('data-test-external-source', '[data-test-external-source]', { externalSource: attribute('data-test-external-source', '[data-test-external-source]', {
@ -28,7 +19,6 @@ export default function(
'routing', 'routing',
'tags', 'tags',
]), ]),
filter: filter(),
// TODO: These need to somehow move to subpages // TODO: These need to somehow move to subpages
instances: collection('.consul-service-instance-list > ul > li:not(:first-child)', { instances: collection('.consul-service-instance-list > ul > li:not(:first-child)', {
address: text('[data-test-address]'), 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]]; expected = [items[0]];
actual = items.filter( actual = items.filter(
predicate({ predicate({
accesses: ['allow'], access: ['allow'],
}) })
); );
assert.deepEqual(actual, expected); assert.deepEqual(actual, expected);
@ -28,7 +28,7 @@ module('Unit | Filter | Predicates | intention', function() {
expected = [items[1]]; expected = [items[1]];
actual = items.filter( actual = items.filter(
predicate({ predicate({
accesses: ['deny'], access: ['deny'],
}) })
); );
assert.deepEqual(actual, expected); assert.deepEqual(actual, expected);
@ -36,7 +36,7 @@ module('Unit | Filter | Predicates | intention', function() {
expected = items; expected = items;
actual = items.filter( actual = items.filter(
predicate({ predicate({
accesses: ['allow', 'deny'], access: ['allow', 'deny'],
}) })
); );
assert.deepEqual(actual, expected); assert.deepEqual(actual, expected);

View File

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

View File

@ -1 +1,158 @@
common: 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