Repository: Tencent/tdesign-mobile-vue
Pull request #753 (2023-05-31 07:44:05)

fix(Popup): resolved closeBtn invalid when using  slot
<!--
首先，感谢你的贡献！😄
请阅读并遵循 [TDesign 贡献指南](https://github.com/Tencent/tdesign/blob/main/docs/contributing.md)，填写以下 pull request 的信息。
PR 在维护者审核通过后会合并，谢谢！
-->

### 🤔 这个 PR 的性质是？

- [x] 日常 bug 修复
- [ ] 新特性提交
- [ ] 文档改进
- [ ] 演示代码改进
- [ ] 组件样式/交互改进
- [ ] CI/CD 改进
- [ ] 重构
- [ ] 代码风格优化
- [x] 测试用例
- [ ] 分支合并
- [ ] 其他

### 🔗 相关 Issue

<!--
1. 描述相关需求的来源，如相关的 issue 讨论链接。
-->

### 💡 需求背景和解决方案

<!--
1. 要解决的具体问题。
2. 列出最终的 API 实现和用法。
3. 涉及UI/交互变动需要有截图或 GIF。
-->

### 📝 更新日志

<!--
从用户角度描述具体变化，以及可能的 breaking change 和其他风险。
-->

- fix(Popup): 解决使用 `closeBtn` 插槽时关闭无效的问题
- test(Popup): 补充测试用例

- [ ] 本条 PR 不需要纳入 Changelog

### ☑️ 请求合并前的自查清单

⚠️ 请自检并全部**勾选全部选项**。⚠️

- [ ] 文档已补充或无须补充
- [ ] 代码演示已提供或无须提供
- [ ] TypeScript 定义已补充或无须补充
- [ ] Changelog 已提供或无须提供


github-actions[bot]: **Size Change:** +31 B (0%) 

**Total Size:** 469 kB

| Filename | Size | Change |
| :--- | :---: | :---: |
| `./dist/tdesign.js` | 276 kB | +15 B (0%) |
| `./dist/tdesign.min.js` | 133 kB | +16 B (0%) |

<details><summary>ℹ️ <strong>View Unchanged</strong></summary>

| Filename | Size |
| :--- | :---: |
| `./dist/tdesign.css` | 35.1 kB |
| `./dist/tdesign.min.css` | 25.5 kB |

</details>


github-actions[bot]: <a href="https://preview-pr753-tdesign-mobile-vue.surge.sh"><img height="96" alt="完成" src="https://user-images.githubusercontent.com/15634204/150816437-9f5bb788-cd67-4cbc-9897-b82d74e9aa65.png" /></a>
<!-- [工作流地址](https://github.com/Tencent/tdesign-mobile-vue/actions/runs/5130258465) -->
<!-- AUTO_PREVIEW_HOOK -->

Patch:
diff --git a/src/popup/popup.vue b/src/popup/popup.vue
index ba5160ee..f4ded0c0 100644
--- a/src/popup/popup.vue
+++ b/src/popup/popup.vue
@@ -8,7 +8,7 @@
         :style="rootStyles"
         @touchmove="handleMove"
       >
-        <div v-if="closeBtnNode" :class="`${name}__close`">
+        <div v-if="closeBtnNode" :class="`${name}__close`" @click="handleCloseClick">
           <t-node :content="closeBtnNode" />
         </div>
         <slot />
@@ -88,15 +88,15 @@ export default defineComponent({
 
     const closeBtnNode = computed(() =>
       renderTNode(currentInstance, 'closeBtn', {
-        defaultNode: h(CloseIcon, {
-          size: '24px',
-          onClick() {
-            setVisible(false);
-          },
-        }),
+        defaultNode: h(CloseIcon, { size: '24px' }),
       }),
     );
 
+    const handleCloseClick = (e: MouseEvent) => {
+      emitEvent('close', { e });
+      setVisible(false);
+    };
+
     const handleOverlayClick = (args: { e: MouseEvent }) => {
       const { e } = args;
       if (!props.closeOnOverlayClick) {
@@ -142,6 +142,7 @@ export default defineComponent({
       afterEnter,
       afterLeave,
       handleOverlayClick,
+      handleCloseClick,
       handleMove,
     };
   },


Test patch:
diff --git a/src/popup/__test__/index.test.jsx b/src/popup/__test__/index.test.jsx
index 8281a01a..7a31757c 100644
--- a/src/popup/__test__/index.test.jsx
+++ b/src/popup/__test__/index.test.jsx
@@ -2,6 +2,7 @@ import { config, mount } from '@vue/test-utils';
 import { describe, it, expect, vi } from 'vitest';
 import Popup from '../popup.vue';
 import { ref } from 'vue';
+import { CloseIcon } from 'tdesign-icons-vue-next';
 
 config.global.stubs = {
   teleport: true,
@@ -29,18 +30,42 @@ describe('popup', () => {
       expect(onOpen).toBeCalledTimes(1);
     });
 
-    it(':placement', async () => {
+    it(':closeBtn', async () => {
+      const icon = () => <CloseIcon />;
       const wrapper = mount(Popup, {
         props: {
-          placement: 'top',
+          closeBtn: true,
           visible: true,
         },
       });
-      expect(wrapper.find('.t-popup').classes()).toContain(`t-popup--${wrapper.vm.placement}`);
+      expect(wrapper.find('.t-popup__close').exists()).toBeTruthy();
+      // closeBtn = true, 使用默认 CloseIcon
+      expect(wrapper.findComponent(CloseIcon).exists()).toBeTruthy();
+
+      await wrapper.setProps({
+        closeBtn: false,
+      });
+      expect(wrapper.find('.t-popup__close').exists()).toBeFalsy();
+
+      const temp = '取消';
       await wrapper.setProps({
-        placement: 'center',
+        closeBtn: () => {
+          return temp;
+        },
+      });
+      expect(wrapper.find('.t-popup__close').exists()).toBeTruthy();
+      expect(wrapper.find('.t-popup__close').text()).toEqual(temp);
+    });
+
+    it(':placement', async () => {
+      const placementList = ['', 'top', 'left', 'right', 'bottom', 'center'];
+      placementList.forEach((placement, index) => {
+        const wrapper = mount(() => <Popup visible={true} placement={placement} />);
+        const $popup = wrapper.find('.t-popup');
+        if (placement) {
+          expect($popup.classes()).toContain(`t-popup--${placement}`);
+        }
       });
-      expect(wrapper.find('.t-popup').classes()).toContain(`t-popup--${wrapper.vm.placement}`);
     });
 
     it(':zIndex', async () => {
@@ -86,6 +111,7 @@ describe('popup', () => {
             <Popup
               placement="bottom"
               transition-name="slide-fade"
+              closeBtn={true}
               visible={visible.value}
               onVisibleChange={onVisibleChange}
               onOpen={open}
@@ -99,12 +125,17 @@ describe('popup', () => {
 
       const $popup = wrapper.find('.t-popup');
       const $overlay = wrapper.find('.t-overlay');
+      const $closeBtn = wrapper.find('.t-popup__close');
 
       await $overlay.trigger('click');
 
       expect(onVisibleChange).toBeCalledTimes(1);
       expect(close).toBeCalledTimes(1);
 
+      await $closeBtn.trigger('click');
+      expect(onVisibleChange).toBeCalledTimes(2);
+      expect(close).toBeCalledTimes(2);
+
       const $transition = wrapper.findAllComponents({ name: 'transition' }); // => 通过 `name` 找到 transition
       expect($transition).toHaveLength(2);