Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# v3.0.18

`2025-08-15`

- 🏡 chore: 升级icon库 (#3330)
- 🏡 chore: 发布taro下的样式按需插件
- 📖 docs: 更新介绍部分内容 (#3324)
- :sparkles: feat: Ellipsis校验越界不走缓存配置 (#3329)
- :sparkles: feat(price): 支持自定义颜色&数据原样输出 (#3328)
- :sparkles: feat(notify): 支持promise调用notice (#3319)
- :bug: fix(noticebar): 适配鸿蒙样式修复 (#3332)
- :bug: Fix icons svg (#3331)

# v3.0.17

`2025-08-01`
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nutui/nutui-react-taro",
"version": "3.0.18-cpp.beta.0",
"version": "3.0.18-cpp",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

版本号提升到 3.0.18-cpp,请同步校验发版信息一致性

  • 当前 package.json 使用了带渠道后缀的版本号 3.0.18-cpp,而 CHANGELOG 顶部条目为 v3.0.18。请确认是否需要在 CHANGELOG 或发布标签中体现 -cpp 渠道,以避免包版本与发布记录不一致。

可以用以下命令快速检查并统一:


🏁 Script executed:

#!/bin/bash
# 1) 检查 CHANGELOG 是否存在对应条目
rg -n "^# v3\.0\.18(\-cpp)?$" CHANGELOG.md

# 2) 如果要保留 -cpp 渠道,请在 CHANGELOG 顶部标题同步为 v3.0.18-cpp;否则请将 package.json 恢复为 3.0.18

Length of output: 56


请同步 package.jsonCHANGELOG.md 的版本号后缀

当前存在不一致:

  • package.json 第3行:"version": "3.0.18-cpp"
  • CHANGELOG.md 第1行:# v3.0.18

请根据发布渠道需求,二选一并保持一致:

  • 如果要保留 -cpp 渠道后缀:
    • 在 CHANGELOG.md 顶部,将 # v3.0.18 修改为 # v3.0.18-cpp
  • 如果无需渠道后缀:
    • 在 package.json 中,将版本回退为 "3.0.18"
🤖 Prompt for AI Agents
In package.json around lines 3 to 3, the version string ("3.0.18-cpp") and
CHANGELOG.md top heading ("# v3.0.18") differ; choose one of the two options and
make them consistent: either update CHANGELOG.md first line to "# v3.0.18-cpp"
to keep the -cpp release channel suffix, or change package.json version to
"3.0.18" to remove the suffix; apply the chosen change and verify both files now
contain the exact same version text.

"style": "dist/style.css",
"main": "dist/nutui.react.umd.js",
"module": "dist/es/packages/nutui.react.build.js",
Expand Down
18 changes: 15 additions & 3 deletions src/packages/input/input.taro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,28 @@ export const Input = forwardRef((props: Partial<TaroInputProps>, ref) => {
const inputRef = useRef<HTMLInputElement>(null)
const [active, setActive] = useState(false)

// 兼容H5和小程序获取原生input标签
const getNativeInput = () => {
if (Taro.getEnv() === 'WEB') {
const taroInputCoreEl = inputRef.current as HTMLElement
const inputEl = taroInputCoreEl.querySelector('input')
return inputEl
}
return inputRef.current
}

useImperativeHandle(ref, () => {
return {
clear: () => {
setValue('')
},
focus: () => {
inputRef.current?.focus()
const nativeInput = getNativeInput()
nativeInput?.focus()
},
blur: () => {
inputRef.current?.blur()
const nativeInput = getNativeInput()
nativeInput?.blur()
},
get nativeElement() {
return inputRef.current
Expand Down Expand Up @@ -183,7 +195,7 @@ export const Input = forwardRef((props: Partial<TaroInputProps>, ref) => {
placeholder === undefined ? locale.placeholder : placeholder
}
placeholderClass={`${classPrefix}-placeholder`}
disabled={disabled || readOnly}
disabled={disabled}
value={value}
focus={autoFocus || focus}
confirmType={confirmType}
Expand Down
87 changes: 61 additions & 26 deletions src/packages/popup/__tests__/popup.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,22 @@
import * as React from 'react'
import { render, fireEvent } from '@testing-library/react'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import '@testing-library/jest-dom'
import { Popup } from '../popup'

test('should change z-index when using z-index prop', () => {
const { container } = render(<Popup visible zIndex={99} />)
const element = container.querySelector('.nut-popup') as HTMLElement
expect(element.style.zIndex).toEqual('99')
test('renders without crashing', () => {
render(<Popup visible>Test Content</Popup>)
expect(screen.getByText('Test Content')).toBeInTheDocument()
})

test('prop overlay-class test', async () => {
const { container } = render(<Popup visible overlayClassName="testclas" />)
const overlay = container.querySelector('.nut-overlay') as HTMLElement
expect(overlay).toHaveClass('testclas')
})
test('opens and closes correctly', () => {
const { rerender } = render(<Popup visible={false}>Test Content</Popup>)

test('prop overlay-style test', async () => {
const { container } = render(
<Popup visible overlayStyle={{ color: 'red' }} />
)
const overlay = container.querySelector('.nut-overlay') as HTMLElement
expect(overlay).toHaveStyle({
color: 'red',
})
})
// Initially, it should not be visible
expect(screen.queryByText('Test Content')).not.toBeInTheDocument()

test('should lock scroll when showed', async () => {
const { rerender } = render(<Popup visible={false} />)
rerender(<Popup visible />)
expect(document.body.classList.contains('nut-overflow-hidden')).toBe(true)
// Rerender with visible true
rerender(<Popup visible>Test Content</Popup>)
expect(screen.getByText('Test Content')).toBeInTheDocument()
})

test('should not render overlay when overlay prop is false', () => {
Expand Down Expand Up @@ -91,6 +79,14 @@ test('pop description', () => {
expect(title).toHaveTextContent('副标题')
})

test('pop minHeight', () => {
const { container } = render(
<Popup minHeight="30%" visible position="bottom" />
)
const node = container.querySelector('.nut-popup') as HTMLElement
expect(node).toHaveStyle({ minHeight: '30%' })
})

test('should render close icon when using closeable prop', () => {
const { container } = render(<Popup visible closeable />)
const closeIcon = container.querySelector(
Expand Down Expand Up @@ -145,11 +141,15 @@ test('event click-title-right icon and keep overlay test ', () => {
expect(overlay2).toBeNull()
})

test('should emit open event when prop visible is set to true', () => {
test('should emit open event when prop visible is set to true', async () => {
const onOpen = vi.fn()
const { rerender } = render(<Popup visible={false} onOpen={onOpen} />)
rerender(<Popup visible onOpen={onOpen} />)
expect(onOpen).toBeCalled()
rerender(
<Popup visible onOpen={onOpen} closeOnOverlayClick>
test
</Popup>
)
await waitFor(() => expect(onOpen).toBeCalled())
})

test('event click-overlay test', async () => {
Expand All @@ -171,3 +171,38 @@ test('pop destroyOnClose', () => {
fireEvent.click(overlay)
expect(onClose).toBeCalled()
})

test('handles touch events correctly', () => {
const handleTouchStart = vi.fn()
const handleTouchMove = vi.fn()
const handleTouchEnd = vi.fn()

render(
<Popup
visible
resizable
position="bottom"
// minHeight="400px"
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
>
Test Content
</Popup>
)

const popup = document.body.querySelector('.nut-popup') as HTMLElement

// Simulate touch events
fireEvent.touchStart(popup, { touches: [{ pageY: 400 }] })
expect(handleTouchStart).toHaveBeenCalled()

fireEvent.touchMove(popup, { touches: [{ pageY: 50 }] })
expect(handleTouchMove).toHaveBeenCalled()

fireEvent.touchMove(popup, { touches: [{ pageY: 450 }] })
expect(handleTouchMove).toHaveBeenCalled()

fireEvent.touchEnd(popup)
expect(handleTouchEnd).toHaveBeenCalled()
})
37 changes: 33 additions & 4 deletions src/packages/popup/demos/h5/demo1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,53 @@ import React, { useState } from 'react'
import { Popup, Cell } from '@nutui/nutui-react'

const Demo = () => {
const [showIcon, setShowIcon] = useState(false)
const [showPopup, setShowPopup] = useState(false)
const [showPopupResiable, setShowPopupResiable] = useState(false)

return (
<>
<Cell
title="基础弹框"
onClick={() => {
setShowIcon(true)
setShowPopup(true)
}}
/>
<Cell
title="基础弹框:可上下滑动"
onClick={() => {
setShowPopupResiable(true)
}}
/>
<Popup
closeable
visible={showIcon}
visible={showPopup}
title="标题"
description="这里是副标题这是副标题"
position="bottom"
onClose={() => {
setShowIcon(false)
setShowPopup(false)
}}
/>
<Popup
closeable
resizable
minHeight="10%"
style={{ height: '60%' }}
visible={showPopupResiable}
title="上下滑动"
description="弹层区域滑动起来"
position="bottom"
onClose={() => {
setShowPopupResiable(false)
}}
onTouchMove={(height, e, direction) => {
console.log('onTouchMove', height, e, direction)
}}
onTouchStart={(height, e) => {
console.log('onTouchStart', height, e)
}}
onTouchEnd={(height, e) => {
console.log('onTouchEnd', height, e)
}}
/>
</>
Expand Down
1 change: 1 addition & 0 deletions src/packages/popup/demos/h5/demo2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const Demo2 = () => {
visible={showTop}
destroyOnClose
position="top"
resizable
onClose={() => {
setShowTop(false)
}}
Expand Down
37 changes: 33 additions & 4 deletions src/packages/popup/demos/taro/demo1.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,53 @@ import React, { useState } from 'react'
import { Popup, Cell } from '@nutui/nutui-react-taro'

const Demo = () => {
const [showIcon, setShowIcon] = useState(false)
const [showPopup, setShowPopup] = useState(false)
const [showPopupResiable, setShowPopupResiable] = useState(false)

return (
<>
<Cell
title="基础弹框"
onClick={() => {
setShowIcon(true)
setShowPopup(true)
}}
/>
<Cell
title="基础弹框:可上下滑动"
onClick={() => {
setShowPopupResiable(true)
}}
/>
<Popup
closeable
visible={showIcon}
visible={showPopup}
title="标题"
description="这里是副标题这是副标题"
position="bottom"
onClose={() => {
setShowIcon(false)
setShowPopup(false)
}}
/>
<Popup
closeable
resizable
minHeight="10%"
style={{ height: '60%' }}
visible={showPopupResiable}
title="上下滑动"
description="弹层区域滑动起来"
position="bottom"
onClose={() => {
setShowPopupResiable(false)
}}
onTouchMove={(height, e, direction) => {
console.log('onTouchMove', height, e, direction)
}}
onTouchStart={(height, e) => {
console.log('onTouchStart', height, e)
}}
onTouchEnd={(height, e) => {
console.log('onTouchEnd', height, e)
}}
/>
</>
Expand Down
10 changes: 8 additions & 2 deletions src/packages/popup/doc.en-US.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,25 @@ import { Popup } from '@nutui/nutui-react'
| closeable | whether to show the close button | `boolean` | `false` |
| closeIconPosition | close button position | `top-left` \| `top-right` \| `bottom-left` \| `bottom-right` | `top-right` |
| closeIcon | Custom Icon | `ReactNode` | `close` |
| resizable | Enable vertical resizing of the popup | `boolean` | `false` |
| minHeight | Minimum height of the popup | `string` | `26%` |
| left | The left of title | `ReactNode` | `-` |
| title | The center of title | `ReactNode` | `-` |
| top | The top of popup | `ReactNode` | `-` |
| description | The subtitle/description | `ReactNode` | `-` |
| destroyOnClose | Whether to close after the component is destroyed | `boolean` | `false` |
| round | Whether to show rounded corners | `boolean` | `false` |
| portal | Mount the specified node | `HTMLElement` \| `(() => HTMLElement)` \| null` | `null` |
| afterShow | afterShow from `Overlay`, Fired when the mask opening animation ends | `event: HTMLElement` | `-` |
| afterClose | afterClose from `Overlay`, Fired when the mask closing animation ends | `event: HTMLElement` | `-` |
| onClick | Triggered when the popup is clicked | `event: MouseEvent` | `-` |
| onCloseIconClick | Fired when the close icon is clicked | `event: MouseEvent` | `-` |
| onOpen | Triggered when the popup is opened | `-` | `-` |
| onClose | Fired when the popup is closed | `-` | `-` |
| afterShow | afterShow from `Overlay`, Fired when the mask opening animation ends | `event: HTMLElement` | `-` |
| afterClose | afterClose from `Overlay`, Fired when the mask closing animation ends | `event: HTMLElement` | `-` |
| onOverlayClick | Click on the mask to trigger | `event: MouseEvent` | `-` |
| onTouchStart | triggered when starting to touch | `(height: number, event: TouchEvent<HTMLDivElement>) => void` | `-` |
| onTouchMove | triggered while moving | `(height: number, event: TouchEvent<HTMLDivElement>, direction: 'up' \| 'down') => void` | `-` |
| onTouchEnd | triggered when finishing to touch | `(height: number, event: TouchEvent<HTMLDivElement>) => void` | `-` |

## Theming

Expand Down
10 changes: 8 additions & 2 deletions src/packages/popup/doc.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,19 +87,25 @@ import { Popup } from '@nutui/nutui-react'
| closeable | 是否显示关闭按钮 | `boolean` | `false` |
| closeIconPosition | 关闭按钮位置 | `top-left` \| `top-right` \| `bottom-left` \| `bottom-right` | `top-right` |
| closeIcon | 自定义 Icon | `ReactNode` | `close` |
| resizable | 上下滑动调整高度,当前只支持从底部弹出 | `boolean` | `false` |
| minHeight | 设置最小高度 | `string` | `26%` |
| left | 标题左侧部分 | `ReactNode` | `-` |
| title | 标题中间部分 | `ReactNode` | `-` |
| top | 顶部占位 | `ReactNode` | `-` |
| description | 子标题/描述部分 | `ReactNode` | `-` |
| destroyOnClose | 组件不可见时,卸载内容 | `boolean` | `false` |
| round | 是否显示圆角 | `boolean` | `false` |
| portal | 指定节点挂载 | `HTMLElement` \| `(() => HTMLElement)` \| null` | `null` |
| afterShow | 继承于`Overlay`, 遮罩打开动画结束时触发 | `event: HTMLElement` | `-` |
| afterClose | 继承于`Overlay`, 遮罩关闭动画结束时触发 | `event: HTMLElement` | `-` |
| onClick | 点击弹框时触发 | `event: MouseEvent` | `-` |
| onCloseIconClick | 点击关闭图标时触发 | `event: MouseEvent` | `-` |
| onOpen | 打开弹框时触发 | `-` | `-` |
| onClose | 关闭弹框时触发 | `-` | `-` |
| afterShow | 继承于`Overlay`, 遮罩打开动画结束时触发 | `event: HTMLElement` | `-` |
| afterClose | 继承于`Overlay`, 遮罩关闭动画结束时触发 | `event: HTMLElement` | `-` |
| onOverlayClick | 点击遮罩触发 | `event: MouseEvent` | `-` |
| onTouchStart | 开始触碰时触发 | `(height: number, event: TouchEvent<HTMLDivElement>) => void` | `-` |
| onTouchMove | 滑动时触发 | `(height: number, event: TouchEvent<HTMLDivElement>, direction: 'up' \| 'down') => void` | `-` |
| onTouchEnd | 结束触碰时触发 | `(height: number, event: TouchEvent<HTMLDivElement>) => void` | `-` |

## 主题定制

Expand Down
10 changes: 8 additions & 2 deletions src/packages/popup/doc.taro.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,25 @@ import { Popup } from '@nutui/nutui-react-taro'
| closeable | 是否显示关闭按钮 | `boolean` | `false` |
| closeIconPosition | 关闭按钮位置 | `top-left` \| `top-right` \| `bottom-left` \| `bottom-right` | `top-right` |
| closeIcon | 自定义 Icon | `ReactNode` | `close` |
| resizable | 上下滑动调整高度,当前只支持从底部弹出 | `boolean` | `false` |
| minHeight | 设置最小高度 | `string` | `26%` |
| left | 标题左侧部分 | `ReactNode` | `-` |
| title | 标题中间部分 | `ReactNode` | `-` |
| top | 頂部佔位 | `ReactNode` | `-` |
| description | 子标题/描述部分 | `ReactNode` | `-` |
| destroyOnClose | 组件不可见时,卸载内容 | `boolean` | `false` |
| round | 是否显示圆角 | `boolean` | `false` |
| portal | 指定节点挂载 | ``HTMLElement` \| `(() => HTMLElement)` \| null`` | `null` |
| afterShow | 继承于`Overlay`, 遮罩打开动画结束时触发 | `event: HTMLElement` | `-` |
| afterClose | 继承于`Overlay`, 遮罩关闭动画结束时触发 | `event: HTMLElement` | `-` |
Comment on lines +109 to +110
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

afterShow/afterClose 的事件类型在 Taro 多端需再确认

如果在小程序端没有 HTMLElement 概念,建议注明“仅 H5 返回 HTMLElement”,或改为更通用的 unknown/不带参,并在示例中说明。

可用脚本辅助核对 Overlay 的类型定义来源:


🏁 Script executed:

#!/bin/bash
# 搜索 Overlay 的 afterShow/afterClose 类型定义
rg -nP "(afterShow|afterClose)\s*[:(]" -C2 src | sed -n '1,120p'

Length of output: 1111


修正 afterShow/afterClose 参数类型

根据 src/types/spec/overlay/base.ts 中的定义,afterShowafterClose 均为无参回调:

afterShow: () => void  
afterClose: () => void  

文档中使用了 event: HTMLElement 与代码实现不符,需将类型改为无参并移除 HTMLElement 参数。

请在 src/packages/popup/doc.taro.md 中做如下更新:

• 文件:src/packages/popup/doc.taro.md
位置:约第 109–110 行

原有表格行:

-| afterShow  | 继承于`Overlay`, 遮罩打开动画结束时触发 | `event: HTMLElement` | `-` |
-| afterClose | 继承于`Overlay`, 遮罩关闭动画结束时触发 | `event: HTMLElement` | `-` |

建议修改为:

| afterShow  | 继承于`Overlay`,遮罩打开动画结束时触发        | `() => void` | `-` |
| afterClose | 继承于`Overlay`,遮罩关闭动画结束时触发        | `() => void` | `-` |

若未来仅 H5 端支持获取 HTMLElement,可在示例或备注中说明“仅 H5 支持传入或获取对应 DOM 元素”。

🤖 Prompt for AI Agents
In src/packages/popup/doc.taro.md around lines 109–110, the docs list afterShow
and afterClose as taking an `event: HTMLElement` but the actual types in
src/types/spec/overlay/base.ts define them as no-argument callbacks; update the
two table rows so the type column shows `() => void` (remove `event:
HTMLElement`) and adjust the description to indicate they are parameterless
callbacks; optionally add a short note or example remark that only H5 may
provide a DOM element if applicable.

| onClick | 点击弹框时触发 | `event: MouseEvent` | `-` |
| onCloseIconClick | 点击关闭图标时触发 | `event: MouseEvent` | `-` |
| onOpen | 打开弹框时触发 | `-` | `-` |
| onClose | 关闭弹框时触发 | `-` | `-` |
| afterShow | 继承于`Overlay`, 遮罩打开动画结束时触发 | `event: HTMLElement` | `-` |
| afterClose | 继承于`Overlay`, 遮罩关闭动画结束时触发 | `event: HTMLElement` | `-` |
| onOverlayClick | 点击遮罩触发 | `event: MouseEvent` | `-` |
| onTouchStart | 开始触碰时触发 | `(height: number, event: ITouchEvent) => void` | `-` |
| onTouchMove | 滑动时触发 | `(height: number, event: ITouchEvent, direction: 'up' \| 'down') => void` | `-` |
| onTouchEnd | 结束触碰时触发 | `(height: number, event: ITouchEvent) => void` | `-` |

## 主题定制

Expand Down
Loading
Loading