diff --git a/.sass-lint.yml b/.sass-lint.yml index 174275eba..964332061 100644 --- a/.sass-lint.yml +++ b/.sass-lint.yml @@ -42,7 +42,9 @@ rules: force-pseudo-nesting: 2 # Name Formats - class-name-format: 2 + # TODO: When inline linting is supported set as error (https://github.com/sasstools/sass-lint/pull/402) and ignore + # in Select.scss + class-name-format: 1 function-name-format: 2 mixin-name-format: 2 placeholder-name-format: 2 diff --git a/package.json b/package.json index 1e7b1b1fb..36ce1a892 100644 --- a/package.json +++ b/package.json @@ -89,6 +89,7 @@ "dependencies": { "lodash": "^3.10.1", "react": "^0.14.6", - "react-dom": "^0.14.6" + "react-dom": "^0.14.6", + "react-select": "^1.0.0-beta8" } } diff --git a/src/components/Main.js b/src/components/Main.js index 56e657303..c1aa2a558 100644 --- a/src/components/Main.js +++ b/src/components/Main.js @@ -8,21 +8,50 @@ import { Tab, Radio, RadioGroup, + Select, Toggle, } from './distributionEntry'; require('styles/App.scss'); +const selectCountriesOptions = [ + { value: 'au', label: 'Australia' }, + { value: 'uk', label: 'United Kingdom' }, + { value: 'us', label: 'United States' }, + { value: 'lt', label: 'Lesotho', disabled: true }, +]; + +const selectFlavoursOptions = [ + { label: 'Chocolate', value: 'chocolate' }, + { label: 'Vanilla', value: 'vanilla' }, + { label: 'Strawberry', value: 'strawberry' }, + { label: 'Caramel', value: 'caramel' }, + { label: 'Cookies and Cream', value: 'cookiescream' }, + { label: 'Peppermint', value: 'peppermint' }, +]; + class AppComponent extends React.Component { constructor(props) { super(props); + this.setSelectedCountry = this.setSelectedCountry.bind(this); + this.setSelectedFlavours = this.setSelectedFlavours.bind(this); this.toggleSimpleModal = this.toggleSimpleModal.bind(this); this.state = { + selectedCountry: 'au', + selectedFlavours: 'vanilla', showSimpleModal: false, }; } + setSelectedCountry(newValue) { + this.setState({ selectedCountry: newValue.value }); + } + + setSelectedFlavours(newValue) { + this.setState({ selectedFlavours: newValue }); + } + toggleSimpleModal() { this.setState({ showSimpleModal: !this.state.showSimpleModal }); } @@ -107,6 +136,7 @@ class AppComponent extends React.Component { +

Checkboxes

