From 175f5cb0a0668dd21e91706c8cd94ce7ad9ad130 Mon Sep 17 00:00:00 2001 From: heart <7362469@qq.com> Date: Thu, 11 Jan 2024 01:18:05 +0800 Subject: [PATCH 1/8] fix: fixed error report with no expected value in `expandColumnTitle` slot --- components/vc-table/hooks/useColumns.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/vc-table/hooks/useColumns.tsx b/components/vc-table/hooks/useColumns.tsx index d726de5f2a..85853a9358 100644 --- a/components/vc-table/hooks/useColumns.tsx +++ b/components/vc-table/hooks/useColumns.tsx @@ -179,7 +179,9 @@ function useColumns( class: `${prefixCls.value}-expand-icon-col`, columnType: 'EXPAND_COLUMN', }, - title: renderSlot(contextSlots.value, 'expandColumnTitle', {}, () => ['']), + title: contextSlots.value?.expandColumnTitle + ? renderSlot(contextSlots.value, 'expandColumnTitle', {}, () => ['']) + : null, fixed: fixedColumn, class: `${prefixCls.value}-row-expand-icon-cell`, width: expandColumnWidth.value, From ed249651ae348d4d89c6d785d07f76623cc44623 Mon Sep 17 00:00:00 2001 From: heart <7362469@qq.com> Date: Thu, 11 Jan 2024 01:25:43 +0800 Subject: [PATCH 2/8] fix: optimize optional chain --- components/vc-table/hooks/useColumns.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/vc-table/hooks/useColumns.tsx b/components/vc-table/hooks/useColumns.tsx index 85853a9358..1b2ad72305 100644 --- a/components/vc-table/hooks/useColumns.tsx +++ b/components/vc-table/hooks/useColumns.tsx @@ -179,7 +179,7 @@ function useColumns( class: `${prefixCls.value}-expand-icon-col`, columnType: 'EXPAND_COLUMN', }, - title: contextSlots.value?.expandColumnTitle + title: contextSlots.value.expandColumnTitle ? renderSlot(contextSlots.value, 'expandColumnTitle', {}, () => ['']) : null, fixed: fixedColumn, From bb9c644b156681e7e7ff70612112da5e44e70f2f Mon Sep 17 00:00:00 2001 From: heart <7362469@qq.com> Date: Fri, 12 Jan 2024 11:40:04 +0800 Subject: [PATCH 3/8] fix: use default render --- components/vc-table/hooks/useColumns.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/vc-table/hooks/useColumns.tsx b/components/vc-table/hooks/useColumns.tsx index 1b2ad72305..37f28be3d3 100644 --- a/components/vc-table/hooks/useColumns.tsx +++ b/components/vc-table/hooks/useColumns.tsx @@ -179,9 +179,7 @@ function useColumns( class: `${prefixCls.value}-expand-icon-col`, columnType: 'EXPAND_COLUMN', }, - title: contextSlots.value.expandColumnTitle - ? renderSlot(contextSlots.value, 'expandColumnTitle', {}, () => ['']) - : null, + title: contextSlots.value.expandColumnTitle?.(), fixed: fixedColumn, class: `${prefixCls.value}-row-expand-icon-cell`, width: expandColumnWidth.value, From 40c775980903be50f8f88eb0bb23ea4db8a547da Mon Sep 17 00:00:00 2001 From: heart <7362469@qq.com> Date: Sat, 13 Jan 2024 01:20:55 +0800 Subject: [PATCH 4/8] refactor: use `customRenderSlot` replace `renderSlot` --- components/_util/vnode.ts | 30 ++++++++++++++++++++++-- components/card/Card.tsx | 5 ++-- components/vc-table/Cell/index.tsx | 5 ++-- components/vc-table/hooks/useColumns.tsx | 4 ++-- 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/components/_util/vnode.ts b/components/_util/vnode.ts index e5ab010a42..741e8b9137 100644 --- a/components/_util/vnode.ts +++ b/components/_util/vnode.ts @@ -1,6 +1,6 @@ import { filterEmpty } from './props-util'; -import type { VNode, VNodeProps } from 'vue'; -import { cloneVNode, isVNode, render as VueRender } from 'vue'; +import type { Slots, VNode, VNodeArrayChildren, VNodeProps } from 'vue'; +import { cloneVNode, isVNode, Comment, Fragment, render as VueRender } from 'vue'; import warning from './warning'; import type { RefObject } from './createRef'; type NodeProps = Record & @@ -55,3 +55,29 @@ export function deepCloneElement( export function triggerVNodeUpdate(vm: VNode, attrs: Record, dom: any) { VueRender(cloneVNode(vm, { ...attrs }), dom); } + +export function customRenderSlot( + slots: Slots, + name: string, + props: Record, + fallback?: () => VNodeArrayChildren, +) { + const slot = slots[name]?.(props) || []; + + const ensureValidVNode = (slot: VNodeArrayChildren) => { + return slot.some(child => { + if (!isVNode(child)) return true; + if (child.type === Comment) return false; + if (child.type === Fragment && !ensureValidVNode(child.children as VNodeArrayChildren)) + return false; + return true; + }) + ? slot + : null; + }; + + if (ensureValidVNode(slot)) { + return slot; + } + return fallback?.(); +} diff --git a/components/card/Card.tsx b/components/card/Card.tsx index 18d330cf0f..ce95c655af 100644 --- a/components/card/Card.tsx +++ b/components/card/Card.tsx @@ -1,5 +1,5 @@ import type { VNodeTypes, PropType, VNode, ExtractPropTypes, CSSProperties } from 'vue'; -import { isVNode, defineComponent, renderSlot } from 'vue'; +import { isVNode, defineComponent } from 'vue'; import Tabs from '../tabs'; import PropTypes from '../_util/vue-types'; import { flattenChildren, isEmptyElement, filterEmptyWithUndefined } from '../_util/props-util'; @@ -10,6 +10,7 @@ import devWarning from '../vc-util/devWarning'; import useStyle from './style'; import Skeleton from '../skeleton'; import type { CustomSlotsType } from '../_util/type'; +import { customRenderSlot } from '../_util/vnode'; export interface CardTabListType { key: string; tab: any; @@ -152,7 +153,7 @@ const Card = defineComponent({ `tabList slots is deprecated, Please use \`customTab\` instead.`, ); let tab = temp !== undefined ? temp : slots[name] ? slots[name](item) : null; - tab = renderSlot(slots, 'customTab', item as any, () => [tab]); + tab = customRenderSlot(slots, 'customTab', item as any, () => [tab]); return ; })} diff --git a/components/vc-table/Cell/index.tsx b/components/vc-table/Cell/index.tsx index 37fd51fbc3..636f1fd652 100644 --- a/components/vc-table/Cell/index.tsx +++ b/components/vc-table/Cell/index.tsx @@ -1,7 +1,7 @@ import classNames from '../../_util/classNames'; import { filterEmpty, flattenChildren, isValidElement } from '../../_util/props-util'; import type { CSSProperties, VNodeArrayChildren } from 'vue'; -import { Text, computed, defineComponent, isVNode, renderSlot } from 'vue'; +import { Text, computed, defineComponent, isVNode } from 'vue'; import type { DataIndex, @@ -23,6 +23,7 @@ import { useInjectSticky } from '../context/StickyContext'; import { warning } from '../../vc-util/warning'; import type { MouseEventHandler } from '../../_util/EventInterface'; import eagerComputed from '../../_util/eagerComputed'; +import { customRenderSlot } from 'ant-design-vue/es/_util/vnode'; /** Check if cell is in hover range */ function inHoverRange(cellStartRow: number, cellRowSpan: number, startRow: number, endRow: number) { @@ -223,7 +224,7 @@ export default defineComponent({ contextSlots.value.bodyCell && !column.slots?.customRender ) { - const child = renderSlot( + const child = customRenderSlot( contextSlots.value, 'bodyCell', { diff --git a/components/vc-table/hooks/useColumns.tsx b/components/vc-table/hooks/useColumns.tsx index 37f28be3d3..0b35caef06 100644 --- a/components/vc-table/hooks/useColumns.tsx +++ b/components/vc-table/hooks/useColumns.tsx @@ -1,6 +1,6 @@ import { warning } from '../../vc-util/warning'; import type { ComputedRef, Ref } from 'vue'; -import { renderSlot, computed, watchEffect } from 'vue'; +import { computed, watchEffect } from 'vue'; import type { ColumnsType, ColumnType, @@ -179,7 +179,7 @@ function useColumns( class: `${prefixCls.value}-expand-icon-col`, columnType: 'EXPAND_COLUMN', }, - title: contextSlots.value.expandColumnTitle?.(), + title: contextSlots.value.expandColumnTitle?.() || '', fixed: fixedColumn, class: `${prefixCls.value}-row-expand-icon-cell`, width: expandColumnWidth.value, From add90ff9d39e31eb9dcee6f34da713faf1d8b1c7 Mon Sep 17 00:00:00 2001 From: heart <7362469@qq.com> Date: Sat, 13 Jan 2024 01:32:38 +0800 Subject: [PATCH 5/8] style: code format --- components/_util/vnode.ts | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/components/_util/vnode.ts b/components/_util/vnode.ts index 741e8b9137..aa0e588ddd 100644 --- a/components/_util/vnode.ts +++ b/components/_util/vnode.ts @@ -56,6 +56,18 @@ export function triggerVNodeUpdate(vm: VNode, attrs: Record, dom: a VueRender(cloneVNode(vm, { ...attrs }), dom); } +const ensureValidVNode = (slot: VNodeArrayChildren) => { + return slot.some(child => { + if (!isVNode(child)) return true; + if (child.type === Comment) return false; + if (child.type === Fragment && !ensureValidVNode(child.children as VNodeArrayChildren)) + return false; + return true; + }) + ? slot + : null; +}; + export function customRenderSlot( slots: Slots, name: string, @@ -63,19 +75,6 @@ export function customRenderSlot( fallback?: () => VNodeArrayChildren, ) { const slot = slots[name]?.(props) || []; - - const ensureValidVNode = (slot: VNodeArrayChildren) => { - return slot.some(child => { - if (!isVNode(child)) return true; - if (child.type === Comment) return false; - if (child.type === Fragment && !ensureValidVNode(child.children as VNodeArrayChildren)) - return false; - return true; - }) - ? slot - : null; - }; - if (ensureValidVNode(slot)) { return slot; } From c67eb2fb39072bfdbcf25b1e14de4107d0ca70d0 Mon Sep 17 00:00:00 2001 From: heart <7362469@qq.com> Date: Sat, 13 Jan 2024 01:38:15 +0800 Subject: [PATCH 6/8] perf: optimize useColumns code --- components/card/Card.tsx | 1 + components/vc-table/hooks/useColumns.tsx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/card/Card.tsx b/components/card/Card.tsx index ce95c655af..a5f83c8496 100644 --- a/components/card/Card.tsx +++ b/components/card/Card.tsx @@ -11,6 +11,7 @@ import useStyle from './style'; import Skeleton from '../skeleton'; import type { CustomSlotsType } from '../_util/type'; import { customRenderSlot } from '../_util/vnode'; + export interface CardTabListType { key: string; tab: any; diff --git a/components/vc-table/hooks/useColumns.tsx b/components/vc-table/hooks/useColumns.tsx index 0b35caef06..b9eac5aa52 100644 --- a/components/vc-table/hooks/useColumns.tsx +++ b/components/vc-table/hooks/useColumns.tsx @@ -14,6 +14,7 @@ import type { import { INTERNAL_COL_DEFINE } from '../utils/legacyUtil'; import { EXPAND_COLUMN } from '../constant'; import { useInjectSlots } from '../../table/context'; +import { customRenderSlot } from 'ant-design-vue/es/_util/vnode'; function flatColumns(columns: ColumnsType): ColumnType[] { return columns.reduce((list, column) => { @@ -179,7 +180,7 @@ function useColumns( class: `${prefixCls.value}-expand-icon-col`, columnType: 'EXPAND_COLUMN', }, - title: contextSlots.value.expandColumnTitle?.() || '', + title: customRenderSlot(contextSlots.value, 'expandColumnTitle', {}, () => ['']), fixed: fixedColumn, class: `${prefixCls.value}-row-expand-icon-cell`, width: expandColumnWidth.value, From b18edbd0a39fe4dfff44c00cfb694d37f5758065 Mon Sep 17 00:00:00 2001 From: heart <7362469@qq.com> Date: Sat, 13 Jan 2024 08:30:31 +0800 Subject: [PATCH 7/8] fix: fix path --- components/vc-table/hooks/useColumns.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/vc-table/hooks/useColumns.tsx b/components/vc-table/hooks/useColumns.tsx index b9eac5aa52..811c1e24ef 100644 --- a/components/vc-table/hooks/useColumns.tsx +++ b/components/vc-table/hooks/useColumns.tsx @@ -14,7 +14,7 @@ import type { import { INTERNAL_COL_DEFINE } from '../utils/legacyUtil'; import { EXPAND_COLUMN } from '../constant'; import { useInjectSlots } from '../../table/context'; -import { customRenderSlot } from 'ant-design-vue/es/_util/vnode'; +import { customRenderSlot } from '../../_util/vnode'; function flatColumns(columns: ColumnsType): ColumnType[] { return columns.reduce((list, column) => { From 3eb39324e5f5ffd1d641c6e44a16728d412e37e8 Mon Sep 17 00:00:00 2001 From: heart <7362469@qq.com> Date: Sun, 14 Jan 2024 17:28:28 +0800 Subject: [PATCH 8/8] feat: add customRenderSlot unit test --- components/_util/__mocks__/RenderSlot.tsx | 11 ++++++++++ components/_util/__tests__/vNode.test.js | 26 +++++++++++++++++++++++ components/_util/vnode.ts | 6 +++--- 3 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 components/_util/__mocks__/RenderSlot.tsx create mode 100644 components/_util/__tests__/vNode.test.js diff --git a/components/_util/__mocks__/RenderSlot.tsx b/components/_util/__mocks__/RenderSlot.tsx new file mode 100644 index 0000000000..42e40ee0a9 --- /dev/null +++ b/components/_util/__mocks__/RenderSlot.tsx @@ -0,0 +1,11 @@ +import { defineComponent } from 'vue'; +import { customRenderSlot } from '../vnode'; + +export default defineComponent({ + name: 'RenderSlot', + setup(_props, { slots }) { + return () => { + return customRenderSlot(slots, 'default', {}, () => ['default value']); + }; + }, +}); diff --git a/components/_util/__tests__/vNode.test.js b/components/_util/__tests__/vNode.test.js new file mode 100644 index 0000000000..4bfc7e8fa9 --- /dev/null +++ b/components/_util/__tests__/vNode.test.js @@ -0,0 +1,26 @@ +import RenderSlot from '../__mocks__/RenderSlot'; +import { mount } from '@vue/test-utils'; +import { nextTick } from 'vue'; + +describe('render slot content', () => { + it('renders slot content', () => { + const wrapper = mount(RenderSlot, { + slots: { + default: () => 'This is slot content', + }, + }); + + expect(wrapper.html()).toContain('This is slot content'); + }); + + it('render default value when slot is fragment', async () => { + const wrapper = mount(RenderSlot, { + slots: { + default: () => <>, + }, + }); + + await nextTick(); + expect(wrapper.html()).toContain('default value'); + }); +}); diff --git a/components/_util/vnode.ts b/components/_util/vnode.ts index aa0e588ddd..8565cbe5ba 100644 --- a/components/_util/vnode.ts +++ b/components/_util/vnode.ts @@ -56,8 +56,8 @@ export function triggerVNodeUpdate(vm: VNode, attrs: Record, dom: a VueRender(cloneVNode(vm, { ...attrs }), dom); } -const ensureValidVNode = (slot: VNodeArrayChildren) => { - return slot.some(child => { +const ensureValidVNode = (slot: VNodeArrayChildren | null) => { + return (slot || []).some(child => { if (!isVNode(child)) return true; if (child.type === Comment) return false; if (child.type === Fragment && !ensureValidVNode(child.children as VNodeArrayChildren)) @@ -74,7 +74,7 @@ export function customRenderSlot( props: Record, fallback?: () => VNodeArrayChildren, ) { - const slot = slots[name]?.(props) || []; + const slot = slots[name]?.(props); if (ensureValidVNode(slot)) { return slot; }