Skip to content

Instantly share code, notes, and snippets.

@gtb104
Last active April 19, 2018 17:23
Show Gist options
  • Save gtb104/a760184e14b36ae8f1059d0e3c33c138 to your computer and use it in GitHub Desktop.
Save gtb104/a760184e14b36ae8f1059d0e3c33c138 to your computer and use it in GitHub Desktop.
EPST Bug
import Ember from 'ember';
const {
Component,
computed
} = Ember;
export default Component.extend({
classNameBindings: ['isActive', ':pill-meta'],
isActive: false,
options: [
{ displayName: "A", flags: -2147482621, format: "Text", formattedName: "a(A)", metaName: "a" },
{ displayName: "B", flags: -2147482621, format: "Text", formattedName: "b(B)", metaName: "a" },
{ displayName: "C", flags: -2147482621, format: "Text", formattedName: "c(C)", metaName: "a" },
{ displayName: "D", flags: -2147482621, format: "Text", formattedName: "d(D)", metaName: "a" },
],
selection: null,
sendMessage: () => {},
actions: {
onChange(selection /* powerSelectAPI, event */) {
this._broadcast('META_SELECTED', selection);
},
onFocus(powerSelectAPI /* event */) {
if (powerSelectAPI.lastSearchedText) {
powerSelectAPI.actions.search('');
}
powerSelectAPI.actions.open();
}
},
_broadcast(type, data) {
this.get('sendMessage')(type, data);
},
_isActive: computed('isActive', 'options', function() {
const isActive = this.get('isActive');
const options = this.get('options');
return isActive && options.length > 0;
}),
_matcher: (m, input) => {
const _input = input.toLowerCase();
const _metaName = m.metaName.toLowerCase();
const _displayName = m.displayName.toLowerCase();
return _metaName.indexOf(_input) & _displayName.indexOf(_input);
}
});
import Ember from 'ember';
const {
Component,
computed,
isEmpty,
run: { scheduleOnce }
} = Ember;
const makeOperatorExpensive = (obj) => ({ ...obj, isExpensive: true });
const eq = { displayName: '=', isExpensive: false, hasValue: true };
const notEq = { displayName: '!=', isExpensive: false, hasValue: true };
const exists = { displayName: 'exists', isExpensive: false, hasValue: false };
const notExists = { displayName: '!exists', isExpensive: false, hasValue: false };
const begins = { displayName: 'begins', isExpensive: false, hasValue: true };
const contains = { displayName: 'contains', isExpensive: true, hasValue: true };
const ends = { displayName: 'ends', isExpensive: true, hasValue: true };
const operatorsForMetaIndexedByKey = [exists, notExists, makeOperatorExpensive(eq), makeOperatorExpensive(notEq)];
const operatorsForMetaIndexedByKeyWithTextFormat = [exists, notExists, makeOperatorExpensive(eq), makeOperatorExpensive(notEq), makeOperatorExpensive(begins), ends, contains];
const operatorsForMetaIndexedByValue = [exists, notExists, eq, notEq ];
const operatorsForMetaIndexedByValueWithTextFormat = [exists, notExists, eq, notEq, begins, ends, contains];
const operatorsForSessionId = [exists, notExists, eq, notEq];
const defaultOperators = [eq, notEq, exists, notExists, contains, begins, ends];
const NONE = 'none';
const KEY = 'key';
const VALUE = 'value';
const indices = [NONE, KEY, VALUE];
export default Component.extend({
classNameBindings: ['isActive', ':pill-operator'],
isActive: false,
meta: null,
selection: null,
sendMessage: () => {},
options: computed('meta', function() {
const meta = this.get('meta');
let options = [];
if (!isEmpty(meta)) {
const { format, flags = 1, metaName } = meta;
const index = flags & '0xF' - 1;
const indexedBy = indices[index];
if (indexedBy === KEY) {
options = (format === 'Text') ?
operatorsForMetaIndexedByKeyWithTextFormat :
operatorsForMetaIndexedByKey;
} else if (indexedBy === VALUE) {
options = (format === 'Text') ?
operatorsForMetaIndexedByValueWithTextFormat :
operatorsForMetaIndexedByValue;
} else if (metaName === 'sessionid') {
options = operatorsForSessionId;
}
options = defaultOperators;
}
return options;
}),
didUpdateAttrs() {
this._super(...arguments);
if (this.get('isActive')) {
scheduleOnce('afterRender', this, '_focusOnPowerSelectTrigger');
}
},
actions: {
onChange(selection /* powerSelectAPI, event */) {
this._broadcast('OPERATOR_SELECTED', selection);
},
onFocus(powerSelectAPI /* event */) {
powerSelectAPI.actions.open();
}
},
_broadcast(type, data) {
this.get('sendMessage')(type, data);
},
_focusOnPowerSelectTrigger() {
const trigger = this.element.querySelector('.ember-power-select-trigger input');
if (trigger) {
trigger.focus();
}
},
_matcher: (o, input) => {
const _displayName = o.displayName.toLowerCase();
const _input = input.toLowerCase();
return _displayName.indexOf(_input);
}
});
import Ember from 'ember';
const { Component } = Ember;
export default Component.extend({
classNameBindings: ['isActive', ':pill-value'],
isActive: false,
initialKeyUp: true,
sendMessage: () => {},
valueString: null,
didRender() {
const input = this.element.querySelector('input');
if (input) {
input.focus();
}
},
actions: {
onKeyUp(input, event) {
if (this.get('initialKeyUp') && event.keyCode === 13) {
this.toggleProperty('initialKeyUp');
return;
}
switch (event.keyCode) {
case 13:// Enter
this._broadcast('VALUE_ENTER_KEY');
break;
case 27:// Escape
this._broadcast('VALUE_ESCAPE_KEY');
break;
case 8:// Backspace
this._broadcast('VALUE_BACKSPACE_KEY', input);
break;
case 37:// ArrowLeft
this._broadcast('VALUE_ARROW_LEFT_KEY', input);
break;
case 39:// ArrowRight
this._broadcast('VALUE_ARROW_RIGHT_KEY', input);
break;
default:
this._broadcast('VALUE_SET', input);
}
}
},
_broadcast(type, data) {
this.get('sendMessage')(type, data);
}
});
import Ember from 'ember';
const { Component } = Ember;
export default Component.extend({
classNameBindings: ['isActive', ':query-pill'],
filter: null,
isActive: false,
sendMessage: () => {},
isMetaActive: false,
isOperatorActive: false,
isValueActive: false,
selectedMeta: null,
selectedOperator: null,
valueString: null,
didInsertElement() {
this._super(...arguments);
this._broadcast('PILL_INITIALIZED');
if (this.get('isActive')) {
this.set('isMetaActive', true);
}
},
keyUp() {
return false;
},
actions: {
handleMessage(type, data) {
switch (type) {
case 'META_SELECTED':
this._metaSelected(data);
break;
case 'OPERATOR_SELECTED':
this._operatorSelected(data);
break;
case 'VALUE_SET':
this._valueSet(data);
break;
case 'VALUE_ENTER_KEY':
this._createPill(data);
break;
case 'VALUE_ESCAPE_KEY':
this._cancelPillCreation();
break;
case 'VALUE_BACKSPACE_KEY':
this._backspaceKeyPressed(data);
break;
case 'VALUE_ARROW_LEFT_KEY':
this._leftArrowKeyPressed(data);
break;
case 'VALUE_ARROW_RIGHT_KEY':
this._rightArrowKeyPressed(data);
break;
default:
this._broadcast(type, data);
}
}
},
_broadcast(type, data) {
this.get('sendMessage')(type, data);
},
_metaSelected(selectedMeta) {
this.setProperties({
selectedMeta,
isMetaActive: false,
isOperatorActive: true,
isValueActive: false
});
},
_operatorSelected(selectedOperator) {
this.setProperties({
selectedOperator,
isMetaActive: false,
isOperatorActive: false,
isValueActive: selectedOperator.hasValue,
isActive: selectedOperator.hasValue
});
},
_createPill() {
this.setProperties({
isMetaActive: false,
isOperatorActive: false,
isValueActive: false,
isActive: false
});
},
_valueSet(data) {
if (data !== undefined) {
this.set('valueString', data);
}
},
_cancelPillCreation() {
// TODO
log('_cancelPillCreation() called');
},
_backspaceKeyPressed(data) {
// TODO
log('_backspaceKeyPressed() called', data);
},
_leftArrowKeyPressed(data) {
// TODO
log('_leftArrowKeyPressed() called', data);
},
_rightArrowKeyPressed(data) {
// TODO
log('_rightArrowKeyPressed() called', data);
}
});
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
handleMessage(id, type, data) {
console.log(id, type, data);
}
}
});
body {
margin: 12px 16px;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-size: 12pt;
background-color: #ccc;
}
.code {
font-family: courier;
}
.query-container {
background-color: #fff;
width: 100%;
display: flex;
flex-flow: row wrap;
justify-content: flex-start;
max-height: 12rem;
overflow-x: hidden;
overflow-y: auto;
}
.query-pill {
border: 1px solid black;
border-radius: 0.25rem;
display: flex;
flex: 0 0 auto;
margin: 0.25rem;
padding: 0 0.25rem;
min-height: 2rem;
line-height: 2rem;
}
.query-pill.is-active {
border: none;
flex: 1 0 auto;
}
.query-pill > div.is-active {
flex-grow: 1;
}
.pill-meta, .pill-operator, .pill-value {
height: 2rem;
}
.pill-meta input, .pill-operator input, .pill-value input {
width: 100%;
}
.ember-power-select-typeahead-input, .ember-power-select-trigger {
border: none;
}
.ember-power-select-typeahead-input, .ember-power-select-trigger input {
padding: 0;
}
<p>If <i>pill-operator</i> has <i>initiallyOpened=true</i>, then the first item in the dropdown is not automatically highlighted. If <i>initiallyOpened=false</i>, then first item is selected, but integration tests for the <i>pill-operator</i> fail because the dropdown does not open.</p>
<p>To see this behavior, click in the white box below. Notice the first item is automatically highlighted. Hit ENTER. Notice the first item of the second dropdown is not automatically highlighted.</p>
<br>
<br>
<div class="query-container">
{{query-pill
isActive=true
sendMessage=(action 'handleMessage' this.elementId)
}}
</div>
<br>
<br>
<p>Here is the test that is failing</p>
<pre class='code'>
test('it shows an open Power Select if active', async function(assert) {
this.set('meta', meta);
await render(hbs`{&shy;{pill-operator isActive=true meta=meta}}`);
assert.equal(findAll(.ember-power-select-option).length, 7);
});
</pre>
<p><i>.ember-power-select-option</i> length is 0 at time of assert.</p>
{{#if _isActive}}
{{#power-select-typeahead
allowClear=false
defaultHighlighted=undefined
extra=(hash labelPath='metaName')
initiallyOpened=false
matcher=_matcher
matchTriggerWidth=false
noMatchesMessage='All Meta Filtered out'
onchange=(action 'onChange')
onfocus=(action 'onFocus')
options=options
selected=selection
as |meta|
}}
{{meta.formattedName}}
{{/power-select-typeahead}}
{{else}}
{{selection.metaName}}
{{/if}}
{{#if isActive}}
{{#power-select-typeahead
allowClear=false
defaultHighlighted=undefined
extra=(hash labelPath='displayName')
initiallyOpened=true
matcher=_matcher
matchTriggerWidth=false
noMatchesMessage='All Operators Filtered out'
onchange=(action 'onChange')
onfocus=(action 'onFocus')
options=options
selected=selection
as |operator|
}}
{{operator.displayName}}
{{/power-select-typeahead}}
{{else}}
{{selection.displayName}}
{{/if}}
{{#if isActive}}
{{input
key-up='onKeyUp'
type='text'
value=(readonly valueString)
}}
{{else}}
{{valueString}}
{{/if}}
{{pill-meta
isActive=(readonly isMetaActive)
sendMessage=(action 'handleMessage')
selection=(readonly selectedMeta)
}}
&nbsp;
{{pill-operator
isActive=(readonly isOperatorActive)
meta=(readonly selectedMeta)
selection=(readonly selectedOperator)
sendMessage=(action 'handleMessage')
}}
&nbsp;
{{pill-value
isActive=(readonly isValueActive)
sendMessage=(action 'handleMessage')
valueString=(readonly valueString)
}}
{
"version": "0.13.1",
"EmberENV": {
"FEATURES": {}
},
"options": {
"use_pods": false,
"enable-testing": false
},
"dependencies": {
"jquery": "https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.js",
"ember": "2.16.2",
"ember-template-compiler": "2.16.2",
"ember-testing": "2.16.2"
},
"addons": {
"ember-power-select": "1.10.4",
"ember-power-select-typeahead": "0.6.1"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment