Repository: ant-design/ant-design-mobile
Pull request #5484 (2022-07-19 12:03:50)

feat(SwipeAction): add `onActionsReveal` prop
feat(swipeAction): add onActionsReveal props


Patch:
diff --git a/config/menus.tsx b/config/menus.tsx
index c5d07133f..92f7c3345 100644
--- a/config/menus.tsx
+++ b/config/menus.tsx
@@ -54,10 +54,6 @@ export const menus = {
       title: 'HD',
       path: '/guide/hd',
     },
-    {
-      title: 'Pre Built Bundles',
-      path: '/guide/pre-built-bundles',
-    },
     {
       title: 'SSR (Experimental)',
       path: '/guide/ssr',
@@ -108,10 +104,6 @@ export const menus = {
       title: '高清适配',
       path: '/zh/guide/hd',
     },
-    {
-      title: '预构建产物',
-      path: '/zh/guide/pre-built-bundles',
-    },
     {
       title: '服务端渲染 / SSR（试验性）',
       path: '/zh/guide/ssr',
diff --git a/docs/guide/faq.en.md b/docs/guide/faq.en.md
index cc3a33320..35aa5f3d8 100644
--- a/docs/guide/faq.en.md
+++ b/docs/guide/faq.en.md
@@ -97,4 +97,4 @@ Please don't try `npm install demos`. You can just ignore them.
 
 ### Does antd-mobile have umd package on CDN?
 
-Yes, please refer to [this document](/guide/pre-built-bundles) for specific usage.
+Yes, you can find it at [unpkg](https://unpkg.com/browse/antd-mobile@5/umd/antd-mobile.js) or [jsdelivr](https://www.jsdelivr.com/package/npm/antd-mobile?path=umd). Of course, you can also deploy it yourself.
diff --git a/docs/guide/faq.zh.md b/docs/guide/faq.zh.md
index bbe9dbf50..3029fd7af 100644
--- a/docs/guide/faq.zh.md
+++ b/docs/guide/faq.zh.md
@@ -97,4 +97,4 @@ codesandbox 是一个浏览器端的沙盒运行环境，支持多种流行的
 
 ### antd-mobile 有 CDN 上的 umd 包吗？
 
-有的，具体用法请参考[这篇文档](/zh/guide/pre-built-bundles)。
+有的，你可以在 [unpkg](https://unpkg.com/browse/antd-mobile@5/umd/antd-mobile.js) 或 [jsdelivr](https://www.jsdelivr.com/package/npm/antd-mobile?path=umd) 上找到。当然，你也可以自己部署。
diff --git a/docs/guide/pre-built-bundles.en.md b/docs/guide/pre-built-bundles.en.md
deleted file mode 100644
index 41912b1e8..000000000
--- a/docs/guide/pre-built-bundles.en.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# Pre-Built Bundles
-
-antd-mobile provides a set of pre-built artifacts, consisting of two parts:
-
-js resource:
-
-```text
-/bundle/antd-mobile.cjs.js
-/bundle/antd-mobile.es.js
-/bundle/antd-mobile.umd.js
-/bundle/antd-mobile.compatible.umd.js
-```
-
-Among them, we provide two versions of umd files, one is `antd-mobile.umd.js` not processed by babel, the compatibility is `Chrome >= 61` `iOS >= 11`, the other is processed by babel `antd-mobile.compatible.umd.js`, compatibility is `Chrome >= 49` `iOS >= 9`.
-
-css resource:
-
-```text
-/bundle/css-vars-patch.css
-/bundle/style.css
-```
-
-These pre-built js files do not contain css content, so usually, you need to selectively import a certain version of js resources (such as `/bundle/antd-mobile.umd.js`), and then add additional Import `/bundle/style.css`.
-
-You can find it at [unpkg](https://unpkg.com/browse/antd-mobile@5/bundle/) or [jsdelivr](https://www.jsdelivr.com/package/npm/antd-mobile?path =bundle).
diff --git a/docs/guide/pre-built-bundles.zh.md b/docs/guide/pre-built-bundles.zh.md
deleted file mode 100644
index 1244854db..000000000
--- a/docs/guide/pre-built-bundles.zh.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# 预构建产物
-
-antd-mobile 提供了一套预构建的产物，包括两部分：
-
-js 资源：
-
-```text
-/bundle/antd-mobile.cjs.js
-/bundle/antd-mobile.es.js
-/bundle/antd-mobile.umd.js
-/bundle/antd-mobile.compatible.umd.js
-```
-
-其中我们提供了两个版本的 umd 文件，一个是未被 babel 处理的 `antd-mobile.umd.js`，兼容性为 `Chrome >= 61` `iOS >= 11`，另一个是经过 babel 处理的 `antd-mobile.compatible.umd.js`，兼容性为 `Chrome >= 49` `iOS >= 9`。
-
-css 资源：
-
-```text
-/bundle/css-vars-patch.css
-/bundle/style.css
-```
-
-这些预构建的 js 文件中是不包含 css 内容的，因此通常情况下，你需要选择性地引入某一个版本的 js 资源（例如 `/bundle/antd-mobile.umd.js`），然后再额外引入 `/bundle/style.css`。
-
-你可以在 [unpkg](https://unpkg.com/browse/antd-mobile@5/bundle/) 或 [jsdelivr](https://www.jsdelivr.com/package/npm/antd-mobile?path=bundle) 上找到这些文件。
diff --git a/package.json b/package.json
index f3b7ad716..b8c7e1343 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "antd-mobile",
-  "version": "5.19.0",
+  "version": "5.18.0",
   "scripts": {
     "start": "dumi dev",
     "build": "gulp",
diff --git a/src/components/passcode-input/index.ts b/src/components/passcode-input/index.ts
index 397c6ce76..33b182489 100644
--- a/src/components/passcode-input/index.ts
+++ b/src/components/passcode-input/index.ts
@@ -1,6 +1,6 @@
 import './passcode-input.less'
 import { PasscodeInput } from './passcode-input'
 
-export type { PasscodeInputProps, PasscodeInputRef } from './passcode-input'
+export type { PasscodeInputProps } from './passcode-input'
 
 export default PasscodeInput
diff --git a/src/components/swipe-action/index.en.md b/src/components/swipe-action/index.en.md
index cb74156b7..10fb74718 100644
--- a/src/components/swipe-action/index.en.md
+++ b/src/components/swipe-action/index.en.md
@@ -22,6 +22,7 @@ Swipe to reveal hidden function menus.
 | onAction            | Triggered when operation button is clicked                                           | `(action: Action, e: React.MouseEvent) => void` | -       |
 | rightActions        | List of operation buttons on the right                                               | `Action[]`                                      | `[]`    |
 | stopPropagation     | Stop the propagation of some events.                                                 | `PropagationEvent[]`                            | `[]`    |
+| onActionsReveal     | Triggered when the operation button appears completely                               | (side: 'left' \| 'right') => void               | -       |
 
 ### Action
 
diff --git a/src/components/swipe-action/index.zh.md b/src/components/swipe-action/index.zh.md
index 01c9fe0fe..39f7c874e 100644
--- a/src/components/swipe-action/index.zh.md
+++ b/src/components/swipe-action/index.zh.md
@@ -22,6 +22,7 @@
 | onAction            | 点击操作按钮时触发           | `(action: Action, e: React.MouseEvent) => void` | -      |
 | rightActions        | 右侧的操作按钮列表           | `Action[]`                                      | `[]`   |
 | stopPropagation     | 阻止某些事件的冒泡           | `PropagationEven[]`                             | `[]`   |
+| onActionsReveal     | 按钮完全出现时触发           | (side: 'left' \| 'right') => void               | -      |
 
 ### Action
 
diff --git a/src/components/swipe-action/swipe-action.tsx b/src/components/swipe-action/swipe-action.tsx
index 29dfa5e27..58f0b06f1 100644
--- a/src/components/swipe-action/swipe-action.tsx
+++ b/src/components/swipe-action/swipe-action.tsx
@@ -47,6 +47,7 @@ export type SwipeActionProps = {
   closeOnAction?: boolean
   children: ReactNode
   stopPropagation?: PropagationEvent[]
+  onActionsReveal?: (side: 'left' | 'right') => void
 } & NativeProps<'--background'>
 
 const defaultProps = {
@@ -102,9 +103,13 @@ export const SwipeAction = forwardRef<SwipeActionRef, SwipeActionProps>(
           } else {
             position = 0
           }
+          const targetX = nearest([-rightWidth, 0, leftWidth], position)
           api.start({
-            x: nearest([-rightWidth, 0, leftWidth], position),
+            x: targetX,
           })
+          if (targetX !== 0) {
+            p.onActionsReveal?.(targetX > 0 ? 'left' : 'right')
+          }
           window.setTimeout(() => {
             draggingRef.current = false
           })
@@ -149,6 +154,7 @@ export const SwipeAction = forwardRef<SwipeActionRef, SwipeActionProps>(
             x: getLeftWidth(),
           })
         }
+        p.onActionsReveal?.(side)
       },
       close,
     }))


Test patch:
diff --git a/src/components/passcode-input/tests/passcode-input.test.tsx b/src/components/passcode-input/tests/passcode-input.test.tsx
deleted file mode 100644
index 31261ca82..000000000
--- a/src/components/passcode-input/tests/passcode-input.test.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import React, { createRef } from 'react'
-import { render, screen, fireEvent, userEvent, act } from 'testing'
-import PasscodeInput, { PasscodeInputRef } from '..'
-import { NumberKeyboard } from 'antd-mobile'
-
-const classPrefix = 'adm-passcode-input'
-const cellClassPrefix = 'adm-passcode-input-cell'
-
-function getCells() {
-  return document.body.querySelectorAll(`.${classPrefix}-cell`)
-}
-
-function mockClick(el: HTMLElement) {
-  fireEvent.touchStart(el, { touches: [{}] })
-  fireEvent.touchEnd(el, { touches: [{}] })
-}
-
-describe('PasscodeInput', () => {
-  test('basic usage', () => {
-    render(<PasscodeInput keyboard={<NumberKeyboard />} />)
-    const input = screen.getByRole('button', { name: '密码输入框' })
-    fireEvent.focus(input)
-    expect(input).toHaveClass(`${classPrefix}-focused`)
-
-    mockClick(screen.getByText('1'))
-    const cells = getCells()
-    expect(cells[0]).toHaveClass(`${cellClassPrefix}-dot`)
-    expect(cells[1]).toHaveClass(
-      `${cellClassPrefix}-caret ${cellClassPrefix}-focused`
-    )
-  })
-
-  test('plain should be work', () => {
-    render(<PasscodeInput plain keyboard={<NumberKeyboard />} />)
-    const input = screen.getByRole('button', { name: '密码输入框' })
-    fireEvent.focus(input)
-
-    mockClick(screen.getByText('1'))
-    const cells = getCells()
-    expect(cells[0]).not.toHaveClass(`${cellClassPrefix}-dot`)
-    expect(cells[0]).toHaveTextContent('1')
-  })
-
-  test('renders with error', () => {
-    render(<PasscodeInput error />)
-    const input = screen.getByRole('button', { name: '密码输入框' })
-    expect(input).toHaveClass(`${classPrefix}-error`)
-  })
-
-  test('seperated should be work', () => {
-    render(<PasscodeInput seperated keyboard={<NumberKeyboard />} />)
-    const input = screen.getByRole('button', { name: '密码输入框' })
-    expect(input).toHaveClass(`${classPrefix}-seperated`)
-    fireEvent.focus(input)
-
-    const cells = getCells()
-    expect(cells[0]).toHaveClass(
-      `${cellClassPrefix}-caret ${cellClassPrefix}-focused`
-    )
-
-    mockClick(screen.getByText('1'))
-    expect(cells[0]).toHaveClass(`${cellClassPrefix}-dot`)
-    expect(cells[1]).toHaveClass(
-      `${cellClassPrefix}-caret ${cellClassPrefix}-focused`
-    )
-  })
-
-  test('native keyboard should be work', async () => {
-    render(<PasscodeInput plain />)
-    const input = screen.getByRole('button', { name: '密码输入框' })
-    fireEvent.focus(input)
-    await userEvent.keyboard('abc')
-    expect(input).toHaveTextContent('abc')
-  })
-
-  test('event callbacks should be called', async () => {
-    const onBlur = jest.fn()
-    const onChange = jest.fn()
-    const onFill = jest.fn()
-    const onFocus = jest.fn()
-    render(
-      <PasscodeInput
-        length={4}
-        onBlur={onBlur}
-        onChange={onChange}
-        onFill={onFill}
-        onFocus={onFocus}
-      />
-    )
-
-    const input = screen.getByRole('button', { name: '密码输入框' })
-    fireEvent.focus(input)
-    expect(onFocus).toBeCalled()
-    await userEvent.keyboard('abcde')
-    expect(onFill).toBeCalled()
-    expect(onChange).toBeCalledTimes(4)
-    fireEvent.blur(input)
-    expect(onBlur).toBeCalled()
-  })
-
-  test('imperative handle', () => {
-    const ref = createRef<PasscodeInputRef>()
-    render(<PasscodeInput ref={ref} />)
-    const input = screen.getByRole('button', { name: '密码输入框' })
-    act(() => {
-      ref.current?.focus()
-    })
-    expect(input).toHaveClass(`${classPrefix}-focused`)
-    act(() => {
-      ref.current?.blur()
-    })
-    expect(input).not.toHaveClass(`${classPrefix}-focused`)
-  })
-})
diff --git a/src/components/swipe-action/tests/swipe-action.test.tsx b/src/components/swipe-action/tests/swipe-action.test.tsx
index 2268b804d..73f09ebd3 100644
--- a/src/components/swipe-action/tests/swipe-action.test.tsx
+++ b/src/components/swipe-action/tests/swipe-action.test.tsx
@@ -227,4 +227,59 @@ describe('SwipeAction', () => {
     fireEvent.click(getByText('delete'))
     expect(onAction).not.toBeCalled()
   })
+
+  test('onActionsReveal should be called when the ref.show is called', async () => {
+    const onActionsReveal = jest.fn()
+    const App = () => {
+      const ref = useRef<SwipeActionRef>(null)
+      return (
+        <>
+          <SwipeAction
+            ref={ref}
+            leftActions={leftActions}
+            onActionsReveal={onActionsReveal}
+          >
+            A
+          </SwipeAction>
+          <button onClick={() => ref.current?.show('left')}>left</button>
+        </>
+      )
+    }
+    const { getByText } = render(<App />)
+
+    fireEvent.click(getByText('left'))
+    await waitFor(() => {
+      expect(onActionsReveal).toBeCalledTimes(1)
+      expect(onActionsReveal).toBeCalledWith('left')
+    })
+  })
+
+  test('onActionsReveal should be called when the operation button is revealed', async () => {
+    const onActionsReveal = jest.fn()
+    const App = () => {
+      return (
+        <SwipeAction
+          rightActions={rightActions}
+          leftActions={leftActions}
+          onActionsReveal={onActionsReveal}
+          data-testid='swipe'
+        >
+          A
+        </SwipeAction>
+      )
+    }
+
+    const { getByTestId } = render(<App />)
+
+    swipe(getByTestId('swipe'), [
+      {
+        clientX: 150,
+      },
+    ])
+
+    await waitFor(() => {
+      expect(onActionsReveal).toBeCalledTimes(1)
+      expect(onActionsReveal).toBeCalledWith('left')
+    })
+  })
 })