From dea4f5e2bf60fe621501435021327724dd2f8b9f Mon Sep 17 00:00:00 2001 From: HQ-Lin Date: Mon, 1 Aug 2022 15:01:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=20rate=20=E7=BB=84?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/_common | 2 +- src/rate/Rate.tsx | 161 +- .../__snapshots__/rate.test.tsx.snap | 3062 ++++++++++++----- src/rate/__tests__/rate.test.tsx | 8 +- src/rate/_example/base.jsx | 2 +- src/rate/_example/{mulit.jsx => custom.jsx} | 0 src/rate/_example/size.jsx | 6 +- src/rate/_example/status.jsx | 7 +- src/rate/_example/texts.jsx | 2 +- src/rate/defaultProps.ts | 4 +- test/ssr/__snapshots__/ssr.test.js.snap | 10 + 11 files changed, 2338 insertions(+), 926 deletions(-) rename src/rate/_example/{mulit.jsx => custom.jsx} (100%) diff --git a/src/_common b/src/_common index cfe0bc031..189b9ff3f 160000 --- a/src/_common +++ b/src/_common @@ -1 +1 @@ -Subproject commit cfe0bc031dd9d55fc760e7a2e074bc868be9bcc3 +Subproject commit 189b9ff3fd9a8878b1618ce3bd4dbb4d7c69746c diff --git a/src/rate/Rate.tsx b/src/rate/Rate.tsx index 70b0784d7..f5586a55b 100644 --- a/src/rate/Rate.tsx +++ b/src/rate/Rate.tsx @@ -1,38 +1,17 @@ -import React, { MouseEvent, useState, useCallback } from 'react'; +import React, { MouseEvent, useState, forwardRef } from 'react'; +import classNames from 'classnames'; +import { StarFilledIcon as TdStarFilledIcon } from 'tdesign-icons-react'; import Tooltip from '../tooltip'; import { TdRateProps } from './type'; +import { StyledProps } from '../common'; import useConfig from '../hooks/useConfig'; +import useGlobalIcon from '../hooks/useGlobalIcon'; import useControlled from '../hooks/useControlled'; import { rateDefaultProps } from './defaultProps'; -const getFilledStarSvg = (size) => ( - - - -); +export interface RateProps extends TdRateProps, StyledProps {} -const getFilledHalfStarSvg = (size) => ( - - - - - - - - - -); - -export type RateProps = TdRateProps; -const Rate = (props: RateProps) => { +const Rate = forwardRef((props: RateProps, ref: React.Ref) => { const { allowHalf, // 是否允许半选 color, // 评分图标的颜色,样式中默认为 #ED7B2F。一个值表示设置选中高亮的五角星颜色,两个值表示分别设置 选中高亮的五角星颜色 和 未选中暗灰的五角星颜色。示例:['#ED7B2F', '#999999'] @@ -42,15 +21,23 @@ const Rate = (props: RateProps) => { showText, // 是否显示对应的辅助文字 size, // 评分图标的大小,示例:`20` texts, // 自定义评分等级对应的辅助文字。组件内置默认值为:['极差', '失望', '一般', '满意', '惊喜']。自定义值示例:['1分', '2分', '3分', '4分', '5分'] + className, + style, onChange, } = props; + + const { classPrefix } = useConfig(); + const { StarFilledIcon } = useGlobalIcon({ StarFilledIcon: TdStarFilledIcon }); const [starValue = 0, setStarValue] = useControlled(props, 'value', onChange); const [hoverValue = undefined, setHoverValue] = useState(undefined); - const rootRef = React.useRef(null); - const { classPrefix } = useConfig(); + const displayValue = hoverValue || starValue; + const rootRef = React.useRef(null); + + const activeColor = Array.isArray(color) ? color[0] : color; + const defaultColor = Array.isArray(color) ? color[1] : 'var(--td-bg-color-component)'; - const getStarValue = (event: MouseEvent, index: number) => { + const getStarValue = (event: MouseEvent, index: number) => { if (allowHalf) { const rootNode = rootRef.current; const { left } = rootNode.getBoundingClientRect(); @@ -58,89 +45,75 @@ const Rate = (props: RateProps) => { const { width } = firstStar.getBoundingClientRect(); const { clientX } = event; const starMiddle = width * (index - 0.5) + gap * (index - 1); - if (clientX - left < starMiddle) { - return index - 0.5; - } - if (clientX - left >= starMiddle) { - return index; - } - } else { - return index; + if (clientX - left >= starMiddle) return index; + if (clientX - left < starMiddle) return index - 0.5; } + + return index; }; - const mouseEnterHandler = (event: MouseEvent, index: number) => { + const mouseEnterHandler = (event: MouseEvent, index: number) => { + if (disabled) return; setHoverValue(getStarValue(event, index)); }; const mouseLeaveHandler = () => { + if (disabled) return; setHoverValue(undefined); }; - const clickHandler = (event: MouseEvent, index: number) => { + const clickHandler = (event: MouseEvent, index: number) => { + if (disabled) return; setStarValue(getStarValue(event, index)); }; - const getStarStyle = useCallback( - (index: number, count: number, displayValue: number): React.CSSProperties => { - const filledColor = Array.isArray(color) ? color[0] : color || '#ED7B2F'; - const defaultColor = Array.isArray(color) ? color[1] : '#E7E7E7'; - return { - marginRight: index < count - 1 ? gap : '', - width: size, - height: size, - color: index < displayValue ? filledColor : defaultColor, - }; - }, - [size, color, gap], - ); + const getStarCls = (index: number) => { + if (allowHalf && index + 0.5 === displayValue) return `${classPrefix}-rate__item--half`; + if (index >= displayValue) return ''; + if (index < displayValue) return `${classPrefix}-rate__item--full`; + }; - const getStar = useCallback( - (allowHalf: boolean, index: number, displayValue: number) => { - if (allowHalf && index + 0.5 === displayValue) { - return getFilledHalfStarSvg(size); - } - if (index >= displayValue) { - return getFilledStarSvg(size); - } - if (index < displayValue) { - return getFilledStarSvg(size); - } - }, - [size], - ); - const displayValue = hoverValue || starValue; return ( -
!disabled && mouseLeaveHandler()}> - {[...Array(count)].map((_, index) => - showText ? ( - -
!disabled && mouseEnterHandler(event, index + 1)} - onClick={(event) => !disabled && clickHandler(event, index + 1)} - style={getStarStyle(index, count, displayValue)} - className={`${classPrefix}-rate__wrapper`} - > - {getStar(allowHalf, index, displayValue)} -
-
- ) : ( -
+
    + {[...Array(count)].map((_, index) => ( +
  • !disabled && mouseEnterHandler(event, index + 1)} - onClick={(event) => !disabled && clickHandler(event, index + 1)} - style={getStarStyle(index, count, displayValue)} - className={`${classPrefix}-rate__wrapper`} + className={classNames(`${classPrefix}-rate__item`, getStarCls(index))} + onClick={(event) => clickHandler(event, index + 1)} + onMouseMove={(event) => mouseEnterHandler(event, index + 1)} > - {getStar(allowHalf, index, displayValue)} -
- ), - )} + {showText ? ( + +
+ +
+
+ +
+
+ ) : ( + <> +
+ +
+
+ +
+ + )} + + ))} + {showText &&
{texts[displayValue - 1]}
}
); -}; +}); Rate.displayName = 'Rate'; Rate.defaultProps = rateDefaultProps; diff --git a/src/rate/__tests__/__snapshots__/rate.test.tsx.snap b/src/rate/__tests__/__snapshots__/rate.test.tsx.snap index b44b7b117..5e8083467 100644 --- a/src/rate/__tests__/__snapshots__/rate.test.tsx.snap +++ b/src/rate/__tests__/__snapshots__/rate.test.tsx.snap @@ -5,1199 +5,2621 @@ exports[`Rate 组件测试 count 1`] = `
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- -`; - -exports[`Rate 组件测试 create 1`] = ` -
-
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
-
-`; - -exports[`base.jsx 1`] = ` - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
-
-`; - -exports[`mulit.jsx 1`] = ` - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
- - - -
-
-
-`; - -exports[`size.jsx 1`] = ` - -
-
-
+ +
  • +
  • +
  • -
  • -
    -
    -
    + +
  • +
  • +
  • -
  • -
    -
    -
    +
  • +
  • +
  • +
  • +
  • -
  • -
    -
    -
    -`; - -exports[`status.jsx 1`] = ` - -
    -
    -
    + +
  • +
  • +
  • -
  • -
    -
    + +
    +
    +`; + +exports[`Rate 组件测试 create 1`] = ` +
    +
    +
      -
      + +
    • +
    • +
    • -
    • -
    -
    -
    + +
  • +
  • +
  • - - - - - -
    -
  • -
    + +
    -
    + `; -exports[`texts.jsx 1`] = ` +exports[`base.jsx 1`] = `
    -
    -
    - - - -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - -
    -
    -
    -
    - - - -
    -
    +
    + + + +
    +
    + + + +
    + +
  • +
    + + + +
    +
    + + + +
    +
  • +
  • +
    + + + +
    +
    + + + +
    +
  • +
  • +
    + + + +
    +
    + + + +
    +
  • +
  • +
    + + + +
    +
    + + + +
    +
  • + +
    +
    +`; + +exports[`custom.jsx 1`] = ` + +
    +
      +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    +
    +
    +`; + +exports[`size.jsx 1`] = ` + +
    +
    +

    + 16px +

    +
    +
    +
    +
      +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    +
    +
    +
    +

    + 24px +

    +
    +
    +
    +
      +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    +
    +
    +
    +
    +`; + +exports[`status.jsx 1`] = ` + +
    +
    +

    + 未评分状态 +

    +
    +
    +
    +
      +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    +
    +
    +
    +

    + 满分状态 +

    +
    +
    +
    +
      +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    +
    +
    +
    +

    + 半星状态 +

    +
    +
    +
    +
      +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    • +
      + + + +
      +
      + + + +
      +
    • +
    +
    +
    +
    +
    +`; + +exports[`texts.jsx 1`] = ` + +
    +
      +
    • +
      + +
      + + + +
      +
      + + + +
      +
      +
      +
    • +
    • +
      + +
      + + + +
      +
      + + + +
      +
      +
      +
    • +
    • +
      + +
      + + + +
      +
      + + + +
      +
      +
      +
    • +
    • +
      + +
      + + + +
      +
      + + + +
      +
      +
      +
    • +
    • +
      + +
      + + + +
      +
      + + + +
      +
      +
      +
    • +
    diff --git a/src/rate/__tests__/rate.test.tsx b/src/rate/__tests__/rate.test.tsx index 6b6d79581..8c0fff812 100644 --- a/src/rate/__tests__/rate.test.tsx +++ b/src/rate/__tests__/rate.test.tsx @@ -10,14 +10,14 @@ describe('Rate 组件测试', () => { test('create', async () => { const { container } = render(); expect(container.firstChild.classList.contains('t-rate')).toBeTruthy(); - expect(document.querySelectorAll('.t-rate__wrapper')).toHaveLength(5); + expect(document.querySelectorAll('.t-rate__item')).toHaveLength(5); expect(container).toMatchSnapshot(); }); // 点击测试 test('onChange', async () => { const clickFn = jest.fn(); render(); - fireEvent.click(document.querySelector('.t-rate__wrapper')); + fireEvent.click(document.querySelector('.t-rate__item')); expect(clickFn).toBeCalledTimes(1); expect(clickFn).toBeCalledWith(1); }); @@ -25,13 +25,13 @@ describe('Rate 组件测试', () => { test('disable', async () => { const clickFn = jest.fn(); render(); - fireEvent.click(document.querySelector('.t-rate__wrapper')); + fireEvent.click(document.querySelector('.t-rate__item')); expect(clickFn).toBeCalledTimes(0); }); // 数量测试 test('count', async () => { const { container } = render(); - expect(document.querySelectorAll('.t-rate__wrapper')).toHaveLength(10); + expect(document.querySelectorAll('.t-rate__item')).toHaveLength(10); expect(container).toMatchSnapshot(); }); }); diff --git a/src/rate/_example/base.jsx b/src/rate/_example/base.jsx index 7e790676c..704ca8e46 100644 --- a/src/rate/_example/base.jsx +++ b/src/rate/_example/base.jsx @@ -2,5 +2,5 @@ import React from 'react'; import { Rate } from 'tdesign-react'; export default function BasicRate() { - return ; + return ; } diff --git a/src/rate/_example/mulit.jsx b/src/rate/_example/custom.jsx similarity index 100% rename from src/rate/_example/mulit.jsx rename to src/rate/_example/custom.jsx diff --git a/src/rate/_example/size.jsx b/src/rate/_example/size.jsx index 6138f29ad..c1a8fb474 100644 --- a/src/rate/_example/size.jsx +++ b/src/rate/_example/size.jsx @@ -3,10 +3,12 @@ import { Space, Rate } from 'tdesign-react'; export default function BasicRate() { return ( - + +

    16px

    + +

    24px

    -
    ); } diff --git a/src/rate/_example/status.jsx b/src/rate/_example/status.jsx index 9d4268dfd..9fe4026f4 100644 --- a/src/rate/_example/status.jsx +++ b/src/rate/_example/status.jsx @@ -4,9 +4,14 @@ import { Space, Rate } from 'tdesign-react'; export default function BasicRate() { return ( +

    未评分状态

    + +

    满分状态

    - + +

    半星状态

    +
    ); } diff --git a/src/rate/_example/texts.jsx b/src/rate/_example/texts.jsx index d6f725cd1..d9fb97f59 100644 --- a/src/rate/_example/texts.jsx +++ b/src/rate/_example/texts.jsx @@ -2,5 +2,5 @@ import React from 'react'; import { Rate } from 'tdesign-react'; export default function BasicRate() { - return ; + return ; } diff --git a/src/rate/defaultProps.ts b/src/rate/defaultProps.ts index 28aead7d2..20c06610b 100644 --- a/src/rate/defaultProps.ts +++ b/src/rate/defaultProps.ts @@ -8,9 +8,9 @@ export const rateDefaultProps: TdRateProps = { defaultValue: 0, allowHalf: false, color: '#ED7B2F', - size: '20', + size: '24px', count: 5, - gap: 6, + gap: 4, showText: false, texts: ['极差', '失望', '一般', '满意', '惊喜'], }; diff --git a/test/ssr/__snapshots__/ssr.test.js.snap b/test/ssr/__snapshots__/ssr.test.js.snap index 820ea90a5..f09f05ada 100644 --- a/test/ssr/__snapshots__/ssr.test.js.snap +++ b/test/ssr/__snapshots__/ssr.test.js.snap @@ -618,6 +618,16 @@ exports[`ssr snapshot test renders ./src/range-input/_example/size.jsx correctly exports[`ssr snapshot test renders ./src/range-input/_example/status.jsx correctly 1`] = `"
    -
    -
    -
    "`; +exports[`ssr snapshot test renders ./src/rate/_example/base.jsx correctly 1`] = `"
    "`; + +exports[`ssr snapshot test renders ./src/rate/_example/custom.jsx correctly 1`] = `"
    "`; + +exports[`ssr snapshot test renders ./src/rate/_example/size.jsx correctly 1`] = `"

    16px

    24px

    "`; + +exports[`ssr snapshot test renders ./src/rate/_example/status.jsx correctly 1`] = `"

    未评分状态

    满分状态

    半星状态

    "`; + +exports[`ssr snapshot test renders ./src/rate/_example/texts.jsx correctly 1`] = `"
    满意
    "`; + exports[`ssr snapshot test renders ./src/select/_example/base.jsx correctly 1`] = `"
    "`; exports[`ssr snapshot test renders ./src/select/_example/collapsed.jsx correctly 1`] = `"
    请选择
    请选择
    "`;