Browse Source

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
pull/7302/head
Carl Chen 10 months ago committed by GitHub
parent
commit
d870f3f8e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      components/_util/__mocks__/RenderSlot.tsx
  2. 26
      components/_util/__tests__/vNode.test.js
  3. 29
      components/_util/vnode.ts
  4. 6
      components/card/Card.tsx
  5. 5
      components/vc-table/Cell/index.tsx
  6. 5
      components/vc-table/hooks/useColumns.tsx

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

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

29
components/_util/vnode.ts

@ -1,6 +1,6 @@
import { filterEmpty } from './props-util'; import { filterEmpty } from './props-util';
import type { VNode, VNodeProps } from 'vue'; import type { Slots, VNode, VNodeArrayChildren, VNodeProps } from 'vue';
import { cloneVNode, isVNode, render as VueRender } from 'vue'; import { cloneVNode, isVNode, Comment, Fragment, render as VueRender } from 'vue';
import warning from './warning'; import warning from './warning';
import type { RefObject } from './createRef'; import type { RefObject } from './createRef';
type NodeProps = Record<string, any> & 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) { export function triggerVNodeUpdate(vm: VNode, attrs: Record<string, any>, dom: any) {
VueRender(cloneVNode(vm, { ...attrs }), dom); 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?.();
}

6
components/card/Card.tsx

@ -1,5 +1,5 @@
import type { VNodeTypes, PropType, VNode, ExtractPropTypes, CSSProperties } from 'vue'; 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 Tabs from '../tabs';
import PropTypes from '../_util/vue-types'; import PropTypes from '../_util/vue-types';
import { flattenChildren, isEmptyElement, filterEmptyWithUndefined } from '../_util/props-util'; import { flattenChildren, isEmptyElement, filterEmptyWithUndefined } from '../_util/props-util';
@ -10,6 +10,8 @@ import devWarning from '../vc-util/devWarning';
import useStyle from './style'; import useStyle from './style';
import Skeleton from '../skeleton'; import Skeleton from '../skeleton';
import type { CustomSlotsType } from '../_util/type'; import type { CustomSlotsType } from '../_util/type';
import { customRenderSlot } from '../_util/vnode';
export interface CardTabListType { export interface CardTabListType {
key: string; key: string;
tab: any; tab: any;
@ -152,7 +154,7 @@ const Card = defineComponent({
`tabList slots is deprecated, Please use \`customTab\` instead.`, `tabList slots is deprecated, Please use \`customTab\` instead.`,
); );
let tab = temp !== undefined ? temp : slots[name] ? slots[name](item) : null; 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} />; return <TabPane tab={tab} key={item.key} disabled={item.disabled} />;
})} })}
</Tabs> </Tabs>

5
components/vc-table/Cell/index.tsx

@ -1,7 +1,7 @@
import classNames from '../../_util/classNames'; import classNames from '../../_util/classNames';
import { filterEmpty, flattenChildren, isValidElement } from '../../_util/props-util'; import { filterEmpty, flattenChildren, isValidElement } from '../../_util/props-util';
import type { CSSProperties, VNodeArrayChildren } from 'vue'; import type { CSSProperties, VNodeArrayChildren } from 'vue';
import { Text, computed, defineComponent, isVNode, renderSlot } from 'vue'; import { Text, computed, defineComponent, isVNode } from 'vue';
import type { import type {
DataIndex, DataIndex,
@ -23,6 +23,7 @@ import { useInjectSticky } from '../context/StickyContext';
import { warning } from '../../vc-util/warning'; import { warning } from '../../vc-util/warning';
import type { MouseEventHandler } from '../../_util/EventInterface'; import type { MouseEventHandler } from '../../_util/EventInterface';
import eagerComputed from '../../_util/eagerComputed'; import eagerComputed from '../../_util/eagerComputed';
import { customRenderSlot } from 'ant-design-vue/es/_util/vnode';
/** Check if cell is in hover range */ /** Check if cell is in hover range */
function inHoverRange(cellStartRow: number, cellRowSpan: number, startRow: number, endRow: number) { function inHoverRange(cellStartRow: number, cellRowSpan: number, startRow: number, endRow: number) {
@ -223,7 +224,7 @@ export default defineComponent<CellProps>({
contextSlots.value.bodyCell && contextSlots.value.bodyCell &&
!column.slots?.customRender !column.slots?.customRender
) { ) {
const child = renderSlot( const child = customRenderSlot(
contextSlots.value, contextSlots.value,
'bodyCell', 'bodyCell',
{ {

5
components/vc-table/hooks/useColumns.tsx

@ -1,6 +1,6 @@
import { warning } from '../../vc-util/warning'; import { warning } from '../../vc-util/warning';
import type { ComputedRef, Ref } from 'vue'; import type { ComputedRef, Ref } from 'vue';
import { renderSlot, computed, watchEffect } from 'vue'; import { computed, watchEffect } from 'vue';
import type { import type {
ColumnsType, ColumnsType,
ColumnType, ColumnType,
@ -14,6 +14,7 @@ import type {
import { INTERNAL_COL_DEFINE } from '../utils/legacyUtil'; import { INTERNAL_COL_DEFINE } from '../utils/legacyUtil';
import { EXPAND_COLUMN } from '../constant'; import { EXPAND_COLUMN } from '../constant';
import { useInjectSlots } from '../../table/context'; import { useInjectSlots } from '../../table/context';
import { customRenderSlot } from '../../_util/vnode';
function flatColumns<RecordType>(columns: ColumnsType<RecordType>): ColumnType<RecordType>[] { function flatColumns<RecordType>(columns: ColumnsType<RecordType>): ColumnType<RecordType>[] {
return columns.reduce((list, column) => { return columns.reduce((list, column) => {
@ -179,7 +180,7 @@ function useColumns<RecordType>(
class: `${prefixCls.value}-expand-icon-col`, class: `${prefixCls.value}-expand-icon-col`,
columnType: 'EXPAND_COLUMN', columnType: 'EXPAND_COLUMN',
}, },
title: renderSlot(contextSlots.value, 'expandColumnTitle', {}, () => ['']), title: customRenderSlot(contextSlots.value, 'expandColumnTitle', {}, () => ['']),
fixed: fixedColumn, fixed: fixedColumn,
class: `${prefixCls.value}-row-expand-icon-cell`, class: `${prefixCls.value}-row-expand-icon-cell`,
width: expandColumnWidth.value, width: expandColumnWidth.value,

Loading…
Cancel
Save