Repository: ant-design/ant-design-mobile
Pull request #5449 (2022-07-06 08:07:22)

enhance(Rate): use aria labels to imporve a11y


Patch:
diff --git a/src/components/rate/rate.tsx b/src/components/rate/rate.tsx
index 065b2f201..345bb70e9 100644
--- a/src/components/rate/rate.tsx
+++ b/src/components/rate/rate.tsx
@@ -48,6 +48,9 @@ export const Rate: FC<RateProps> = p => {
             setValue(v)
           }
         }}
+        role='radio'
+        aria-checked={value >= v}
+        aria-label={'' + v}
       >
         {props.character}
       </div>
@@ -55,7 +58,11 @@ export const Rate: FC<RateProps> = p => {
   }
   return withNativeProps(
     props,
-    <div className={classPrefix}>
+    <div
+      className={classPrefix}
+      role='radiogroup'
+      aria-readonly={props.readOnly}
+    >
       {starList.map((_, i) => (
         <div key={i} className={classNames(`${classPrefix}-box`)}>
           {props.allowHalf && renderStar(i + 0.5, true)}
diff --git a/src/components/rate/star.tsx b/src/components/rate/star.tsx
index 5d599a8b6..cb640c430 100644
--- a/src/components/rate/star.tsx
+++ b/src/components/rate/star.tsx
@@ -2,16 +2,6 @@ import React from 'react'
 import { FC } from 'react'
 
 export const Star: FC = () => {
-  // return (
-  //   <svg height='1em' viewBox='0 0 48 48' xmlns='http://www.w3.org/2000/svg'>
-  //     <title>{'star-fill\u5907\u4EFD 71'}</title>
-  //     <path
-  //       d='m24 36-10.52 5.53a2 2 0 0 1-2.902-2.108l2.01-11.714-8.511-8.296a2 2 0 0 1 1.108-3.411l11.762-1.71 5.26-10.657a2 2 0 0 1 3.586 0l5.26 10.658L42.815 16a2 2 0 0 1 1.108 3.411l-8.51 8.296 2.009 11.714a2 2 0 0 1-2.902 2.109L24 36Z'
-  //       fill='#E5E5E5'
-  //       fillRule='evenodd'
-  //     />
-  //   </svg>
-  // )
   return (
     <svg
       viewBox='0 0 42 40'


Test patch:
diff --git a/src/components/rate/tests/rate.test.tsx b/src/components/rate/tests/rate.test.tsx
new file mode 100644
index 000000000..92c5e581e
--- /dev/null
+++ b/src/components/rate/tests/rate.test.tsx
@@ -0,0 +1,41 @@
+import React from 'react'
+import { render, testA11y, fireEvent, screen } from 'testing'
+import Rate from '..'
+
+const classPrefix = `adm-rate`
+
+describe('Rate', () => {
+  test('a11y', async () => {
+    await testA11y(<Rate />)
+  })
+
+  test('readOnly should be work', () => {
+    const onChange = jest.fn()
+    render(<Rate value={4} readOnly onChange={onChange} />)
+    const radio = screen.getByRole('radio', { name: '1' })
+    fireEvent.click(radio)
+    expect(radio).toHaveClass(`${classPrefix}-star-readonly`)
+    expect(onChange).not.toBeCalled()
+  })
+
+  test('allowHalf should be work', () => {
+    render(<Rate allowHalf />)
+    const radio = screen.getByRole('radio', { name: '1.5' })
+    fireEvent.click(radio)
+    expect(radio).toHaveClass(`${classPrefix}-star-half`)
+    expect(radio).toHaveClass(`${classPrefix}-star-active`)
+
+    fireEvent.click(radio)
+    expect(
+      document.querySelectorAll(`.${classPrefix}-star-active`)
+    ).toHaveLength(0)
+  })
+
+  test('can not clear', () => {
+    render(<Rate allowClear={false} defaultValue={1} />)
+    const radio = screen.getByRole('radio', { name: '1' })
+    expect(radio).toHaveClass(`${classPrefix}-star-active`)
+    fireEvent.click(radio)
+    expect(radio).toHaveClass(`${classPrefix}-star-active`)
+  })
+})