Skip to content

Commit e9c1cfe

Browse files
adamraiderAdam Raider
authored andcommitted
feat(select): add javascript (#41)
* feat(select): add javascripts * delete comment * delete file * cleans up code, adds specs * tweak animation
1 parent 546a8af commit e9c1cfe

File tree

12 files changed

+1456
-58
lines changed

12 files changed

+1456
-58
lines changed

packages/ray/babel.config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module.exports = {
2+
presets: [
3+
[
4+
'@babel/preset-env',
5+
{
6+
targets: {
7+
node: 'current'
8+
}
9+
}
10+
]
11+
],
12+
plugins: ['@babel/plugin-proposal-class-properties']
13+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## JavaScript
2+
3+
```javascript
4+
import { Select } from '@wework/ray';
5+
6+
Select.createAll();
7+
// or
8+
Select.create(document.querySelector('.ray-select'));
9+
```
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
export const CSS_CLASSES = {
2+
ACTIVE: 'ray-select--active',
3+
HAS_VALUE: 'ray-select--has-value',
4+
PLACEHOLDER_MODE: 'ray-select--placeholder-mode',
5+
EL__INPUT: 'ray-select__input'
6+
};
7+
8+
export const STRINGS = {
9+
INIT_SELECTOR: 'ray-select'
10+
};
Lines changed: 88 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,125 @@
1-
const SELECT_MODIFIER_ACTIVE = 'ray-select--active';
2-
const SELECT_EL_INPUT = 'ray-select__input';
3-
const SELECT_MODIFIER_HAS_VALUE = 'ray-select--has-value';
4-
const SELECT_MODIFIER_PLACEHOLDER_MODE = 'ray-select--placeholder-mode';
1+
import { CSS_CLASSES, STRINGS } from './constants';
2+
import { validateNodeType, isTargetingItself } from './util';
53

64
class Select {
7-
constructor(element, options) {
8-
this.element = element;
9-
this.inputElement = element.querySelector(`.${SELECT_EL_INPUT}`);
10-
this.bindEventListeners();
5+
static instances = new WeakMap();
116

12-
const option = this.getCurrentValueOptionElement();
7+
static get cssClasses() {
8+
return CSS_CLASSES;
9+
}
10+
11+
static get strings() {
12+
return STRINGS;
13+
}
14+
15+
static create(element, options) {
16+
return this.instances.get(element) || new this(element, options);
17+
}
18+
19+
static createAll(target = document, _options = {}) {
20+
// Finds all instances of select on the document or within a given element and instantiates them.
21+
const options = {
22+
initSelector: this.strings.INIT_SELECTOR,
23+
..._options
24+
};
25+
26+
validateNodeType(target);
27+
28+
if (isTargetingItself(target, options)) {
29+
this.create(target, options);
30+
} else {
31+
const selects = [...target.querySelectorAll(options.initSelector)];
32+
return selects.forEach(select => this.create(select, options));
33+
}
34+
}
35+
36+
constructor(root, options) {
37+
this._root = root;
38+
this._inputElement = this._root.querySelector(
39+
`.${this.constructor.cssClasses.EL__INPUT}`
40+
);
41+
42+
if (!this._inputElement) {
43+
throw new Error(
44+
`Select must have an input element with a class of .${
45+
this.constructor.cssClasses.EL__INPUT
46+
}`
47+
);
48+
}
49+
50+
this._bindEventListeners();
51+
52+
const option = this._getCurrentValueOptionElement();
1353

1454
if (option && option.innerHTML) {
1555
if (option.dataset.rayPlaceholder) {
16-
this.element.classList.add(SELECT_MODIFIER_PLACEHOLDER_MODE);
56+
this._root.classList.add(this.constructor.cssClasses.PLACEHOLDER_MODE);
1757
} else {
18-
this.element.classList.add(SELECT_MODIFIER_HAS_VALUE);
58+
this._root.classList.add(this.constructor.cssClasses.HAS_VALUE);
1959
}
2060
}
61+
62+
this.constructor.instances.set(this._root, this);
2163
}
2264

23-
bindEventListeners() {
24-
this.inputElement.addEventListener('focus', this.onFocus);
25-
this.inputElement.addEventListener('blur', this.onBlur);
26-
this.inputElement.addEventListener('change', this.onChange);
65+
_bindEventListeners() {
66+
this._inputElement.addEventListener('focus', this.onFocus);
67+
this._inputElement.addEventListener('blur', this.onBlur);
68+
this._inputElement.addEventListener('change', this.onChange);
2769
}
2870

2971
value() {
30-
return this.element.value;
72+
// Current value of the Select
73+
return this._inputElement.value;
74+
}
75+
76+
set(value) {
77+
this._inputElement.value = value;
78+
this._inputElement.dispatchEvent(new Event('change'));
3179
}
3280

3381
onFocus = () => {
34-
this.element.classList.add(SELECT_MODIFIER_ACTIVE);
82+
this._root.classList.add(this.constructor.cssClasses.ACTIVE);
3583
};
3684

3785
onBlur = () => {
38-
this.element.classList.remove(SELECT_MODIFIER_ACTIVE);
86+
this._root.classList.remove(this.constructor.cssClasses.ACTIVE);
3987
};
4088

4189
onChange = () => {
42-
const option = this.getCurrentValueOptionElement();
90+
const option = this._getCurrentValueOptionElement();
4391

4492
if (option) {
4593
if (option.dataset.rayPlaceholder) {
46-
this.element.classList.add(SELECT_MODIFIER_PLACEHOLDER_MODE);
47-
this.element.classList.remove(SELECT_MODIFIER_HAS_VALUE);
94+
this._root.classList.add(this.constructor.cssClasses.PLACEHOLDER_MODE);
95+
this._root.classList.remove(this.constructor.cssClasses.HAS_VALUE);
4896
} else {
49-
this.element.classList.add(SELECT_MODIFIER_HAS_VALUE);
50-
this.element.classList.remove(SELECT_MODIFIER_PLACEHOLDER_MODE);
97+
this._root.classList.add(this.constructor.cssClasses.HAS_VALUE);
98+
this._root.classList.remove(
99+
this.constructor.cssClasses.PLACEHOLDER_MODE
100+
);
51101
}
52102
} else {
53-
this.element.classList.remove(
54-
SELECT_MODIFIER_PLACEHOLDER_MODE,
55-
SELECT_MODIFIER_HAS_VALUE
103+
this._root.classList.remove(
104+
this.constructor.cssClasses.PLACEHOLDER_MODE,
105+
this.constructor.cssClasses.HAS_VALUE
56106
);
57107
}
58108
};
59109

60-
getCurrentValueOptionElement = () => {
61-
return this.inputElement.options[this.inputElement.selectedIndex];
110+
_getCurrentValueOptionElement = () => {
111+
return this._inputElement.options[this._inputElement.selectedIndex];
62112
};
63113

64-
teardownEventListeners() {}
114+
destroy() {
115+
// Implement this method to release any resources / deregister any listeners they have
116+
// attached. An example of this might be deregistering a resize event from the window object.
117+
this._inputElement.removeEventListener('focus', this.onFocus);
118+
this._inputElement.removeEventListener('blur', this.onBlur);
119+
this._inputElement.removeEventListener('change', this.onChange);
120+
121+
this.constructor.instances.delete(this._root);
122+
}
65123
}
66124

67-
export { Select };
125+
export default Select;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<div class="ray-select">
2+
<select class="ray-select__input">
3+
<option value="" disabled selected data-ray-placeholder />
4+
<option value="Pikatchu">Pikatchu</option>
5+
<option value="Squirtle">Squirtle</option>
6+
<option value="Squirtle">Charmander</option>
7+
</select>
8+
9+
<label class="ray-select__label">
10+
What's your favorite Pokémon?
11+
</label>
12+
</div>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
function validateNodeType(target) {
2+
if (
3+
target.nodeType !== Node.ELEMENT_NODE &&
4+
target.nodeType !== Node.DOCUMENT_NODE
5+
) {
6+
throw new TypeError(
7+
'DOM document or DOM element should be given to search for and initialize this widget.'
8+
);
9+
}
10+
}
11+
12+
function isTargetingItself(target, options) {
13+
return (
14+
target.nodeType === Node.ELEMENT_NODE &&
15+
target.matches(options.selectorInit)
16+
);
17+
}
18+
19+
export { validateNodeType, isTargetingItself };
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default {
2+
prefix: 'ray-'
3+
};

packages/ray/lib/global/mixins/_form-items.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
padding-right: $ray-field-h-spacing;
1414
padding-top: $ray-field-v-spacing;
1515
color: $ray-color-text-medium;
16-
transition: padding 0.2s ease;
16+
transition: padding 0.2s ease, top 0.2s ease, left 0.2s ease;
1717
height: 100%;
1818
width: 100%;
1919
max-width: calc(100% - 12px);

packages/ray/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"author": "Adam Raider <adam.raider@wework.com>",
66
"scripts": {
77
"commit": "git-cz",
8-
"test": "echo \"Error: no test specified\"",
8+
"test": "jest",
99
"storybook": "start-storybook -s ./stories/static -p 6006",
1010
"build-storybook": "NODE_ENV=production build-storybook -c .storybook -o .out -s ./stories/static",
1111
"build": "yarn prebuild && gulp html:source sass:source sass:compiled",
@@ -19,6 +19,7 @@
1919
"dependencies": {},
2020
"devDependencies": {
2121
"@babel/core": "7.2.2",
22+
"@babel/plugin-proposal-class-properties": "7.3.0",
2223
"@commitlint/cli": "7.5.2",
2324
"@commitlint/config-conventional": "7.5.0",
2425
"@storybook/addon-backgrounds": "4.1.11",
@@ -39,9 +40,11 @@
3940
"gulp-uglify": "3.0.1",
4041
"gulp-util": "3.0.8",
4142
"husky": "1.3.1",
43+
"jest": "24.1.0",
4244
"lint-staged": "8.1.3",
4345
"lodash": "4.17.11",
4446
"mini-css-extract-plugin": "0.4.4",
47+
"nanohtml": "1.4.0",
4548
"node-sass": "4.11.0",
4649
"optimize-css-assets-webpack-plugin": "5.0.1",
4750
"postcss-loader": "3.0.0",

packages/ray/stories/select.stories.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import React from 'react';
22
import { storiesOf } from '@storybook/react';
33

44
import withPadding from './util/withPadding';
5-
import { Select } from '../lib/components/select';
5+
import Select from '../lib/components/select';
66

77
let idNumber = 0;
88

99
function init() {
10-
const selects = document.querySelectorAll('.ray-select');
11-
selects.forEach(select => new Select(select));
10+
Select.createAll(document, {
11+
initSelector: '.ray-select'
12+
});
1213
}
1314

1415
storiesOf('Select', module)

0 commit comments

Comments
 (0)