mirror of https://github.com/status-im/consul.git
ui: Create PopoverSelect, CatalogToolbar, and update tests (#7489)
* Create PopoverSelect component and styling * Create CatalogToolbar component and Styling * ui: Adds `selectable-key-values` helper (#7472) Preferably we want all copy/text to live in the template. Whilst you can achieve what we've done here with a combination of different helpers, as we will be using this approach in various places it's probably best to make a helper. We also hit an ember bug related to using the `let` helper and trying to access `thingThatWasLet.firstObject` (which can also be worked around using `object-at`). Moving everything to a helper 'sorted' everything. Probably worthwhile noting that if the sort option themselves become dynamic, I'm not sure if the helper here would actually react as you would expect (I'm aware that ember helpers on react on the root arguments, not necesarily sub properties of those arguments). If we get to that point this helper could take the same approach as what I believe ember-composable-helpers does to get around this, or move them to the view controller. If we do ever moved this to the view controller, we can still use the exported function from the new helper here to keep using the same functionality and tests we have here. * Create tests for sorting services with CatalogToolbar * Add rule to print 'ember/no-global-jquery' as a warning Co-authored-by: John Cowen <johncowen@users.noreply.github.com>
This commit is contained in:
parent
7e83dc2aeb
commit
b2ecc65d21
|
@ -16,7 +16,8 @@ module.exports = {
|
|||
rules: {
|
||||
'no-unused-vars': ['error', { args: 'none' }],
|
||||
'ember/no-new-mixins': ['warn'],
|
||||
'ember/no-jquery': 'warn'
|
||||
'ember/no-jquery': 'warn',
|
||||
'ember/no-global-jquery': 'warn'
|
||||
},
|
||||
overrides: [
|
||||
// node files
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<form class="catalog-toolbar" data-test-catalog-toolbar>
|
||||
<FreetextFilter @searchable={{searchable}} @value={{value}} @placeholder="Search" />
|
||||
<PopoverSelect
|
||||
data-popover-select
|
||||
@selected={{selected}}
|
||||
@options={{options}}
|
||||
@onchange={{onchange}}
|
||||
@title='Sort By'
|
||||
/>
|
||||
</form>
|
|
@ -0,0 +1,5 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: '',
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
{{yield}}
|
||||
{{#if (gt items.length 0)}}
|
||||
<ListCollection @cellHeight={{73}} @items={{items}} class="consul-service-list" as |item index|>
|
||||
<a href={{href-to routeName item.Name}} class={{service/health-checks item}}>
|
||||
<a data-test-service-name href={{href-to routeName item.Name}} class={{service/health-checks item}}>
|
||||
{{item.Name}}
|
||||
</a>
|
||||
<ul>
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
<div class="popover-select" ...attributes>
|
||||
<PopoverMenu @keyboardAccess={{false}}>
|
||||
<BlockSlot @name="trigger">
|
||||
<span>
|
||||
{{selected.value}}
|
||||
</span>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="menu" as |id send keypressClick change|>
|
||||
<li role="separator">
|
||||
{{title}}
|
||||
</li>
|
||||
{{#each options as |option|}}
|
||||
<li role="none" class={{if (eq selected.key option.key) 'is-active'}}>
|
||||
<button tabindex="-1" role="menuitem" type="button" value={{option.key}} onclick={{action (queue (action 'change' option) change )}}>{{option.value}}</button>
|
||||
</li>
|
||||
{{/each}}
|
||||
</BlockSlot>
|
||||
</PopoverMenu>
|
||||
</div>
|
|
@ -0,0 +1,19 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
actions: {
|
||||
change: function(option, e) {
|
||||
// We fake an event here, which could be a bit of a footbun if we treat
|
||||
// it completely like an event, we should be abe to avoid doing this
|
||||
// when we move to glimmer components (this.args.selected vs this.selected)
|
||||
this.onchange({
|
||||
target: {
|
||||
selected: option,
|
||||
},
|
||||
// make this vaguely event like to avoid
|
||||
// having a separate property
|
||||
preventDefault: function(e) {},
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
|
@ -4,6 +4,7 @@ import WithEventSource from 'consul-ui/mixins/with-event-source';
|
|||
import WithSearching from 'consul-ui/mixins/with-searching';
|
||||
export default Controller.extend(WithEventSource, WithSearching, {
|
||||
queryParams: {
|
||||
sortBy: 'sort',
|
||||
s: {
|
||||
as: 'filter',
|
||||
},
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import { helper } from '@ember/component/helper';
|
||||
import { slugify } from 'consul-ui/helpers/slugify';
|
||||
export const selectableKeyValues = function(params = [], hash = {}) {
|
||||
let selected;
|
||||
|
||||
const items = params.map(function(item, i) {
|
||||
let key, value;
|
||||
switch (typeof item) {
|
||||
case 'string':
|
||||
key = slugify([item]);
|
||||
value = item;
|
||||
break;
|
||||
default:
|
||||
if (item.length > 1) {
|
||||
key = item[0];
|
||||
value = item[1];
|
||||
} else {
|
||||
key = slugify([item[0]]);
|
||||
value = item[0];
|
||||
}
|
||||
break;
|
||||
}
|
||||
const kv = {
|
||||
key: key,
|
||||
value: value,
|
||||
};
|
||||
switch (typeof hash.selected) {
|
||||
case 'string':
|
||||
if (hash.selected === item[0]) {
|
||||
selected = kv;
|
||||
}
|
||||
break;
|
||||
case 'number':
|
||||
if (hash.selected === i) {
|
||||
selected = kv;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return kv;
|
||||
});
|
||||
return {
|
||||
items: items,
|
||||
selected: typeof selected === 'undefined' ? items[0] : selected,
|
||||
};
|
||||
};
|
||||
export default helper(selectableKeyValues);
|
|
@ -48,3 +48,9 @@
|
|||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
%split-button {
|
||||
@extend %secondary-button;
|
||||
padding: 0 8px !important;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
|
|
|
@ -112,3 +112,23 @@
|
|||
%internal-button-dangerous:hover {
|
||||
@extend %internal-button-dangerous-intent;
|
||||
}
|
||||
|
||||
%split-button span::after {
|
||||
@extend %as-pseudo;
|
||||
position: absolute;
|
||||
background-color: $gray-300;
|
||||
width: 1px;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
margin-left: 8px;
|
||||
}
|
||||
%split-button::before {
|
||||
@extend %as-pseudo;
|
||||
height: 16px;
|
||||
}
|
||||
%sort-button::before {
|
||||
@extend %with-sort-icon;
|
||||
margin-top: 8px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
|
|
@ -27,3 +27,28 @@
|
|||
%more-popover-menu-panel [id$='-']:first-child:checked ~ ul label[for$='-'] + [role='menu'] {
|
||||
display: block;
|
||||
}
|
||||
%popover-menu {
|
||||
@extend %display-toggle-siblings;
|
||||
}
|
||||
%popover-menu + label > * {
|
||||
@extend %toggle-button;
|
||||
}
|
||||
%popover-menu-panel {
|
||||
@extend %menu-panel;
|
||||
width: 192px;
|
||||
}
|
||||
%popover-menu + label + div {
|
||||
@extend %popover-menu-panel;
|
||||
}
|
||||
%popover-menu-panel:not(.above) {
|
||||
top: 38px;
|
||||
}
|
||||
%popover-menu-panel:not(.left) {
|
||||
right: 10px;
|
||||
}
|
||||
%popover-menu-panel li [role='menu'] {
|
||||
display: none;
|
||||
}
|
||||
%popover-menu-panel [id$='-']:first-child:checked ~ ul label[for$='-'] + [role='menu'] {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -7,3 +7,12 @@
|
|||
%more-popover-menu + label > * {
|
||||
font-size: 0;
|
||||
}
|
||||
%popover-menu + label > *::after {
|
||||
@extend %with-chevron-down-icon, %as-pseudo;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
%popover-menu + label > * {
|
||||
@extend %split-button, %sort-button;
|
||||
}
|
||||
|
|
|
@ -146,6 +146,7 @@ $search-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" fill
|
|||
$service-identity-svg: url('data:image/svg+xml;charset=UTF-8,<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M6.5 13a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm11-3a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7zm-4 11a3.5 3.5 0 1 1 0-7 3.5 3.5 0 0 1 0 7z" id="a"/></defs><use fill="%239E2159" xlink:href="%23a" fill-rule="evenodd"/></svg>');
|
||||
$settings-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M19.43 12.98c.04-.32.07-.64.07-.98 0-.34-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65A.488.488 0 0 0 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98 0 .33.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 16c-2.206 0-4-1.794-4-4s1.794-4 4-4 4 1.794 4 4-1.794 4-4 4z" fill="%23000"/></svg>');
|
||||
$source-file-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M13.714 9.143l3.572 3.571-3.572 3.572-.714-1.4 2.143-2.172L13 10.571l.714-1.428zm-3.571 1.4L8 12.714l2.143 2.143-.714 1.429-3.572-3.572L9.43 9.143l.714 1.4zm8.571 10.028H4.43V3.43h10l4.285 4.285v12.857zM15.143 2H4.429C3.643 2 3 2.643 3 3.429V20.57C3 21.357 3.643 22 4.429 22h14.285c.786 0 1.429-.643 1.429-1.429V7l-5-5z" fill="%23000"/></svg>');
|
||||
$sort-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M0,10.0867585 L6,10.0867585 L6,8.40563206 L0,8.40563206 L0,10.0867585 L0,10.0867585 Z M3,12.4056321 L3,14.0867585 L0,14.0867585 L0,12.4056321 L3,12.4056321 Z M15.1301377,0 L15.1301377,1.68112641 L0,1.68112641 L0,0 L15.1301377,0 Z M13.8692929,4.62309763 L13.8692929,11.8384922 L16.8112641,8.89802258 L18,10.0867585 L13.0287297,15.0580288 L8.05745938,10.0867585 L9.24619526,8.89802258 L12.1881665,11.8393328 L12.1881665,4.62309763 L13.8692929,4.62309763 Z M10.0867585,4.20281603 L10.0867585,5.88394244 L0,5.88394244 L0,4.20281603 L10.0867585,4.20281603 Z" fill="%23000"/></svg>');
|
||||
$star-fill-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27z" fill="%23000"/></svg>');
|
||||
$star-outline-svg: url('data:image/svg+xml;charset=UTF-8,<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z" fill="%23000"/></svg>');
|
||||
$star-svg: url('data:image/svg+xml;charset=UTF-8,<svg width="10" height="9" viewBox="0 0 10 9" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="a" d="M5 7.196L7.575 8.75l-.683-2.93 2.275-1.97-2.996-.254L5 .833 3.83 3.596.832 3.85l2.275 1.97-.683 2.93z"/></defs><use fill="%239E2159" xlink:href="%23a" fill-rule="evenodd"/></svg>');
|
||||
|
|
|
@ -1468,6 +1468,16 @@
|
|||
mask-image: $source-file-svg;
|
||||
}
|
||||
|
||||
%with-sort-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $sort-svg;
|
||||
}
|
||||
%with-sort-mask {
|
||||
@extend %with-mask;
|
||||
-webkit-mask-image: $sort-svg;
|
||||
mask-image: $sort-svg;
|
||||
}
|
||||
|
||||
%with-star-fill-icon {
|
||||
@extend %with-icon;
|
||||
background-image: $star-fill-svg;
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
.filter-bar {
|
||||
@extend %filter-bar;
|
||||
}
|
||||
.catalog-toolbar {
|
||||
@extend %catalog-toolbar;
|
||||
}
|
||||
%catalog-toolbar {
|
||||
@extend %filter-bar;
|
||||
}
|
||||
%filter-bar [role='radiogroup'] {
|
||||
@extend %expanded-single-select;
|
||||
}
|
||||
|
|
|
@ -7,19 +7,36 @@
|
|||
%filter-bar + :not(.notice) {
|
||||
margin-top: 1.8em;
|
||||
}
|
||||
%catalog-toolbar {
|
||||
padding: 4px 8px;
|
||||
display: flex;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: -12px !important;
|
||||
border-bottom: 1px solid $gray-200;
|
||||
}
|
||||
@media #{$--horizontal-filters} {
|
||||
%filter-bar {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-between;
|
||||
}
|
||||
%catalog-toolbar {
|
||||
flex-direction: row;
|
||||
}
|
||||
%filter-bar > *:first-child {
|
||||
margin-left: 12px;
|
||||
}
|
||||
%catalog-toolbar > *:first-child {
|
||||
margin-left: 0px;
|
||||
}
|
||||
%filter-bar fieldset {
|
||||
min-width: 210px;
|
||||
width: auto;
|
||||
}
|
||||
%catalog-toolbar fieldset {
|
||||
min-width: none;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@media #{$--lt-horizontal-filters} {
|
||||
%filter-bar > *:first-child {
|
||||
|
|
|
@ -3,6 +3,9 @@
|
|||
border: $decor-border-100;
|
||||
border-radius: $decor-radius-100;
|
||||
}
|
||||
%catalog-toolbar > div {
|
||||
border: none;
|
||||
}
|
||||
// TODO: Move this elsewhere
|
||||
@media #{$--horizontal-selects} {
|
||||
%filter-bar label:not(:last-child) {
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
@import './grid-collection';
|
||||
@import './consul-service-list';
|
||||
@import './consul-service-instance-list';
|
||||
@import './popover-select';
|
||||
|
||||
/**/
|
||||
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
.popover-select {
|
||||
@extend %popover-select;
|
||||
}
|
||||
%popover-select > [type='checkbox'] {
|
||||
@extend %popover-menu;
|
||||
}
|
||||
%popover-select {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
padding-left: 12px;
|
||||
height: 100%;
|
||||
}
|
||||
%popover-select label {
|
||||
border-right: none !important;
|
||||
}
|
|
@ -1,26 +1,43 @@
|
|||
{{title 'Services'}}
|
||||
<AppView @class="service list">
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
Services <em>{{format-number services.length}} total</em>
|
||||
</h1>
|
||||
<label for="toolbar-toggle"></label>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<PhraseEditor @placeholder="service:name tag:name status:critical search-term" @value={{slice 0 terms.length terms}} @onchange={{action (mut terms) value='target.value'}} @searchable={{searchable}} />
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
{{#let (selectable-key-values
|
||||
(array "Name:asc" "A to Z")
|
||||
(array "Name:desc" "Z to A")
|
||||
selected=sortBy
|
||||
)
|
||||
as |sort|
|
||||
}}
|
||||
<AppView @class="service list">
|
||||
<BlockSlot @name="notification" as |status type|>
|
||||
{{partial 'dc/services/notifications'}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="header">
|
||||
<h1>
|
||||
Services <em>{{format-number items.length}} total</em>
|
||||
</h1>
|
||||
<label for="toolbar-toggle"></label>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="toolbar">
|
||||
{{#if (gt items.length 0) }}
|
||||
<CatalogToolbar
|
||||
@searchable={{searchable}}
|
||||
@value={{search}}
|
||||
@selected={{sort.selected}}
|
||||
@options={{sort.items}}
|
||||
@onchange={{action (mut sortBy) value='target.selected.key'}}
|
||||
/>
|
||||
{{/if}}
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="content">
|
||||
<ChangeableSet @dispatcher={{searchable}}>
|
||||
<BlockSlot @name="set" as |filtered|>
|
||||
<ConsulServiceList @routeName="dc.services.show" @items={{filtered}} @proxies={{proxies}}/> </BlockSlot>
|
||||
<ConsulServiceList @routeName="dc.services.show" @items={{sort-by sort.selected.key filtered}} @proxies={{proxies}}/>
|
||||
</BlockSlot>
|
||||
<BlockSlot @name="empty">
|
||||
<p>
|
||||
There are no services.
|
||||
</p>
|
||||
</BlockSlot>
|
||||
</ChangeableSet>
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
|
||||
</BlockSlot>
|
||||
</AppView>
|
||||
{{/let}}
|
||||
|
|
|
@ -2,17 +2,14 @@
|
|||
Feature: dc / services / list blocking
|
||||
Scenario: Viewing the listing pages for service
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 6 service models from yaml
|
||||
And 3 service models from yaml
|
||||
---
|
||||
- Name: Service-0
|
||||
- Name: Service-0-proxy
|
||||
Kind: 'connect-proxy'
|
||||
Kind: ~
|
||||
- Name: Service-1
|
||||
- Name: Service-1-proxy
|
||||
Kind: 'connect-proxy'
|
||||
Kind: ~
|
||||
- Name: Service-2
|
||||
- Name: Service-2-proxy
|
||||
Kind: 'connect-proxy'
|
||||
Kind: ~
|
||||
---
|
||||
And a network latency of 100
|
||||
When I visit the services page for yaml
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
@setupApplicationTest
|
||||
Feature: dc / services / sorting
|
||||
Scenario:
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And 6 service models from yaml
|
||||
---
|
||||
- Name: Service-A
|
||||
Kind: ~
|
||||
- Name: Service-B
|
||||
Kind: ~
|
||||
- Name: Service-C
|
||||
Kind: ~
|
||||
- Name: Service-D
|
||||
Kind: ~
|
||||
- Name: Service-E
|
||||
Kind: ~
|
||||
- Name: Service-F
|
||||
Kind: ~
|
||||
---
|
||||
When I visit the services page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.1.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-F
|
||||
- Service-E
|
||||
- Service-D
|
||||
- Service-C
|
||||
- Service-B
|
||||
- Service-A
|
||||
---
|
||||
When I click selected on the sort
|
||||
When I click options.0.button on the sort
|
||||
Then I see name on the services vertically like yaml
|
||||
---
|
||||
- Service-A
|
||||
- Service-B
|
||||
- Service-C
|
||||
- Service-D
|
||||
- Service-E
|
||||
- Service-F
|
||||
---
|
|
@ -0,0 +1,10 @@
|
|||
import steps from '../steps';
|
||||
|
||||
// step definitions that are shared between features should be moved to the
|
||||
// tests/acceptance/steps/steps.js file
|
||||
|
||||
export default function(assert) {
|
||||
return steps(assert).then('I should find a file', function() {
|
||||
assert.ok(true, this.step);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import steps from '../../steps';
|
||||
|
||||
// step definitions that are shared between features should be moved to the
|
||||
// tests/acceptance/steps/steps.js file
|
||||
|
||||
export default function(assert) {
|
||||
return steps(assert).then('I should find a file', function() {
|
||||
assert.ok(true, this.step);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { module, skip } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | catalog-toolbar', function(hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
skip('it renders', async function(assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`<CatalogToolbar />`);
|
||||
|
||||
assert.equal(this.element.querySelector('form').length, 1);
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<CatalogToolbar>
|
||||
template block text
|
||||
</CatalogToolbar>
|
||||
`);
|
||||
|
||||
assert.equal(this.element.textContent.trim(), 'template block text');
|
||||
});
|
||||
});
|
|
@ -21,6 +21,8 @@ import radiogroup from 'consul-ui/tests/lib/page-object/radiogroup';
|
|||
import tabgroup from 'consul-ui/tests/lib/page-object/tabgroup';
|
||||
import freetextFilter from 'consul-ui/tests/pages/components/freetext-filter';
|
||||
import catalogFilter from 'consul-ui/tests/pages/components/catalog-filter';
|
||||
import catalogToolbar from 'consul-ui/tests/pages/components/catalog-toolbar';
|
||||
import popoverSort from 'consul-ui/tests/pages/components/popover-sort';
|
||||
import aclFilter from 'consul-ui/tests/pages/components/acl-filter';
|
||||
import intentionFilter from 'consul-ui/tests/pages/components/intention-filter';
|
||||
import tokenListFactory from 'consul-ui/tests/pages/components/token-list';
|
||||
|
@ -75,10 +77,10 @@ export default {
|
|||
index: create(index(visitable, collection)),
|
||||
dcs: create(dcs(visitable, clickable, attribute, collection)),
|
||||
services: create(
|
||||
services(visitable, clickable, text, attribute, collection, page, catalogFilter, radiogroup)
|
||||
services(visitable, clickable, text, attribute, collection, page, popoverSort, radiogroup)
|
||||
),
|
||||
service: create(
|
||||
service(visitable, attribute, collection, text, consulIntentionList, catalogFilter, tabgroup)
|
||||
service(visitable, attribute, collection, text, consulIntentionList, catalogToolbar, tabgroup)
|
||||
),
|
||||
instance: create(instance(visitable, attribute, collection, text, tabgroup)),
|
||||
nodes: create(nodes(visitable, clickable, attribute, collection, catalogFilter)),
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
import { triggerable } from 'ember-cli-page-object';
|
||||
export default {
|
||||
search: triggerable('keypress', '[name="s"]'),
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
import { clickable, collection } from 'ember-cli-page-object';
|
||||
export default {
|
||||
scope: '[data-popover-select]',
|
||||
selected: clickable('button'),
|
||||
options: collection('li[role="none"]', {
|
||||
button: clickable('button'),
|
||||
}),
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
export default function(visitable, clickable, text, attribute, collection, page, filter) {
|
||||
export default function(visitable, clickable, text, attribute, collection, page, popoverSort) {
|
||||
const service = {
|
||||
name: text('a span:nth-child(2)'),
|
||||
name: text('[data-test-service-name]'),
|
||||
service: clickable('a'),
|
||||
externalSource: attribute('data-test-external-source', '[data-test-external-source]'),
|
||||
kind: attribute('data-test-kind', '[data-test-kind]'),
|
||||
|
@ -12,7 +12,7 @@ export default function(visitable, clickable, text, attribute, collection, page,
|
|||
name: clickable('a'),
|
||||
}),
|
||||
navigation: page.navigation,
|
||||
filter: filter,
|
||||
home: clickable('[data-test-home]'),
|
||||
sort: popoverSort,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
/* eslint no-console: "off" */
|
||||
import $ from '-jquery';
|
||||
|
||||
const notFound = 'Element not found';
|
||||
const cannotDestructure = "Cannot destructure property 'context'";
|
||||
const cannotReadContext = "Cannot read property 'context' of undefined";
|
||||
|
@ -57,6 +59,40 @@ export default function(scenario, assert, find, currentPage) {
|
|||
);
|
||||
});
|
||||
})
|
||||
.then('I see $property on the $component vertically like yaml\n$yaml', function(
|
||||
property,
|
||||
component,
|
||||
yaml
|
||||
) {
|
||||
const _component = currentPage()[component];
|
||||
const iterator = new Array(_component.length).fill(true);
|
||||
assert.ok(iterator.length > 0);
|
||||
|
||||
const items = _component.toArray().sort((a, b) => {
|
||||
return (
|
||||
$(a.scope)
|
||||
.get(0)
|
||||
.getBoundingClientRect().top -
|
||||
$(b.scope)
|
||||
.get(0)
|
||||
.getBoundingClientRect().top
|
||||
);
|
||||
});
|
||||
|
||||
iterator.forEach(function(item, i, arr) {
|
||||
const actual = typeof items[i][property] === 'undefined' ? null : items[i][property];
|
||||
|
||||
const expected = typeof yaml[i] === 'number' ? yaml[i].toString() : yaml[i];
|
||||
|
||||
assert.deepEqual(
|
||||
actual,
|
||||
expected,
|
||||
`Expected to see ${property} on ${component}[${i}] as ${JSON.stringify(
|
||||
expected
|
||||
)}, was ${JSON.stringify(actual)}`
|
||||
);
|
||||
});
|
||||
})
|
||||
.then(['I see $property on the $component'], function(property, component) {
|
||||
// TODO: Time to work on repetition
|
||||
// Collection
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
import { selectableKeyValues } from 'consul-ui/helpers/selectable-key-values';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Helper | selectable-key-values', function() {
|
||||
test('it turns arrays into key values and selects the first item by default', function(assert) {
|
||||
const actual = selectableKeyValues([['key-1', 'value-1'], ['key-2', 'value-2']]);
|
||||
assert.equal(actual.items.length, 2);
|
||||
assert.deepEqual(actual.selected, { key: 'key-1', value: 'value-1' });
|
||||
});
|
||||
test('it turns arrays into key values and selects the defined key', function(assert) {
|
||||
const actual = selectableKeyValues([['key-1', 'value-1'], ['key-2', 'value-2']], {
|
||||
selected: 'key-2',
|
||||
});
|
||||
assert.equal(actual.items.length, 2);
|
||||
assert.deepEqual(actual.selected, { key: 'key-2', value: 'value-2' });
|
||||
});
|
||||
test('it turns arrays into key values and selects the defined index', function(assert) {
|
||||
const actual = selectableKeyValues([['key-1', 'value-1'], ['key-2', 'value-2']], {
|
||||
selected: 1,
|
||||
});
|
||||
assert.equal(actual.items.length, 2);
|
||||
assert.deepEqual(actual.selected, { key: 'key-2', value: 'value-2' });
|
||||
});
|
||||
test('it turns arrays with only one element into key values and selects the defined index', function(assert) {
|
||||
const actual = selectableKeyValues([['Value 1'], ['Value 2']], { selected: 1 });
|
||||
assert.equal(actual.items.length, 2);
|
||||
assert.deepEqual(actual.selected, { key: 'value-2', value: 'Value 2' });
|
||||
});
|
||||
test('it turns strings into key values and selects the defined index', function(assert) {
|
||||
const actual = selectableKeyValues(['Value 1', 'Value 2'], { selected: 1 });
|
||||
assert.equal(actual.items.length, 2);
|
||||
assert.deepEqual(actual.selected, { key: 'value-2', value: 'Value 2' });
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue