From b9d0231736a20544aa240e3b97e6d2723532ec40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=8E=E4=BC=9F=E6=9D=B0?= <674416404@qq.com> Date: Tue, 3 Dec 2024 15:27:41 +0800 Subject: [PATCH] fix(dialog): fix cancelBtn and confirmBtn slot not work --- src/dialog/__test__/index.test.jsx | 29 ++++++++++++++- src/dialog/dialog.en-US.md | 16 ++++----- src/dialog/dialog.md | 26 +++++++------- src/dialog/dialog.tsx | 58 +++++++++++++++++++----------- src/dialog/props.ts | 2 +- src/dialog/type.ts | 10 ++++-- 6 files changed, 94 insertions(+), 47 deletions(-) diff --git a/src/dialog/__test__/index.test.jsx b/src/dialog/__test__/index.test.jsx index b9024b6ce..c767e2058 100644 --- a/src/dialog/__test__/index.test.jsx +++ b/src/dialog/__test__/index.test.jsx @@ -183,7 +183,34 @@ describe('dialog', () => { expect(onClose).toHaveBeenCalledWith({ e: expect.any(MouseEvent), trigger: 'overlay' }); }); }); - + describe('slots', () => { + it(':confirmBtn', async () => { + const visible = true; + const confirmBtn = 'confirm'; + const wrapper = mount(Dialog, { + props: { + visible, + }, + slots: { + confirmBtn: `${confirmBtn}`, + }, + }); + expect(wrapper.find('.slot-confirm-btn').text()).toBe(confirmBtn); + }); + it(':cancelBtn', async () => { + const visible = true; + const cancelBtn = 'cancel'; + const wrapper = mount(Dialog, { + props: { + visible, + }, + slots: { + cancelBtn: `${cancelBtn}`, + }, + }); + expect(wrapper.find('.slot-cancel-btn').text()).toBe(cancelBtn); + }); + }); describe('event', () => { it(':cancel && confirm && close', async () => { const visible = true; diff --git a/src/dialog/dialog.en-US.md b/src/dialog/dialog.en-US.md index 639de437e..095c9a37b 100644 --- a/src/dialog/dialog.en-US.md +++ b/src/dialog/dialog.en-US.md @@ -7,13 +7,14 @@ name | type | default | description | required -- | -- | -- | -- | -- actions | Array / Slot / Function | - | Typescript:`Array`,[Button API Documents](./button?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dialog/type.ts) | N -buttonLayout | String | horizontal | options:horizontal/vertical | N +buttonLayout | String | horizontal | options: horizontal/vertical | N cancelBtn | String / Object / Slot / Function | - | Typescript:`string \| ButtonProps \| TNode \| null`,[Button API Documents](./button?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dialog/type.ts) | N -closeOnOverlayClick | Boolean | undefined | \- | N +closeBtn | Boolean | false | \- | N +closeOnOverlayClick | Boolean | false | \- | N confirmBtn | String / Object / Slot / Function | - | Typescript:`string \| ButtonProps \| TNode \| null`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N content | String / Slot / Function | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N destroyOnClose | Boolean | false | \- | N -overlayProps | Object | {} | \- | N +overlayProps | Object | {} | Typescript:`OverlayProps`,[Overlay API Documents](./overlay?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dialog/type.ts) | N preventScrollThrough | Boolean | true | \- | N showOverlay | Boolean | true | \- | N title | String / Slot / Function | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N @@ -32,6 +33,7 @@ name | params | description -- | -- | -- cancel | `(context: { e: MouseEvent })` | \- close | `(context: DialogCloseContext)` | [see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dialog/type.ts)。`type DialogEventSource = 'cancel' \| 'overlay'``interface DialogCloseContext { trigger: DialogEventSource; e: MouseEvent }` +closed | \- | \- confirm | `(context: { e: MouseEvent })` | \- overlay-click | `(context: { e: MouseEvent })` | \- @@ -70,8 +72,6 @@ name | params | default | description -- | -- | -- | -- options | \- | - | Typescript:`DialogOptions` -插件返回值:`DialogInstance` - ### DialogPlugin.alert 同时也支持 `this.$dialog.alert`。 @@ -80,10 +80,8 @@ name | params | default | description -- | -- | -- | -- options | Object | - | Typescript:`Omit` -插件返回值:`DialogInstance` - - ### CSS Variables + The component provides the following CSS variables, which can be used to customize styles. Name | Default Value | Description -- | -- | -- @@ -97,4 +95,4 @@ Name | Default Value | Description --td-dialog-title-color | @font-gray-1 | - --td-dialog-title-font-size | 18px | - --td-dialog-title-line-height | 26px | - ---td-dialog-width | 311px | - +--td-dialog-width | 311px | - \ No newline at end of file diff --git a/src/dialog/dialog.md b/src/dialog/dialog.md index a404648b1..6ceb63a1e 100644 --- a/src/dialog/dialog.md +++ b/src/dialog/dialog.md @@ -1,19 +1,20 @@ :: BASE_DOC :: ## API + ### Dialog Props -名称 | 类型 | 默认值 | 说明 | 必传 +名称 | 类型 | 默认值 | 描述 | 必传 -- | -- | -- | -- | -- actions | Array / Slot / Function | - | 操作栏。TS 类型:`Array`,[Button API Documents](./button?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dialog/type.ts) | N buttonLayout | String | horizontal | 多按钮排列方式。可选项:horizontal/vertical | N cancelBtn | String / Object / Slot / Function | - | 取消按钮,可自定义。值为 null 则不显示取消按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。使用 TNode 自定义按钮时,需自行控制取消事件。TS 类型:`string \| ButtonProps \| TNode \| null`,[Button API Documents](./button?tab=api)。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dialog/type.ts) | N closeBtn | Boolean | false | 多按钮排列方式。可选项:true/false | N -closeOnOverlayClick | Boolean | undefined | 点击蒙层时是否触发关闭事件 | N +closeOnOverlayClick | Boolean | false | 点击蒙层时是否触发关闭事件 | N confirmBtn | String / Object / Slot / Function | - | 确认按钮。值为 null 则不显示确认按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。使用 TNode 自定义按钮时,需自行控制确认事件。TS 类型:`string \| ButtonProps \| TNode \| null`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N content | String / Slot / Function | - | 内容。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N destroyOnClose | Boolean | false | 是否在关闭弹框的时候销毁子元素 | N -overlayProps | Object | {} | 透传至 Overlay 组件 | N +overlayProps | Object | {} | 透传至 Overlay 组件。TS 类型:`OverlayProps`,[Overlay API Documents](./overlay?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dialog/type.ts) | N preventScrollThrough | Boolean | true | 防止滚动穿透 | N showOverlay | Boolean | true | 是否显示遮罩层 | N title | String / Slot / Function | - | 标题。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N @@ -22,7 +23,7 @@ width | String / Number | - | 对话框宽度,示例:320, '500px', '80%' | N zIndex | Number | - | 对话框层级,Web 侧样式默认为 2500,移动端和小程序样式默认为 1500 | N onCancel | Function | | TS 类型:`(context: { e: MouseEvent }) => void`如果“取消”按钮存在,则点击“取消”按钮时触发,同时触发关闭事件 | N onClose | Function | | TS 类型:`(context: DialogCloseContext) => void`关闭事件,点击 取消按钮 或 点击蒙层 时触发。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dialog/type.ts)。`type DialogEventSource = 'cancel' \| 'overlay'``interface DialogCloseContext { trigger: DialogEventSource; e: MouseEvent }` | N -onClosed | Function | | TS 类型:`() => void`组件关闭且动画结束后执行。 | N +onClosed | Function | | TS 类型:`() => void`对话框消失动画效果结束后触发 | N onConfirm | Function | | TS 类型:`(context: { e: MouseEvent }) => void`如果“确认”按钮存在,则点击“确认”按钮时触发 | N onOverlayClick | Function | | TS 类型:`(context: { e: MouseEvent }) => void`如果蒙层存在,点击蒙层时触发 | N @@ -32,12 +33,13 @@ onOverlayClick | Function | | TS 类型:`(context: { e: MouseEvent }) => void -- | -- | -- cancel | `(context: { e: MouseEvent })` | 如果“取消”按钮存在,则点击“取消”按钮时触发,同时触发关闭事件 close | `(context: DialogCloseContext)` | 关闭事件,点击 取消按钮 或 点击蒙层 时触发。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dialog/type.ts)。`type DialogEventSource = 'cancel' \| 'overlay'``interface DialogCloseContext { trigger: DialogEventSource; e: MouseEvent }` +closed | \- | 对话框消失动画效果结束后触发 confirm | `(context: { e: MouseEvent })` | 如果“确认”按钮存在,则点击“确认”按钮时触发 overlay-click | `(context: { e: MouseEvent })` | 如果蒙层存在,点击蒙层时触发 ### DialogOptions -名称 | 类型 | 默认值 | 说明 | 必传 +名称 | 类型 | 默认值 | 描述 | 必传 -- | -- | -- | -- | -- className | String | - | 弹框类名,示例:'t-class-dialog-first t-class-dialog-second' | N style | String / Object | - | 弹框 style 属性,输入 [CSSStyleDeclaration.cssText](https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleDeclaration/cssText)。TS 类型:`string \| Styles`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N @@ -56,7 +58,7 @@ update | `(props: DialogOptions)` | \- | 必需。更新弹框内容 同时也支持 `this.$dialog`。 -参数名称 | 参数类型 | 参数默认值 | 参数说明 +参数名称 | 参数类型 | 参数默认值 | 参数描述 -- | -- | -- | -- options | \- | - | TS 类型:`DialogOptions` @@ -66,24 +68,20 @@ options | \- | - | TS 类型:`DialogOptions` 同时也支持 `this.$dialog.confirm`。 -参数名称 | 参数类型 | 参数默认值 | 参数说明 +参数名称 | 参数类型 | 参数默认值 | 参数描述 -- | -- | -- | -- options | \- | - | TS 类型:`DialogOptions` -插件返回值:`DialogInstance` - ### DialogPlugin.alert 同时也支持 `this.$dialog.alert`。 -参数名称 | 参数类型 | 参数默认值 | 参数说明 +参数名称 | 参数类型 | 参数默认值 | 参数描述 -- | -- | -- | -- options | Object | - | TS 类型:`Omit` -插件返回值:`DialogInstance` - - ### CSS Variables + 组件提供了下列 CSS 变量,可用于自定义样式。 名称 | 默认值 | 描述 -- | -- | -- @@ -97,4 +95,4 @@ options | Object | - | TS 类型:`Omit` --td-dialog-title-color | @font-gray-1 | - --td-dialog-title-font-size | 18px | - --td-dialog-title-line-height | 26px | - ---td-dialog-width | 311px | - +--td-dialog-width | 311px | - \ No newline at end of file diff --git a/src/dialog/dialog.tsx b/src/dialog/dialog.tsx index 5958031a4..f10fd0843 100644 --- a/src/dialog/dialog.tsx +++ b/src/dialog/dialog.tsx @@ -2,6 +2,7 @@ import { CloseIcon } from 'tdesign-icons-vue-next'; import { computed, defineComponent } from 'vue'; import get from 'lodash/get'; import isString from 'lodash/isString'; +import isObject from 'lodash/isObject'; import TButton, { ButtonProps } from '../button'; import TPopup from '../popup'; @@ -9,12 +10,12 @@ import config from '../config'; import props from './props'; import { useTNodeJSX, useContent } from '../hooks/tnode'; import { usePrefixClass } from '../hooks/useClass'; +import { TdDialogProps } from './type'; const { prefix } = config; export default defineComponent({ name: `${prefix}-dialog`, - components: { TPopup, TButton, CloseIcon }, props, emits: ['update:visible', 'confirm', 'overlay-click', 'cancel', 'close', 'closed'], setup(props, context) { @@ -77,20 +78,31 @@ export default defineComponent({ context.emit('overlay-click', { e }); }; - const calcBtn = (btn: any) => (isString(btn) ? { content: btn } : btn); - const confirmBtnProps = computed(() => ({ + const calcBtn = (btn: TdDialogProps['cancelBtn'] | TdDialogProps['confirmBtn']) => { + if (isString(btn)) { + return { content: btn }; + } + + if (isObject(btn)) { + return btn; + } + + return {}; + }; + + const confirmBtnProps = computed(() => ({ theme: 'primary', ...calcBtn(props.confirmBtn), })); + const cancelBtnProps = computed(() => ({ theme: isTextStyleBtn.value ? 'default' : 'light', ...calcBtn(props.cancelBtn), })); + const actionsBtnProps = computed(() => props.actions?.map((item) => calcBtn(item))); return () => { - const { visible, showOverlay, overlayProps, preventScrollThrough, destroyOnClose, zIndex, closeBtn, actions } = - props; const renderTitleNode = () => { const titleNode = renderTNodeJSX('title'); if (!titleNode) { @@ -114,43 +126,49 @@ export default defineComponent({ const actionsNode = renderTNodeJSX('actions'); if (actionsNode && actionsBtnProps.value) { return actionsBtnProps.value.map((item, index) => ( - + )); } return null; }; const renderCancelBtnNode = () => { const cancelBtnNode = renderTNodeJSX('cancelBtn'); - if (!actions && cancelBtnNode) { - return ; + if (!props.actions && cancelBtnNode) { + if (context.slots.cancelBtn) { + return cancelBtnNode; + } + return ; } return null; }; const renderConfirmBntNode = () => { const confirmBtnNode = renderTNodeJSX('confirmBtn'); - if (!actions && confirmBtnNode) { - return ; + if (!props.actions && confirmBtnNode) { + if (context.slots.confirmBtn) { + return confirmBtnNode; + } + return ; } return null; }; return ( - {renderTNodeJSX('top')} - {closeBtn && ( + {props.closeBtn && ( - + )} @@ -164,7 +182,7 @@ export default defineComponent({ {renderConfirmBntNode()} - + ); }; }, diff --git a/src/dialog/props.ts b/src/dialog/props.ts index ce73affc7..6d2127c44 100644 --- a/src/dialog/props.ts +++ b/src/dialog/props.ts @@ -78,7 +78,7 @@ export default { onCancel: Function as PropType, /** 关闭事件,点击 取消按钮 或 点击蒙层 时触发 */ onClose: Function as PropType, - /** 组件关闭且动画结束后执行 */ + /** 对话框消失动画效果结束后触发 */ onClosed: Function as PropType, /** 如果“确认”按钮存在,则点击“确认”按钮时触发 */ onConfirm: Function as PropType, diff --git a/src/dialog/type.ts b/src/dialog/type.ts index 29ea94b55..0f78e4909 100644 --- a/src/dialog/type.ts +++ b/src/dialog/type.ts @@ -5,6 +5,7 @@ * */ import { ButtonProps } from '../button'; +import { OverlayProps } from '../overlay'; import { TNode, Styles } from '../common'; export interface TdDialogProps { @@ -21,6 +22,11 @@ export interface TdDialogProps { * 取消按钮,可自定义。值为 null 则不显示取消按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。使用 TNode 自定义按钮时,需自行控制取消事件 */ cancelBtn?: string | ButtonProps | TNode | null; + /** + * 多按钮排列方式。可选项:true/false + * @default false + */ + closeBtn?: boolean; /** * 点击蒙层时是否触发关闭事件 */ @@ -42,7 +48,7 @@ export interface TdDialogProps { * 透传至 Overlay 组件 * @default {} */ - overlayProps?: object; + overlayProps?: OverlayProps; /** * 防止滚动穿透 * @default true @@ -78,7 +84,7 @@ export interface TdDialogProps { */ onClose?: (context: DialogCloseContext) => void; /** - * 组件关闭且动画结束后执行 + * 对话框消失动画效果结束后触发 */ onClosed?: () => void; /**