From b24de1d5a3cdddb8967ddf1be67c47418ae70087 Mon Sep 17 00:00:00 2001 From: "tianhaoyun@didiglobal.com" Date: Thu, 19 Dec 2024 17:04:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8Drate=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E4=B8=ADwx=E5=B0=8F=E7=A8=8B=E5=BA=8F=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/app.mpx | 3 +- example/common/config.ts | 3 +- example/pages/loading/README.md | 29 +++ example/pages/loading/index.mpx | 41 ++++ example/pages/rate/index.mpx | 136 ++++++----- example/static/wx/project.config.json | 23 +- jest.config.js | 3 +- .../__tests__/components/rate/rate.spec.js | 211 ++++++++++++++++++ .../components/rate/template/wx-model.mpx | 28 +++ .../stylus/theme/components/loading.styl | 18 ++ .../stylus/theme/components/switch.styl | 3 +- .../common/stylus/theme/components/toast.styl | 2 + .../src/components/loading/index.mpx | 47 ++++ .../src/components/loading/index.ts | 25 +++ .../mpx-cube-ui/src/components/rate/index.mpx | 26 ++- .../mpx-cube-ui/src/components/rate/index.ts | 21 +- .../src/components/rate/rate-item-index.ts | 11 +- .../src/components/rate/rate-item.mpx | 56 ++--- 18 files changed, 566 insertions(+), 120 deletions(-) create mode 100644 example/pages/loading/README.md create mode 100644 example/pages/loading/index.mpx create mode 100644 packages/mpx-cube-ui/__tests__/components/rate/rate.spec.js create mode 100644 packages/mpx-cube-ui/__tests__/components/rate/template/wx-model.mpx create mode 100644 packages/mpx-cube-ui/src/common/stylus/theme/components/loading.styl create mode 100644 packages/mpx-cube-ui/src/components/loading/index.mpx create mode 100644 packages/mpx-cube-ui/src/components/loading/index.ts diff --git a/example/app.mpx b/example/app.mpx index dcec318..7a60f5a 100644 --- a/example/app.mpx +++ b/example/app.mpx @@ -45,7 +45,8 @@ "./pages/textarea/index", "./pages/float-ball/index", "./pages/rate/index", - "./pages/switch/index" + "./pages/switch/index", + "./pages/loading/index" ] } diff --git a/example/common/config.ts b/example/common/config.ts index c90bdca..7741906 100644 --- a/example/common/config.ts +++ b/example/common/config.ts @@ -42,7 +42,8 @@ export default { 'divider', 'float-ball', 'rate', - 'switch' + 'switch', + 'loading' ] }, { diff --git a/example/pages/loading/README.md b/example/pages/loading/README.md new file mode 100644 index 0000000..4483924 --- /dev/null +++ b/example/pages/loading/README.md @@ -0,0 +1,29 @@ +## Cube-Segment-Picker + + + +### 介绍 + +段选择器,用于实现多段的选择,比如选择时间段:2010年9月1日 - 2014年6月30日。 + + + +## 示例 + + + +### 城市选择器 + +可以配置多段城市选择。 + + + + + + + +### 日期选择器 + + + + diff --git a/example/pages/loading/index.mpx b/example/pages/loading/index.mpx new file mode 100644 index 0000000..963148e --- /dev/null +++ b/example/pages/loading/index.mpx @@ -0,0 +1,41 @@ + + + + + + + diff --git a/example/pages/rate/index.mpx b/example/pages/rate/index.mpx index 9a61b0e..3181d38 100644 --- a/example/pages/rate/index.mpx +++ b/example/pages/rate/index.mpx @@ -4,61 +4,60 @@ - - - - - - Options - - - - - Disabled - - - - Star Numbers - - - - Justify - - - - CustomStar - - - - AllowHalf - - - + + Options + + + + Disabled + + + + Star Numbers + + + + Justify + + + + CustomStar + + + + AllowHalf + + + + - + + @@ -90,6 +89,10 @@ updateMaxLength (e) { const { value } = e.detail this.max = value + if (value >= 10) { + this.$refs.toast.show() + this.max = '10' + } }, updateJustify (e) { const { value } = e.detail @@ -113,8 +116,32 @@ height 100% background-color rgba(239, 239, 244, .7) .options - margin-top: 20px - + margin: 20px 0 + .rate-option-list + .group + border: 1px solid rgba(0, 0, 0, .1) + border-radius: 5px + .group-item + display: flex + align-items: center + justify-content: space-between + padding-right: 10px + border-bottom: 1px solid rgba(0, 0, 0, .1) + .item + padding-left: 15px + line-height: 52px + height: 52px + width: 120px + text-align: left + .sub + font-size: 14px + background-color: rgba(0, 0, 0, .04) + .input-maxlength + .input-width + flex: 1 + margin-left: 15px + .last + border-bottom: none .desc margin-top 15px @@ -124,6 +151,7 @@ "usingComponents": { "base-container": "../../components/base-container/index.mpx", "rate": "@mpxjs/mpx-cube-ui/src/components/rate/index.mpx", + "cube-toast": "@mpxjs/mpx-cube-ui/src/components/toast/index", "rate-item": "@mpxjs/mpx-cube-ui/src/components/rate/rate-item.mpx" } } diff --git a/example/static/wx/project.config.json b/example/static/wx/project.config.json index 82abd37..9d36c24 100644 --- a/example/static/wx/project.config.json +++ b/example/static/wx/project.config.json @@ -6,9 +6,9 @@ "es6": false, "postcss": true, "checkSiteMap": false, - "minified": false, + "minified": true, "autoAudits": false, - "enhance": false, + "enhance": true, "preloadBackgroundData": false, "coverView": true, "showShadowRootInWxmlPanel": true, @@ -30,27 +30,16 @@ "useStaticServer": true, "useCompilerPlugins": false, "minifyWXML": true, - "ignoreUploadUnusedFiles": true + "ignoreUploadUnusedFiles": true, + "useIsolateContext": true }, "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", - "condition": { - "miniprogram": { - "list": [ - { - "name": "", - "pathName": "pages/picker/index", - "query": "", - "launchMode": "default", - "scene": null - } - ] - } - }, + "condition": {}, "editorSetting": { "tabIndent": "insertSpaces", "tabSize": 2 }, - "libVersion": "2.16.1", + "libVersion": "3.7.1", "packOptions": { "ignore": [], "include": [] diff --git a/jest.config.js b/jest.config.js index 94ca7af..66db89b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -7,7 +7,8 @@ module.exports = { testEnvironment: 'jsdom', collectCoverage: false, testMatch: [ - '**/__tests__/**/*.spec.js' + // '**/__tests__/**/*.spec.js' + '**/__tests__/**/rate.spec.js' ], collectCoverageFrom: ['/packages/mpx-cube-ui/src/components/**/*.{js,mpx,ts}'], coverageDirectory: 'test/coverage', diff --git a/packages/mpx-cube-ui/__tests__/components/rate/rate.spec.js b/packages/mpx-cube-ui/__tests__/components/rate/rate.spec.js new file mode 100644 index 0000000..b2a5ebc --- /dev/null +++ b/packages/mpx-cube-ui/__tests__/components/rate/rate.spec.js @@ -0,0 +1,211 @@ +const simulate = require('@mpxjs/miniprogram-simulate') + +describe('component rate unit test', function () { + // const componentId = simulate.loadMpx('src/components/rate/index.mpx') + const componentTemplateId = simulate.loadMpx('test/components/rate/template/wx-model.mpx') + + // function changeProps(index) { + // const baseProps = { + // options: [ + // { + // value: 'Option1', + // text: 'Option1' + // }, + // { + // value: 'Option2', + // text: 'Option2' + // } + // ], + // value: 'Option2' + // } + // const options = baseProps.options[0] + // if (index === 1) options.position = 'right' + // if (index === 2) options.disabled = true + // if (index === 3) baseProps.inline = true + // if (index === 4) baseProps.colNum = 3 + + // return baseProps + // } + + function newComponent(id, props) { + const component = simulate.render(id, props) + const parent = document.createElement('parent') + component.attach(parent) // 会触发 attach 生命周期 + return component + } + + describe('wx-model check', () => { + const component = newComponent(componentTemplateId) + + // it('matchSnapshot', () => { + // expect(component.dom.innerHTML).toMatchSnapshot() // 判断前后生成的dom是否一样 + // }) + + it(' wx:model', async () => { + // const rates = component.querySelector('rate').querySelector('.cube-rate-items') + const rates = component.querySelector('rate').querySelector('.cube-rate-items').querySelectorAll('.cube-rate-item') + console.log(44, rates.length) + const rate1 = rates[0].querySelector('.cube-rate-item_active') + const rate2 = rates[1].querySelector('.cube-rate-item-def') + const rate3 = rates[2].querySelector('.cube-rate-item-def') + // 确保 rate 首次显示符合预期 + expect(rates.length).toEqual(3) + + expect(rate1 !== undefined).toBe(true) + expect(rate2 !== undefined).toBe(true) + expect(rate3 !== undefined).toBe(true) + + expect(component.instance.value).toBe(1) + + const start = { + changedTouches: [{ clientX: 0, clientY: 0 }] + } + const move = { + changedTouches: [{ clientX: 20, clientY: 0 }] + } + const end = { + changedTouches: [{ clientX: 30, clientY: 0 }] + } + const startHandler = jest.fn() + const moveHandler = jest.fn() + const endHandler = jest.fn() + component.addEventListener('touchstart', startHandler) + component.addEventListener('touchmove', moveHandler) + component.addEventListener('touchend', endHandler) + + component.dispatchEvent('touchstart', start) + component.dispatchEvent('touchmove', move) + component.dispatchEvent('touchend', end) + await simulate.sleep(10) + + // 点击第二个星星后,所有rate-item的值都改变且父组件的值也改变 + const rateClicked1 = rates[0].querySelector('cube-rate-item_active') + const rateClicked2 = rates[1].querySelector('cube-rate-item_active') + const rateClicked3 = rates[2].querySelector('cube-rate-item-def') + expect(component.instance.value).toBe(2) + expect(rateClicked1 !== undefined).toBe(true) + expect(rateClicked2 !== undefined).toBe(true) + expect(rateClicked3 !== undefined).toBe(true) + + // 改变父组件的值,所有radio的值也会改变 + component.instance.value = 3 + await simulate.sleep(10) + const rateChangeValued1 = rates[0].querySelector('cube-rate-item_active') + const rateChangeValued2 = rates[1].querySelector('cube-rate-item_active') + const rateChangeValued3 = rates[2].querySelector('cube-rate-item_active') + expect(rateChangeValued1 !== undefined).toBe(true) + expect(rateChangeValued2 !== undefined).toBe(true) + expect(rateChangeValued3 !== undefined).toBe(true) + }) + }) + // describe(' correct props check ', () => { + // const component = newComponent(componentId, changeProps()) + + // // it('matchSnapshot', () => { + // // expect(component.dom.innerHTML).toMatchSnapshot() // 判断前后生成的dom是否一样 + // // }) + + // it(' props to radio-group ', async () => { + // const radios = component.querySelectorAll('cube-radio') + // expect(component.instance.radioValue).toBe('Option2') + // expect(radios[0].instance.showText).toBe('Option1') + // expect(radios[1].instance.showText).toBe('Option2') + // expect(radios[1].instance.value).toBe('Option2') + // expect(radios[0].instance.value).toBe('Option2') + + // radios[0].querySelector('.cube-radio-label').dispatchEvent('tap') + // await simulate.sleep(10) + // expect(component.instance.radioValue).toBe('Option1') + // }) + // }) + + // describe(' check disabled', () => { + // const component = newComponent(componentId, changeProps(DISABLED)) + + // // it('matchSnapshot', () => { + // // expect(component.dom.innerHTML).toMatchSnapshot() // 判断前后生成的dom是否一样 + // // }) + // const radios = component.querySelectorAll('cube-radio') + // const radio0 = radios[0].querySelector('.cube-radio-label_disabled') + // const radio1 = radios[1].querySelector('.cube-radio-label_disabled') + + // expect(radio0).toBeTruthy() + // expect(radio1).toBe(undefined) + // }) + + // describe(' position check ', () => { + // const component = newComponent(componentId, changeProps(POSITION)) + + // // it('matchSnapshot', () => { + // // expect(component.dom.innerHTML).toMatchSnapshot() // 判断前后生成的dom是否一样 + // // }) + // const radios = component.querySelectorAll('cube-radio') + // const radio0 = radios[0].querySelector('.cube-radio-label-right') + // const radio1 = radios[1].querySelector('.cube-radio-label-right') + + // expect(radio0).toBeTruthy() + // expect(radio1).toBe(undefined) + // }) + + // describe(' line check ', () => { + // const component = newComponent(componentId, changeProps(LINE)) + + // // it('matchSnapshot', () => { + // // expect(component.dom.innerHTML).toMatchSnapshot() // 判断前后生成的dom是否一样 + // // }) + + // const radios = component.querySelector('.radio-group-inline-block') + + // expect(radios).toBeTruthy() + // }) + + // describe('slot check', () => { + // const onClickBtn = jest.fn() + // const prop = { + // title: '标题', + // content: '内容', + // confirmBtn: { + // text: '按钮文本' + // } + // } + // const component = simulate.render( + // simulate.load({ + // usingComponents: { + // 'cube-radio-group': componentId + // }, + // template: ` + // + // ${prop.title} + // ${prop.content} + // ${prop.confirmBtn.text} + // + // `, + // methods: { + // onClickBtn + // } + // }) + // ) + // const parent = document.createElement('parent') + // component.attach(parent) + + // // it('matchSnapshot', () => { + // // expect(component.dom.innerHTML).toMatchSnapshot() // 判断前后生成的dom是否一样 + // // }) + + // it('should render correct contents: title/content/btn', () => { + // const titleText = component.querySelector('.my-title').dom.innerHTML + // const contentText = component.querySelector('.my-content').dom.innerHTML + // const btnText = component.querySelector('.my-btn').dom.innerHTML + + // expect(titleText).toBe(prop.title) + // expect(contentText).toBe(prop.content) + // expect(btnText).toBe(prop.confirmBtn.text) + // }) + // it('should trigger event when click slot btn', async () => { + // const btn = component.querySelector('.my-btn') + // btn.dispatchEvent('tap') + // await simulate.sleep(10) + // expect(onClickBtn).toHaveBeenCalled() + // }) + // }) +}) diff --git a/packages/mpx-cube-ui/__tests__/components/rate/template/wx-model.mpx b/packages/mpx-cube-ui/__tests__/components/rate/template/wx-model.mpx new file mode 100644 index 0000000..8f5f08a --- /dev/null +++ b/packages/mpx-cube-ui/__tests__/components/rate/template/wx-model.mpx @@ -0,0 +1,28 @@ + + + + + diff --git a/packages/mpx-cube-ui/src/common/stylus/theme/components/loading.styl b/packages/mpx-cube-ui/src/common/stylus/theme/components/loading.styl new file mode 100644 index 0000000..3048515 --- /dev/null +++ b/packages/mpx-cube-ui/src/common/stylus/theme/components/loading.styl @@ -0,0 +1,18 @@ +vendors = official +// @type loading +$loading-font-size := 24px +$loading-spinners-width := 2px +$loading-spinners-height := 1rem +$loading-spinners-animation := spinner-fade 1s steps(12, end) infinite + + +$loading-spinner-width := 2px +$loading-spinner-height := 25% +$loading-spinner-normal-opacity := 0.25 // 正常透明度 +$loading-spinner-deep-opacity := 0.85 // 加深透明度 + +@keyframes spinner-fade + 0% + transform rotate(0deg) + 100% + transform rotate(360deg) \ No newline at end of file diff --git a/packages/mpx-cube-ui/src/common/stylus/theme/components/switch.styl b/packages/mpx-cube-ui/src/common/stylus/theme/components/switch.styl index 030d0e1..aa95121 100644 --- a/packages/mpx-cube-ui/src/common/stylus/theme/components/switch.styl +++ b/packages/mpx-cube-ui/src/common/stylus/theme/components/switch.styl @@ -1,4 +1,5 @@ -// @type popup +vendors = official + $switch-width := 40px $switch-height := 24px $switch-bgc := #EAEAEA diff --git a/packages/mpx-cube-ui/src/common/stylus/theme/components/toast.styl b/packages/mpx-cube-ui/src/common/stylus/theme/components/toast.styl index c260df9..ab19fbf 100644 --- a/packages/mpx-cube-ui/src/common/stylus/theme/components/toast.styl +++ b/packages/mpx-cube-ui/src/common/stylus/theme/components/toast.styl @@ -1,3 +1,5 @@ +vendors = official + $toast-dark-opacity-bgc := rgba(0, 0, 0, 0.8) // 背景颜色 // toast diff --git a/packages/mpx-cube-ui/src/components/loading/index.mpx b/packages/mpx-cube-ui/src/components/loading/index.mpx new file mode 100644 index 0000000..fa66501 --- /dev/null +++ b/packages/mpx-cube-ui/src/components/loading/index.mpx @@ -0,0 +1,47 @@ + + + + + + + diff --git a/packages/mpx-cube-ui/src/components/loading/index.ts b/packages/mpx-cube-ui/src/components/loading/index.ts new file mode 100644 index 0000000..05b527a --- /dev/null +++ b/packages/mpx-cube-ui/src/components/loading/index.ts @@ -0,0 +1,25 @@ +import { createComponent } from '../../common/helper/create-component' + +createComponent({ + properties: { + size: { + type: Number, + value: 24 + } + }, + data: { + balde: 12 + }, + computed: { + style() { + if (!this.size) { + return + } + const value = `${this.size}px` + return { + width: value, + height: value + } + } + } +}) diff --git a/packages/mpx-cube-ui/src/components/rate/index.mpx b/packages/mpx-cube-ui/src/components/rate/index.mpx index 18259cf..2ea5533 100644 --- a/packages/mpx-cube-ui/src/components/rate/index.mpx +++ b/packages/mpx-cube-ui/src/components/rate/index.mpx @@ -1,16 +1,18 @@ @@ -20,12 +22,14 @@ @require "@mpxjs/mpx-cube-ui/src/common/stylus/helper.styl" @require "@mpxjs/mpx-cube-ui/src/common/stylus/mixin.styl" @require "@mpxjs/mpx-cube-ui/src/common/stylus/theme/components/rate.styl" - .cube-rate + + .cube-rate, .cube-rate-items list-style: none display: inline-flex vertical-align: top flex-wrap: nowrap max-width: $var(rate-max-width) + .cube-rate-justify width: $var(rate-justify-width) justify-content: space-between diff --git a/packages/mpx-cube-ui/src/components/rate/index.ts b/packages/mpx-cube-ui/src/components/rate/index.ts index 6091d7c..5e2c20d 100644 --- a/packages/mpx-cube-ui/src/components/rate/index.ts +++ b/packages/mpx-cube-ui/src/components/rate/index.ts @@ -46,10 +46,18 @@ createComponent({ allowHalf: { type: Boolean, value: false + }, + /** + * @description 是否自定义 + */ + isCustom: { + type: Boolean, + value: false } }, data: { - tempValue: 0 + tempValue: 0, + domName: '' }, computed: { rateClass() { @@ -69,6 +77,7 @@ createComponent({ }, lifetimes: { created() { + this.domName = this.isCustom ? '#cube-rate' : '#cube-rate-items' this.mousePressed = false } }, @@ -80,12 +89,16 @@ createComponent({ document.addEventListener('mouseup', this.handleEnd) document.addEventListener('mousemove', this.handleMove) } - const rect = this.$refs.rateContainer.$el.getBoundingClientRect() - this.left = rect.left - this.containerWidth = rect.width + const that = this // eslint-disable-line + this.createSelectorQuery().select(this.domName).boundingClientRect(function (rect) { + const { width, left } = rect + that.containerWidth = width + that.left = left + }).exec() } }, handleMove(e) { + e.preventDefault && e.preventDefault() if (this.disabled) return if (!isMouseEvent(e)) { diff --git a/packages/mpx-cube-ui/src/components/rate/rate-item-index.ts b/packages/mpx-cube-ui/src/components/rate/rate-item-index.ts index 8885694..72affa4 100644 --- a/packages/mpx-cube-ui/src/components/rate/rate-item-index.ts +++ b/packages/mpx-cube-ui/src/components/rate/rate-item-index.ts @@ -1,4 +1,6 @@ import { createComponent } from '@mpxjs/core' +import { isWeb } from '../../common/helper/utils' + createComponent({ properties: { index: { @@ -8,13 +10,18 @@ createComponent({ value: { type: Number, value: 0 + }, + isCustom: { + type: Boolean, + value: true } }, computed: { rateItemClass() { + const indexValue = isWeb ? this.index : this.index + 1 return { - 'cube-rate-item_active': this.index <= this.value, - 'cube-rate-item_half_active': this.index === this.value + 0.5 + 'cube-rate-item_active': indexValue <= this.value, + 'cube-rate-item_half_active': indexValue === this.value + 0.5 } } } diff --git a/packages/mpx-cube-ui/src/components/rate/rate-item.mpx b/packages/mpx-cube-ui/src/components/rate/rate-item.mpx index 101c085..d8f0edd 100644 --- a/packages/mpx-cube-ui/src/components/rate/rate-item.mpx +++ b/packages/mpx-cube-ui/src/components/rate/rate-item.mpx @@ -1,8 +1,7 @@ @@ -11,31 +10,32 @@ @require "@mpxjs/mpx-cube-ui/src/common/stylus/helper.styl" @require "@mpxjs/mpx-cube-ui/src/common/stylus/mixin.styl" @require "@mpxjs/mpx-cube-ui/src/common/stylus/theme/components/rate.styl" - .cube-rate-item - position: relative - width: $var(rate-item-width) - flex: $var(rate-item-flex) - margin-right: $var(rate-item-margin-right) - &::after - content: "" - display: block - padding: $var(rate-item-after-padding) - &:last-child - margin-right: $var(rate-item-last-child-margin-right) - .cube-rate-item-def - position: absolute - width: $var(rate-item-def-width) - height: $var(rate-item-def-height) - background-size: $var(rate-item-def-background-size) - background-repeat: no-repeat - background-position: center - bg-image("evaluation_icon_star_gray") - .cube-rate-item_active - .cube-rate-item-def - bg-image("evaluation_icon_star") - .cube-rate-item_half_active - .cube-rate-item-def - bg-image("evaluation_icon_half") + +.cube-rate-item + position: relative + width: $var(rate-item-width) + flex: $var(rate-item-flex) + margin-right: $var(rate-item-margin-right) + &::after + content: "" + display: block + padding: $var(rate-item-after-padding) + &:last-child + margin-right: $var(rate-item-last-child-margin-right) + .cube-rate-item-def + position: absolute + width: $var(rate-item-def-width) + height: $var(rate-item-def-height) + background-size: $var(rate-item-def-background-size) + background-repeat: no-repeat + background-position: center + bg-image("evaluation_icon_star_gray") +.cube-rate-item_active + .cube-rate-item-def + bg-image("evaluation_icon_star") +.cube-rate-item_half_active + .cube-rate-item-def + bg-image("evaluation_icon_half")