fix: fixed error with no expected value in expandColumnTitle slot (#7265)

* fix: fixed error report with no expected value in `expandColumnTitle` slot

* fix: optimize optional chain

* fix: use default render

* refactor: use `customRenderSlot` replace `renderSlot`

* style: code format

* perf: optimize useColumns code

* fix: fix path

* feat: add customRenderSlot unit test
This commit is contained in:
Carl Chen 2024-01-16 20:49:38 +08:00 committed by GitHub
parent c717473568
commit d870f3f8e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 74 additions and 8 deletions

View File

@ -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']);
};
},
});

View File

@ -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');
});
});

View File

@ -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<string, any> &
@ -55,3 +55,28 @@ export function deepCloneElement<T, U>(
export function triggerVNodeUpdate(vm: VNode, attrs: Record<string, any>, dom: any) {
VueRender(cloneVNode(vm, { ...attrs }), dom);
}
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))
return false;
return true;
})
? slot
: null;
};
export function customRenderSlot(
slots: Slots,
name: string,
props: Record<string, unknown>,
fallback?: () => VNodeArrayChildren,
) {
const slot = slots[name]?.(props);
if (ensureValidVNode(slot)) {
return slot;
}
return fallback?.();
}

View File

@ -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,8 @@ 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 +154,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 <TabPane tab={tab} key={item.key} disabled={item.disabled} />;
})}
</Tabs>

View File

@ -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<CellProps>({
contextSlots.value.bodyCell &&
!column.slots?.customRender
) {
const child = renderSlot(
const child = customRenderSlot(
contextSlots.value,
'bodyCell',
{

View File

@ -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,
@ -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 '../../_util/vnode';
function flatColumns<RecordType>(columns: ColumnsType<RecordType>): ColumnType<RecordType>[] {
return columns.reduce((list, column) => {
@ -179,7 +180,7 @@ function useColumns<RecordType>(
class: `${prefixCls.value}-expand-icon-col`,
columnType: 'EXPAND_COLUMN',
},
title: renderSlot(contextSlots.value, 'expandColumnTitle', {}, () => ['']),
title: customRenderSlot(contextSlots.value, 'expandColumnTitle', {}, () => ['']),
fixed: fixedColumn,
class: `${prefixCls.value}-row-expand-icon-cell`,
width: expandColumnWidth.value,