Browse Source

fix(@vben/plugins): stabilize vxe toolbar slot options

pull/8335/head
kilisamemarisaaa 4 weeks ago
parent
commit
9d6478dcaa
  1. 5
      .changeset/steady-grids-measure.md
  2. 162
      packages/effects/plugins/src/vxe-table/__tests__/use-vxe-grid.reactivity.test.ts
  3. 14
      packages/effects/plugins/src/vxe-table/use-vxe-grid.vue

5
.changeset/steady-grids-measure.md

@ -0,0 +1,5 @@
---
'@vben/plugins': patch
---
fix(@vben/plugins): keep vxe toolbar options stable when slot content updates

162
packages/effects/plugins/src/vxe-table/__tests__/use-vxe-grid.reactivity.test.ts

@ -0,0 +1,162 @@
import type { App } from 'vue';
import { createApp, defineComponent, h, nextTick, ref } from 'vue';
import { describe, expect, it, vi } from 'vitest';
import VbenVxeGrid from '../use-vxe-grid.vue';
const observedToolbarConfigs = vi.hoisted(() => [] as unknown[]);
vi.mock('@vben/hooks', async () => {
const { ref } = await import('vue');
return {
usePriorityValues: () => ({
class: ref(undefined),
formOptions: ref(undefined),
gridClass: ref(undefined),
gridEvents: ref(undefined),
gridOptions: ref({
columns: [
{
field: 'operation',
fixed: 'right',
width: 'auto',
},
],
}),
separator: ref(undefined),
showSearchForm: ref(true),
tableData: ref(undefined),
tableTitle: ref(undefined),
tableTitleHelp: ref(undefined),
viewedRowOptions: ref(undefined),
}),
};
});
vi.mock('@vben/preferences', async () => {
const { ref } = await import('vue');
return { usePreferences: () => ({ isMobile: ref(false) }) };
});
vi.mock('@vben/icons', async () => {
const { defineComponent, h } = await import('vue');
return {
EmptyIcon: defineComponent(() => () => h('span')),
};
});
vi.mock('@vben/locales', () => ({ $t: (key: string) => key }));
vi.mock('@vben-core/shadcn-ui', async () => {
const { defineComponent, h } = await import('vue');
const Stub = defineComponent((_, { slots }) => () => h('div', slots.default?.()));
return { VbenHelpTooltip: Stub, VbenLoading: Stub };
});
vi.mock('vxe-pc-ui', async () => {
const { defineComponent, h } = await import('vue');
return { VxeButton: defineComponent(() => () => h('button')) };
});
vi.mock('vxe-table', async () => {
const { defineComponent, h } = await import('vue');
return {
VxeGrid: defineComponent({
inheritAttrs: false,
setup(_, { attrs, slots }) {
return () => {
observedToolbarConfigs.push(attrs.toolbarConfig);
return h('div', [
slots['toolbar-actions']?.({}),
slots['toolbar-tools']?.({}),
]);
};
},
}),
VxeUI: { getConfig: () => ({ grid: {} }) },
};
});
vi.mock('../extends', () => ({ extendProxyOptions: vi.fn() }));
vi.mock('../init', async () => {
const { defineComponent, h } = await import('vue');
return {
useTableForm: () => [
defineComponent(() => () => h('form')),
{
getLatestSubmissionValues: vi.fn(),
getState: vi.fn(() => ({ compact: true })),
getValues: vi.fn(async () => ({})),
reset: vi.fn(),
setLatestSubmissionValues: vi.fn(),
setState: vi.fn(),
unmount: vi.fn(),
},
],
};
});
vi.mock('../viewed-row', () => ({
applyViewedRowOptions: vi.fn(),
useViewedRow: vi.fn(),
}));
describe('vben vxe grid toolbar slots', () => {
it.each(['table-title', 'toolbar-actions', 'toolbar-tools'])(
'keeps toolbar options stable when the %s slot updates',
async (slotName) => {
observedToolbarConfigs.length = 0;
const loading = ref(false);
const api = {
grid: { commitProxy: vi.fn() },
mount: vi.fn(),
reload: vi.fn(),
setState: vi.fn(),
toggleSearchForm: vi.fn(),
unmount: vi.fn(),
useStore: vi.fn(() => ({})),
};
const Consumer = defineComponent(() => () =>
h(VbenVxeGrid, { api } as any, {
[slotName]: () =>
h(
'span',
{ 'data-slot-state': '' },
loading.value ? 'loading' : 'idle',
),
}),
);
const host = document.createElement('div');
document.body.append(host);
const app: App = createApp(Consumer);
try {
app.mount(host);
await nextTick();
await nextTick();
expect(host.querySelector('[data-slot-state]')?.textContent).toBe(
'idle',
);
const initialToolbarConfig = observedToolbarConfigs.at(-1);
loading.value = true;
await nextTick();
await nextTick();
expect(host.querySelector('[data-slot-state]')?.textContent).toBe(
'loading',
);
expect(observedToolbarConfigs.at(-1)).toBe(initialToolbarConfig);
} finally {
app.unmount();
host.remove();
}
},
);
});

14
packages/effects/plugins/src/vxe-table/use-vxe-grid.vue

@ -152,20 +152,20 @@ const [Form, formApi] = useTableForm({
});
const showTableTitle = computed(() => {
return !!slots[TABLE_TITLE]?.() || tableTitle.value;
return !!slots[TABLE_TITLE] || tableTitle.value;
});
const showToolbar = computed(() => {
return (
!!slots[TOOLBAR_ACTIONS]?.() ||
!!slots[TOOLBAR_TOOLS]?.() ||
!!slots[TOOLBAR_ACTIONS] ||
!!slots[TOOLBAR_TOOLS] ||
showTableTitle.value
);
});
const toolbarOptions = computed(() => {
const slotActions = slots[TOOLBAR_ACTIONS]?.();
const slotTools = slots[TOOLBAR_TOOLS]?.();
const hasSlotActions = !!slots[TOOLBAR_ACTIONS];
const hasSlotTools = !!slots[TOOLBAR_TOOLS];
const searchBtn: VxeToolbarPropTypes.ToolConfig = {
code: 'search',
icon: 'vxe-icon-search',
@ -194,10 +194,10 @@ const toolbarOptions = computed(() => {
// 强制使用固定的toolbar配置,不允许用户自定义
// 减少配置的复杂度,以及后续维护的成本
toolbarConfig.slots = {
...(slotActions || showTableTitle.value
...(hasSlotActions || showTableTitle.value
? { buttons: TOOLBAR_ACTIONS }
: {}),
...(slotTools ? { tools: TOOLBAR_TOOLS } : {}),
...(hasSlotTools ? { tools: TOOLBAR_TOOLS } : {}),
};
return { toolbarConfig };
});

Loading…
Cancel
Save