From 563b54c20e71fea8ea8748bdbf3e718a87bfe916 Mon Sep 17 00:00:00 2001 From: Amy Date: Wed, 7 Feb 2018 22:33:37 +0800 Subject: [PATCH] Support Select (#101) * (select) * (select): test * (select): doc * (select): auto-pop, change --- document/common/config/menu.json | 2 + document/components/docs/en-US/select.md | 93 +++++++++++++++ document/components/docs/zh-CN/select.md | 91 ++++++++++++++ example/App.vue | 4 + example/pages/select.vue | 50 ++++++++ example/router/routes.js | 5 + package.json | 1 + src/common/icon/cube-icon.styl | 2 + src/common/icon/cubeic.ttf | Bin 3796 -> 3944 bytes src/common/icon/cubeic.woff | Bin 2404 -> 2492 bytes src/common/stylus/theme/default.styl | 11 ++ src/components/select/select.vue | 145 +++++++++++++++++++++++ src/index.js | 2 + src/module.js | 2 + src/modules/select/index.js | 13 ++ test/unit/index.js | 1 + test/unit/karma.conf.js | 4 +- test/unit/specs/select.spec.js | 61 ++++++++++ 18 files changed, 486 insertions(+), 1 deletion(-) create mode 100644 document/components/docs/en-US/select.md create mode 100644 document/components/docs/zh-CN/select.md create mode 100644 example/pages/select.vue create mode 100644 src/components/select/select.vue create mode 100644 src/modules/select/index.js create mode 100644 test/unit/specs/select.spec.js diff --git a/document/common/config/menu.json b/document/common/config/menu.json index 64ef44d5..6634e21f 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 00000000..428889f7 --- /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 00000000..ccf815e2 --- /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 a87b45d1..6d3fc07e 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 00000000..d236e6cb --- /dev/null +++ b/example/pages/select.vue @@ -0,0 +1,50 @@ + + + diff --git a/example/router/routes.js b/example/router/routes.js index ee46efdb..4faf374d 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 ea53411e..2f9da23c 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 3f3f8061..eab23862 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 GIT binary patch delta 772 zcmY+BO=uHA7>3{3U3WL;r!j5PC~c@|ObfQfs#K~05k$pAvqO$E)t zqkomX33@0VyeMK1UW%vEQ=ykoRHTT9p#cwG1W{;wcc&uG@a%l^?l<4hOrv`vHMg(c zSY3MyXkFyxcxm?P#OdxVV0;6tT(RJcMK8sF1NcHliWIDM-KD)qGR1PWcCtweqQTFp z(q!I=4Mlr+eu?{R*{My5-MGyEN$z{DJLN)Z_2E1a>Sw;$smV&X`o90&PayJw4ogoK zdymE-Xp+Hv-?=MoxgQ7`6o9p78?Aq9>yrVUmBNPRsT|(9n41#6-0#A0lUn=U6<-6E zq5IYV*X?v%fq|#=-i4^I)MUWDpq)L^1vS(7J2QSz{eT8&px-f|W^I!OV~W1*1X+6^suVRxlG}hk`jF6AEUB>{PH0NZzW4 z*OT0-sIxf8V+xiDIip~~karZ`mC1VwHUW8G!FC`YDA*X9a|*v1Uwo3IBT1=mqwe={}J?cxZTJf2T-vSffIu zge9mBQx#N4g?7MB*qg6y+m74fyiF`&Z@%Krwz!W6mbX^R@>Ox`e}C_hTT*K-WK0H# ixrZa^q-ckzg(^30I@5*0>1*S~s#z(N3VC-TQv3reM3e{s delta 630 zcmYL_Pe@cj9LK-&=DGW7-F>cxso1huh){tiDG0^%;!UqX*i%dncw{8H^1-iH>a8Ejpfeb(PyU{ zu$su)#N^_5-`2t*V1EN*Wk2VQ)eS7f0emN2J{|EBxk9@{Zu$8_(04`)qRORza%$8| z4Z3`5@9X=#7fg#R9`m00k$vXnbD1yiHi5)C^%ke6=8}c(jm$Za(g6n_N;iAb5H!i_ z-K9^%?t|Y**mM9#tIekjv;464A! zM@F`wF7gD*2_IQQ_gm=EGkZDPqMVZPHHaZjr7O&kKrI!MNWo!7vU56zW9~# zdeDpDAFN3^2C6|N49p3YG%!2VH3M~^8Vyv0x^AF06u(HrDxjJTx+B$VD6%xDc>{}t zdTG!fq?QaU9O{jMEkM0Buo;{!8=?lNgL!PCf}f&ZJQVAqEPh${tb(;|RV8GH9MOee s{E#Ktga&kA@7(SZdpogKN2J5URGSMC)XvR3_hxf@~ diff --git a/src/common/icon/cubeic.woff b/src/common/icon/cubeic.woff index 921e279844cae96670ce97c63db8ee561e71bbe0..e0ca18edfa6a719c10e2894db157517787909bd3 100644 GIT binary patch delta 1983 zcmV;w2SE7b61)=>cTYw}00961000TR01E&B000kYkrYdRpJ&Hw-hJZnlFCTL}0WB>pMGynhqF8}}lHV|7C`)FuoVE_OJWB>pF8~^|SBnRFD zlW1*pcmMzgfB*mh6aWAKNJr@a0Bmn#VE_OJlmGw#CIA2cCK4_XAZ=lIZ~y=YyZ`_I z9{>OVAORsN09|fjZDjxe2iyPv0Zsq_0(lC? z0e^on5JjKE0=q1m1PPIVQbj3q2`+`Up`wI>nggZE5mJANQ;->tg@lxpQO0_fJY)SC z`5PDl1LNJOAbpbX_^@os`2mxhuQ=Ix#beI&YSgOJTuZIB(N;Sh&d1Be?)0ilZE>yb zOYP@Krn};$tk>&$%eBfn4jD0~rm@~FQ#ya{xaYy9nc0=mugZTO>Hk{a#x(C=>FM5eesq6( zW_tD~*+h{@;;saOf(SxP3@f6b2NS`-9t;E(6)*k}Sqw2Tkkwlbl7kQt5AonZ4+=t} z#^j(U4}mZpt9y4~XC1n$`qg{=Rn=G3HAEuh{tb4OO_DsBCbQ&Ga)=O+8&FlK4HduJ zrVTJEl40oz^J{*E*Q#x{2dt{Bn?Zlr^(>joDk$JUrxJvIoBGh}F408@OdFt3Djv?& zcjegoke>9N)6qeC4vcbbm%7x74m9`b!Bk$H$mDW`T<#qqOOl{;OjY0|%S=hBv>ct6 z)S_{vAVy?fi3(O-bF)+)D0$g*zsw_cYYXkUwt8cO0gEOt&0J~fCWTKbGb=h`3`hID41wCt0GADyb1tm#aQ_-TrJCSYYaW9*`goXq`nX zpmiQ-N0=h|QV3xPI4tnyAfkT_TnUbABlX>u<~*^&iP7@}FrQoW4l5EJvk8N8>?wsp zg)R5=L!k>7-~R?45yBOD5xpe@%f-*Sh^|Ce;(c_zhC0Ffm)>9*%p%51B1b3$N+B9_ zR8NIxR5kis^&A!EqVH8YP?UIyHpQuDVGic9^_UJwIBC+$WSo$E3b21z7@D7gdmw@2 zM|VN^(Y@%Nk15}!mq*urd%Iou z9d-Ge7h8q91JC*&9wH_m1s?%TxofA~Q=8W)@ION{UStQqtsQ^$i@C+t3iGvCE<$fh z2d1TU;>}S~v!*#^num^O6eUwA#Ilo_fcRmw#PO*qGwLdqatm)|{60Yy$ASTUMDO67 zv&a;Fp?yGhWe%2XY|Vf!#62~vJA02spUFk9C`ap5K*^v#n7bhQK<%nCrj4y-zaY^k zHN(&b7o^O%Spzp|lN@ZDV|d z&y7nr$Hs16O5}F_m^-wh-0Tk&OY|Ckfs4y47i ziLy#=y;rXT!x6U5(*S869pPdhxMAJxyTkeL7=1pOUA|Z4vB1Hl;}i>ULU!3cN6yL7 z7w1trviu|0WH>ETw93)Bi<~=X_=$6Pn`>Kr{3rXE6)?^+a!^4MG)%jKF=`56=NX!A z4oAJuW(R+tHWvXFoAhXNfzpM+E6&V}LytPs0Mkwp78~?fa{*wXIXK%`WEvU=XE!(L zG1TbtKMc}u^#A~PoMT{QU|;}Z-DLHkcz&C&4BRXXAaHH&i~k7v|1A~28RD&J_7&}i~)bMiw9K!e_alOFc1d&VT*v^51xZV6wDS*6d+P@hV<(bW3a`FzT9%$9ck_!SS|1Z0 R4XfX3S4w>Vcy}#z006CNd_e#J delta 1895 zcmV-t2blQ06XX&UcTYw}00961000SO01E&B000iukrYdR*#H0nn%2n(K4)xsW&i*J zTmS$BivR!y#6zQZ#%N_>WB>pL=l}o!F8}}lHV{d72WV(zVE_OJ7ytkO8~^|SBnRFD zl4xyocmMzgGynhq6aWAKL__HS0Bmn#VE_OJNB{r;Bme*aBo0sy*ll5WZ~y=YYybcN z9{>OVAORpM09|fjZDjxe2aEs!0Zsq_0(l2?ZqwN=3yfzQierJzjy3phU6tMjCr$TmA!d zfWr7O+K^5p+>;y)+$x0)>tzwwbDjg?JtMx)!y85lUr`r*%aHUlR zuD0sHe}Tqf*?^kqAKv3Wu}BwT8u9Us%=~^PSYaD z`M_Rzer&RkJ$|nJx>L1M02>>SD@-bv7BySL^^R@kvU#2vlg90_il@WrKSqDYbF$0husXd$MhVHK07=8tf)wn4074Mo2Puem;++7b_&&WdybqEy_BoPG*PkSrhumVh$UPE8sw-(~i2)B1E0GU-7wURFb`A8oD?X{u{$_ zYL2reKV(TF?{GosSo*_L;kXq&ee_Y^SB7%xy$Z?cngazn&@*X}RW&1y7Mqq;^qWvF zgikyNP%R%ZrUtz<$T@#|AY+b=1lCY4KYN?rTfn{HkiYqfRq_ttS^vXB#C~(~A<&e! zamG8dyM}@P8JKaA1N=4))Gy%{TPw;{5;+f@Jsp~s)=Heiq-IQW(ln2qk`+bH=M&kI z#~^taE^%^d(u~`RrQE^4GWvj^N^-%3KBf0?-dJRkkZKo@U6Fr-B^rC*VjGg48q}SA z$l@3|b=q{WwVYQ3`mAOcTJNGDkD4}sW2E5Mevh^KMQs%J z__dJ(i8vS)8E8V#X>|;2CqKYv42<#7t^hp+bG6zW+`L(<*Jglit*>t}cx1X+U#%aa z^iVy&n$OdfFj#+EKnJ=P)`D>5U=4+Rpll!GLws&uzCAK>`*L6I=Z|>No3Cl~oaMFAt%53xIr;qMYpyO5S%>?^C_RXf!xFp0w!PXnZRe4I;5;6+uh z>ka0^ll0~O>g{aMzTrn%gUtd7!isFBdTod7}NYN_B=Pz;YqTwgb z;eD>{_3@wVGnU6Zi^xFlC22(|1A~j5nQ00000b^xLP$^h^I5&=R1oB_lE z@&Y&lbONRV)B^wmC<9`6oMT{QU|28RD&J_7&}aRGj_+Xqzv ze^CyCFc1a%uth*n@g5v%fr<^dD@zF;-$eX$erJ*~iP +
+ {{ selectedText }} + {{ placeholder }} + +
+ + + + diff --git a/src/index.js b/src/index.js index 7d72bc7d..ba180d30 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 cd80f562..7f8d60dc 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 00000000..fe0a9cd3 --- /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 ee0f7ca0..92b65931 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 d93b4ac5..a3440b80 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 00000000..f6624e40 --- /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 + }) +} -- GitLab