diff --git a/document/common/config/menu.json b/document/common/config/menu.json index 64ef44d51248f0ed256005864b1f3c39e018aa2b..6634e21fef28c552d940952f0e07f0e68cc47ebe 100644 --- a/document/common/config/menu.json +++ b/document/common/config/menu.json @@ -21,6 +21,7 @@ "checkbox-group": "CheckboxGroup", "radio": "Radio", "input": "Input", + "select": "Select", "switch": "Switch", "validator": "Validator", "loading": "Loading", @@ -86,6 +87,7 @@ "checkbox-group": "CheckboxGroup", "radio": "Radio", "input": "Input", + "select": "Select", "switch": "Switch", "validator": "Validator", "loading": "Loading", diff --git a/document/components/docs/en-US/select.md b/document/components/docs/en-US/select.md new file mode 100644 index 0000000000000000000000000000000000000000..428889f739c19e992eae508e2411e340916a0942 --- /dev/null +++ b/document/components/docs/en-US/select.md @@ -0,0 +1,93 @@ +## Select + +Select component. + +### Example + +- Basic usage + + Basically, you need to use `options` to define each option and the selected value will bind on `v-model`. + + ```html + + + ``` + ```js + export default { + data() { + return { + options: [2013, 2014, 2015, 2016, 2017, 2018], + value: 2016 + } + } + } + ``` + +- Configs and Events + + Select supports the configs of picker title, placeholder, whether auto pop, disabled. And if the selected value is changed when selected, it will emit the event `change`. + + ```html + + + ``` + ```js + export default { + data() { + return { + options: [2013, 2014, 2015, 2016, 2017, 2018], + value: 2016, + title: 'Entry time', + placeholder: 'Please choose entry time', + autoPop: false, + disabled: false + } + }, + methods: { + change(value, index, text) { + console.log('change', value, index, text) + } + } + } + ``` + + There is one thing you may need notice. the `change` event won't change when you directly set the value of `v-model`, it only emit when the change is caused by select. If you want to listen the change of `v-model`, just watch it. + +### Props + +| Attribute | Description | Type | Accepted Values | Default | +| - | - | - | - | - | +| options | options | Array | - | [] | +| v-model | the selected value | Any | - | - | +| placeholder | placeholder | String | - | '请选择' | +| auto-pop | whether auto pop picker | Boolean | true/false | false | +| disabled | whether disabled | Boolean | true/false | false | +| title | the title of picker | String | - | '请选择' | +| cancelTxt | the cancel text of picker | String | - | '取消' | +| confirmTxt | the confirm text of picker | String | - | '确认' | + +- `options ` sub configuration + +| Attribute | Description | Type | Accepted Values | Example | +| - | - | - | - | - | +| value | value of the option | Any | - | - | +| text | text of the option | String | - | - | + +If an option is not an object, such as 2014,we will transform it to { value: 2014, text: 2014 }. + +### Events + +| Event Name | Description | Parameters 1 | Parameters 2 | Parameters 3 | +| - | - | - | - | - | +| change | when the selected value changed by select | the selected value | the selected index | the selected text | diff --git a/document/components/docs/zh-CN/select.md b/document/components/docs/zh-CN/select.md new file mode 100644 index 0000000000000000000000000000000000000000..ccf815e232a9403436a088db78fa71980416a258 --- /dev/null +++ b/document/components/docs/zh-CN/select.md @@ -0,0 +1,91 @@ +## Select + +Select 组件,用于单项选择。 + +### 示例 + +- 基本用法 + + 对于 Select 选择组件,你需要传入 options 定义各个选项,选择的结果则绑定在 v-model 上。 + + ```html + + + ``` + ```js + export default { + data() { + return { + options: [2013, 2014, 2015, 2016, 2017, 2018], + value: 2016 + } + } + } + ``` + +- 配置和事件 + + Select 支持选择器标题(title)、占位符(placeholder)、自动弹出选择器(autoPop)、禁用(disabled)的配置。并且在选择时,如果选择的值改变了,会派发 change 事件。 + + ```html + + + ``` + ```js + export default { + data() { + return { + options: [2013, 2014, 2015, 2016, 2017, 2018], + value: 2016, + title: '入职时间', + placeholder: '请选择入职时间', + autoPop: false, + disabled: false + } + }, + methods: { + change(value, index, text) { + console.log('change', value, index, text) + } + } + } + ``` + + 需要注意的一点是,change 事件在你直接赋值修改 value 的值时,不会触发,只会在选择导致的修改时触发。如果你只是想监听 value 的改变,请直接监听 value。 + +### Props 配置 + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| - | - | - | - | - | +| options | 选项 | Array | - | [] | +| v-model | 选中的值 | Any | - | - | +| placeholder | 占位文案 | String | - | '请选择' | +| auto-pop | 是否自动弹出选择器 | Boolean | true/false | false | +| disabled | 是否禁用 | Boolean | true/false | false | +| title | 选择器的标题 | String | - | '请选择' | +| cancelTxt | 选择器的取消按钮文案 | String | - | '取消' | +| confirmTxt | 选择器的确认按钮文案 | String | - | '确认' | + +- `options ` 子配置项 + +| 参数 | 说明 | 类型 | 可选值 | 示例 | +| - | - | - | - | - | +| value | 该选项的值 | Any | - | - | +| text | 该选项的文案 | String | - | - | + +你可以将每个选项定义成一个对象,其中 text 为选项文案,value为选项的值,若没有将该选项定义为对象,比如 2014,则我们内部会把它转化成 { value: 2014, text: 2014 } + +### 事件 + +| 事件名 | 说明 | 参数1 | 参数2 | 参数3 | +| - | - | - | - | - | +| change | 在选择时,如果选择的值改变了派发 | 选中项的值 | 选中项的索引 | 选中项的文案 | diff --git a/example/App.vue b/example/App.vue index a87b45d1fe62fb0b2fb2cf28e4ec7be0c03e927b..6d3fc07ee6f3cb385b0dca74c2a7806f40339740 100644 --- a/example/App.vue +++ b/example/App.vue @@ -87,6 +87,10 @@ path: '/time-picker', text: 'TimePicker' }, + { + path: '/select', + text: 'Select' + }, { path: '/dialog', text: 'Dialog' diff --git a/example/pages/select.vue b/example/pages/select.vue new file mode 100644 index 0000000000000000000000000000000000000000..d236e6cb174346daf7b4e64380b65ca26ecd4f05 --- /dev/null +++ b/example/pages/select.vue @@ -0,0 +1,50 @@ + + + diff --git a/example/router/routes.js b/example/router/routes.js index ee46efdb0f80e525453c115949a6f747097eda8d..4faf374db845e1e7041ffe4d3677fa1194dd1a60 100644 --- a/example/router/routes.js +++ b/example/router/routes.js @@ -13,6 +13,7 @@ import Rate from '../pages/rate.vue' import Picker from '../pages/picker.vue' import CascadePicker from '../pages/cascade-picker.vue' import TimePicker from '../pages/time-picker.vue' +import Select from '../pages/select.vue' import Dialog from '../pages/dialog.vue' import ActionSheet from '../pages/action-sheet.vue' import Scroll from '../pages/scroll.vue' @@ -84,6 +85,10 @@ const routes = [ path: '/time-picker', component: TimePicker }, + { + path: '/select', + component: Select + }, { path: '/dialog', component: Dialog diff --git a/package.json b/package.json index ea53411e3d14058b3fbfaa33af3f9aeb1a359333..2f9da23cdaa75a5c218bd479aa2da9368886d7df 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "opn": "^4.0.2", "optimize-css-assets-webpack-plugin": "^1.3.0", "ora": "^1.2.0", + "phantomjs-polyfill-find-index": "^1.0.1", "phantomjs-prebuilt": "^2.1.14", "postcss-loader": "^2.0.5", "rimraf": "^2.6.0", diff --git a/src/common/icon/cube-icon.styl b/src/common/icon/cube-icon.styl index 3f3f8061d2c2f3699ff078546691698bbaa514db..eab23862d80ff99760db2a029186378476d230ad 100644 --- a/src/common/icon/cube-icon.styl +++ b/src/common/icon/cube-icon.styl @@ -42,3 +42,5 @@ content: "\e990" .cubeic-square-right::before content: "\e67d" +.cubeic-select::before + content: "\e609" diff --git a/src/common/icon/cubeic.ttf b/src/common/icon/cubeic.ttf index 42a189bd7f64494bcb1568e4f6042478bae479e3..8eabb6980037e007be17ac9eb1c10a99df46dc56 100644 Binary files a/src/common/icon/cubeic.ttf and b/src/common/icon/cubeic.ttf differ diff --git a/src/common/icon/cubeic.woff b/src/common/icon/cubeic.woff index 921e279844cae96670ce97c63db8ee561e71bbe0..e0ca18edfa6a719c10e2894db157517787909bd3 100644 Binary files a/src/common/icon/cubeic.woff and b/src/common/icon/cubeic.woff differ diff --git a/src/common/stylus/theme/default.styl b/src/common/stylus/theme/default.styl index 23f46bed2d07c4a5497af1d998379c8134016c97..178bbb060401bbe83407d2b23438af09fa31003f 100644 --- a/src/common/stylus/theme/default.styl +++ b/src/common/stylus/theme/default.styl @@ -168,3 +168,14 @@ $textarea-indicator-color := $color-light-grey-s // validator $validator-msg-def-color := #f5222d + +// select +$select-color := $color-dark-grey +$select-bgc := $color-white +$select-disabled-color := #b8b8b8 +$select-disabled-bgc := $color-light-grey-opacity +$select-border-color := $color-light-grey-s +$select-border-active-color := $color-orange +$select-icon-color := $color-light-grey-s +$select-icon-active-color := $color-orange +$select-placeholder-color := $color-light-grey diff --git a/src/components/select/select.vue b/src/components/select/select.vue new file mode 100644 index 0000000000000000000000000000000000000000..139c09f6d846bc5bd6e206a965bf2a1721c26aea --- /dev/null +++ b/src/components/select/select.vue @@ -0,0 +1,145 @@ + + + + diff --git a/src/index.js b/src/index.js index 7d72bc7d87a2f384c21036aa5350c088ffb57e68..ba180d30ee65b92668a1846c9a284cb5b59074ae 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,7 @@ import { Popup, TimePicker, CascadePicker, + Select, Dialog, Tip, Toast, @@ -33,6 +34,7 @@ function install(Vue) { Button, TimePicker, CascadePicker, + Select, Dialog, Tip, Toast, diff --git a/src/module.js b/src/module.js index cd80f562a986fdc7342d405cbe49905d6662e3eb..7f8d60dc892bd13e5da69473d91fa7028346a6b4 100644 --- a/src/module.js +++ b/src/module.js @@ -9,6 +9,7 @@ import Dialog from './modules/dialog' import Toast from './modules/toast' import Input from './modules/input' import Validator from './modules/validator' +import Select from './modules/select' import Textarea from './modules/textarea' import Rate from './modules/rate' @@ -39,6 +40,7 @@ export { Picker, TimePicker, CascadePicker, + Select, Dialog, Tip, Toast, diff --git a/src/modules/select/index.js b/src/modules/select/index.js new file mode 100644 index 0000000000000000000000000000000000000000..fe0a9cd34cbd0c1d194161375dce314c54e4c173 --- /dev/null +++ b/src/modules/select/index.js @@ -0,0 +1,13 @@ +import Picker from '../../components/picker/picker.vue' +import Select from '../../components/select/select.vue' +import addPicker from '../picker/api' + +Select.install = function (Vue) { + Vue.component(Picker.name, Picker) + Vue.component(Select.name, Select) + addPicker(Vue, Picker) +} + +Select.Picker = Picker + +export default Select diff --git a/test/unit/index.js b/test/unit/index.js index ee0f7ca0f82c4db29d5a6168c3f5b56c06ac07ac..92b6593199b280f3d54ba749c2885788ab95e8ba 100644 --- a/test/unit/index.js +++ b/test/unit/index.js @@ -1,5 +1,6 @@ import Vue from 'vue' import 'basic-mouse-event-polyfill-phantomjs' +import 'phantomjs-polyfill-find-index' Vue.config.productionTip = false diff --git a/test/unit/karma.conf.js b/test/unit/karma.conf.js index d93b4ac541f80c5d3bfed66352efc196bc0596dd..a3440b80515f8034098ce734267a6a76d0d2083c 100644 --- a/test/unit/karma.conf.js +++ b/test/unit/karma.conf.js @@ -25,7 +25,9 @@ module.exports = function (config) { }, frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'], reporters: ['spec', 'coverage'], - files: ['./index.js'], + files: [ + './index.js' + ], preprocessors: { './index.js': ['webpack', 'sourcemap'] }, diff --git a/test/unit/specs/select.spec.js b/test/unit/specs/select.spec.js new file mode 100644 index 0000000000000000000000000000000000000000..f6624e4011a80bfc85f0966bd29d25500e779e97 --- /dev/null +++ b/test/unit/specs/select.spec.js @@ -0,0 +1,61 @@ +import Vue from 'vue2' +import Select from '@/modules/select' +import instantiateComponent from '@/common/helpers/instantiate-component' + +describe('Select.vue', () => { + let vm + afterEach(() => { + if (vm) { + vm.$parent.destroy() + vm = null + } + }) + + it('use', () => { + Vue.use(Select) + expect(Vue.component(Select.name)) + .to.be.a('function') + }) + + it('should render correct contents', () => { + vm = createSelect({ + value: 2016, + options: [2013, 2014, 2015, 2016, 2017, 2018] + }) + const el = vm.$el + expect(el.querySelector('.cube-select-text').textContent.trim()) + .to.equal('2016') + }) + + it('should trigger events', function (done) { + this.timeout(10000) + + const changeHandle = sinon.spy() + vm = createSelect({ + value: 2016, + options: [2013, 2014, 2015, 2016, 2017, 2018] + }, { + change: changeHandle + }) + const el = vm.$el + el.click() + + setTimeout(() => { + vm.picker.scrollTo(0, 1) + setTimeout(() => { + const confirmBtn = document.querySelector('.cube-picker-choose [data-action="confirm"]') + confirmBtn.click() + expect(changeHandle) + .to.be.callCount(1) + done() + }, 1000) + }, 100) + }) +}) + +function createSelect (props = {}, events = {}) { + return instantiateComponent(Vue, Select, { + props: props, + on: events + }) +}