-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ui: Replaces Service listing filterbar with a phrase-editor search #5507
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import Component from '@ember/component'; | ||
import { get, set } from '@ember/object'; | ||
|
||
export default Component.extend({ | ||
classNames: ['phrase-editor'], | ||
item: '', | ||
remove: function(index, e) { | ||
this.items.removeAt(index, 1); | ||
this.onchange(e); | ||
}, | ||
add: function(e) { | ||
const value = get(this, 'item').trim(); | ||
if (value !== '') { | ||
set(this, 'item', ''); | ||
const currentItems = get(this, 'items') || []; | ||
const items = new Set(currentItems).add(value); | ||
if (items.size > currentItems.length) { | ||
set(this, 'items', [...items]); | ||
this.onchange(e); | ||
} | ||
} | ||
}, | ||
onkeydown: function(e) { | ||
switch (e.keyCode) { | ||
case 8: | ||
if (e.target.value == '' && this.items.length > 0) { | ||
this.remove(this.items.length - 1); | ||
} | ||
break; | ||
} | ||
}, | ||
oninput: function(e) { | ||
set(this, 'item', e.target.value); | ||
}, | ||
onchange: function(e) { | ||
let searchable = get(this, 'searchable'); | ||
if (!Array.isArray(searchable)) { | ||
searchable = [searchable]; | ||
} | ||
searchable.forEach(item => { | ||
item.search(get(this, 'items')); | ||
}); | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,34 @@ | ||
import { get } from '@ember/object'; | ||
import ucfirst from 'consul-ui/utils/ucfirst'; | ||
const find = function(obj, term) { | ||
if (Array.isArray(obj)) { | ||
return obj.some(function(item) { | ||
return find(item, term); | ||
}); | ||
} | ||
return obj.toLowerCase().indexOf(term) !== -1; | ||
}; | ||
export default function(filterable) { | ||
return filterable(function(item, { s = '' }) { | ||
const term = s.toLowerCase(); | ||
return ( | ||
get(item, 'Name') | ||
.toLowerCase() | ||
.indexOf(term) !== -1 || | ||
(get(item, 'Tags') || []).some(function(item) { | ||
return item.toLowerCase().indexOf(term) !== -1; | ||
}) | ||
); | ||
let status; | ||
switch (true) { | ||
case term.indexOf('service:') === 0: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For what it's worth, Ember has had There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yeah, nice, thankyou! Will sort in a sec. |
||
return find(get(item, 'Name'), term.substr(8)); | ||
case term.indexOf('tag:') === 0: | ||
return find(get(item, 'Tags') || [], term.substr(4)); | ||
case term.indexOf('status:') === 0: | ||
status = term.substr(7); | ||
switch (term.substr(7)) { | ||
case 'warning': | ||
case 'critical': | ||
case 'passing': | ||
return get(item, `Checks${ucfirst(status)}`) > 0; | ||
default: | ||
return false; | ||
} | ||
default: | ||
return find(get(item, 'Name'), term) || find(get(item, 'Tags') || [], term); | ||
} | ||
}); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
%with-icon { | ||
background-repeat: no-repeat; | ||
background-position: center; | ||
} | ||
%as-pseudo { | ||
display: inline-block; | ||
content: ''; | ||
visibility: visible; | ||
background-size: contain; | ||
} | ||
%with-cancel-plain-icon { | ||
@extend %with-icon; | ||
background-image: $cancel-plain-svg; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
@import './base-variables'; | ||
@import './base-placeholders'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
@import './phrase-editor/index'; | ||
.phrase-editor { | ||
@extend %phrase-editor; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
@import './skin'; | ||
@import './layout'; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
%phrase-editor { | ||
display: flex; | ||
margin-top: 14px; | ||
margin-bottom: 5px; | ||
} | ||
%phrase-editor ul { | ||
overflow: hidden; | ||
} | ||
%phrase-editor li { | ||
@extend %pill; | ||
float: left; | ||
margin-right: 4px; | ||
} | ||
%phrase-editor span { | ||
display: none; | ||
} | ||
%phrase-editor label { | ||
flex-grow: 1; | ||
} | ||
%phrase-editor input { | ||
width: 100%; | ||
height: 33px; | ||
padding: 8px 10px; | ||
box-sizing: border-box; | ||
} | ||
@media #{$--horizontal-selects} { | ||
%phrase-editor { | ||
margin-top: 14px; | ||
} | ||
%phrase-editor ul { | ||
padding-top: 5px; | ||
padding-left: 5px; | ||
} | ||
%phrase-editor input { | ||
padding-left: 3px; | ||
} | ||
} | ||
@media #{$--lt-horizontal-selects} { | ||
%phrase-editor { | ||
margin-top: 9px; | ||
} | ||
%phrase-editor label { | ||
display: block; | ||
margin-top: 5px; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
@media #{$--horizontal-selects} { | ||
%phrase-editor { | ||
border: 1px solid $gray-300; | ||
border-radius: 2px; | ||
} | ||
%phrase-editor input:focus { | ||
outline: 0; | ||
} | ||
} | ||
@media #{$--lt-horizontal-selects} { | ||
%phrase-editor label { | ||
border: 1px solid $gray-300; | ||
border-radius: 2px; | ||
} | ||
} | ||
%phrase-editor input { | ||
-webkit-appearance: none; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,7 @@ | |
display: inline-block; | ||
padding: 1px 5px; | ||
} | ||
%pill button { | ||
padding: 0; | ||
margin-right: 3px; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<ul> | ||
{{#each items as |item index|}} | ||
<li> | ||
<button type="button" onclick={{action remove index}}>Remove</button>{{item}} | ||
</li> | ||
{{/each}} | ||
</ul> | ||
<label class="type-search"> | ||
<span>Search</span> | ||
<input onchange={{action add}} onsearch={{action add}} oninput={{action oninput}} onkeydown={{action onkeydown}} placeholder="{{placeholder}}" value="{{item}}" type="search" name="s" autofocus="autofocus" /> | ||
</label> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { moduleForComponent, test } from 'ember-qunit'; | ||
import hbs from 'htmlbars-inline-precompile'; | ||
|
||
moduleForComponent('phrase-editor', 'Integration | Component | phrase editor', { | ||
integration: true, | ||
}); | ||
|
||
test('it renders', function(assert) { | ||
// Set any properties with this.set('myProperty', 'value'); | ||
// Handle any actions with this.on('myAction', function(val) { ... }); | ||
|
||
this.render(hbs`{{phrase-editor}}`); | ||
|
||
assert.equal( | ||
this.$() | ||
.text() | ||
.trim(), | ||
'Search' | ||
); | ||
|
||
// Template block usage: | ||
this.render(hbs` | ||
{{#phrase-editor}} | ||
template block text | ||
{{/phrase-editor}} | ||
`); | ||
|
||
assert.equal( | ||
this.$() | ||
.text() | ||
.trim(), | ||
'Search' | ||
); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would normally expect this component to accept an action as an argument and call it rather than requiring a searchable as an interface. It limits its reusability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep agree, this will eventually move up into a wrapping component when we compose more functionality here and wrap it up in a final reusable component.