@@ -120,6 +150,8 @@ class AppComponent extends React.Component {
+ +

Radio Buttons

@@ -149,10 +181,40 @@ class AppComponent extends React.Component {
+

Toggle

+ + +

Select

+
+ +
); } diff --git a/src/components/distributionEntry.js b/src/components/distributionEntry.js index 34fc11004..667ff5b9a 100644 --- a/src/components/distributionEntry.js +++ b/src/components/distributionEntry.js @@ -3,6 +3,7 @@ require('styles/_bootstrap-custom.scss'); require('styles/_icheck-custom.scss'); require('styles/_react-toggle-custom.scss'); +require('styles/components/Select.scss'); import Button from 'react-bootstrap/lib/Button'; import Checkbox from 'react-icheck/lib/Checkbox'; @@ -24,6 +25,8 @@ import { Slicey, } from 'alexandria-adslot'; +import Select from 'react-select'; + module.exports = { Alert, Breadcrumb, @@ -37,6 +40,7 @@ module.exports = { Radio, RadioGroup, Search, + Select, Slicey, Tab, Tabs, diff --git a/src/styles/App.scss b/src/styles/App.scss index be821ee2f..9bd537614 100644 --- a/src/styles/App.scss +++ b/src/styles/App.scss @@ -22,4 +22,8 @@ body { > .btn-panel + .btn-panel { margin-top: $etalon-spacing; } + + > .example-component-panel { + margin-bottom: $etalon-spacing / 2; + } } diff --git a/src/styles/components/Select.scss b/src/styles/components/Select.scss new file mode 100644 index 000000000..f141253c8 --- /dev/null +++ b/src/styles/components/Select.scss @@ -0,0 +1,83 @@ +@import '../variable'; + +// control options +$select-input-border-color: $color-border-lighter; +$select-input-border-radius: $border-radius-base; +$select-input-border-focus: $select-input-border-color; +$select-input-height: 30px; +$select-padding-vertical: 4px; +$select-padding-horizontal: 7px; +$select-text-color: $color-text; + +// menu options +$select-option-color: $color-text-light; +$select-option-focused-color: $select-option-color; +$select-option-focused-bg: $color-gray-white; + +// clear "x" button +$select-clear-color: $color-text-light; +$select-clear-hover-color: $color-negative; + +// arrow indicator +$select-arrow-color: $color-text-light; +$select-arrow-color-hover: $color-text; + +// multi-select item +$select-item-gutter: 4px; +$select-item-color: $color-text-inverse; +$select-item-hover-color: $color-text-light; +$select-item-bg: $color-gray-darker; +$select-item-hover-bg: $select-item-bg; +$select-item-font-size: 11px; +$select-item-padding-vertical: 0; +$select-item-padding-horizontal: 5px; +$select-item-border-radius: 0; + + +@import './node_modules/react-select/scss/default'; + + +.Select { + &:not(.is-disabled) { + .Select-control { + box-shadow: 0 1px 0 0 $color-border-light; + + &:hover, + &:focus { + box-shadow: 0 2px 0 0 $color-border-light; + } + } + + &.is-open, + &.is-focused { + &:not(.is-open) { + > .Select-control { + border-color: $color-border-lighter; + } + } + } + } + + &--multi { + $multi-select-value-height: $select-input-height - ($select-item-padding-horizontal * 2); + + .Select-value { + border: 0; + height: $multi-select-value-height; + + &-icon, + &-label { + line-height: 1; + margin-top: 3px; + } + + &-icon { + border: 0; + float: right; + font-size: $font-size-subheader; + text-align: left; + width: 13px; + } + } + } +} diff --git a/test/components/MainTest.js b/test/components/MainTest.js index 378301327..7129aac8a 100644 --- a/test/components/MainTest.js +++ b/test/components/MainTest.js @@ -3,11 +3,13 @@ import createComponent from 'helpers/shallowRenderHelper'; import Main from 'components/Main'; -import { isElementOfType } from 'react-addons-test-utils'; +import React from 'react'; +import { isElementOfType, createRenderer } from 'react-addons-test-utils'; import { Checkbox, Radio, RadioGroup, + Select, Toggle, } from '../../src/components/distributionEntry'; @@ -51,4 +53,60 @@ describe('MainComponent', () => { const toggleComponent = toggleExampleContainer.props.children; expect(isElementOfType(toggleComponent, Toggle)).to.equal(true); }); + + it('should set and change values for single select', () => { + const getRenderOutputAndCheck = ({ renderer, expectedValue }) => { + const componentRenderOutput = renderer.getRenderOutput(); + + const selectElContainer = componentRenderOutput.props.children[20]; + const selectComponent = selectElContainer.props.children; + expect(isElementOfType(selectComponent, Select)).to.equal(true); + expect(selectComponent.props.value).to.equal(expectedValue); + + return { selectComponent }; + }; + + const renderer = createRenderer(); + renderer.render(
); + + const { selectComponent } = getRenderOutputAndCheck({ + renderer, + expectedValue: 'au', + }); + + selectComponent.props.onChange({ value: 'uk' }); + + getRenderOutputAndCheck({ + renderer, + expectedValue: 'uk', + }); + }); + + it('should set and change values for multi select', () => { + const getRenderOutputAndCheck = ({ renderer, expectedValue }) => { + const componentRenderOutput = renderer.getRenderOutput(); + + const selectElContainer = componentRenderOutput.props.children[21]; + const selectComponent = selectElContainer.props.children; + expect(isElementOfType(selectComponent, Select)).to.equal(true); + expect(selectComponent.props.value).to.eql(expectedValue); + + return { selectComponent }; + }; + + const renderer = createRenderer(); + renderer.render(
); + + const { selectComponent } = getRenderOutputAndCheck({ + renderer, + expectedValue: 'vanilla', + }); + + selectComponent.props.onChange('vanilla,chocolate'); + + getRenderOutputAndCheck({ + renderer, + expectedValue: 'vanilla,chocolate', + }); + }); });