Browse Source

fix(web-antdv-next): keep imperative alerts in theme context (#8343)

pull/8351/head
俞可 · YU KE 4 weeks ago
committed by GitHub
parent
commit
f29f32da4a
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 130
      apps/web-antdv-next/src/adapter/component/__tests__/theme-aware-button.test.ts
  2. 11
      apps/web-antdv-next/src/adapter/component/index.ts
  3. 47
      apps/web-antdv-next/src/adapter/component/theme-aware-button.ts
  4. 6
      apps/web-antdv-next/src/app.vue

130
apps/web-antdv-next/src/adapter/component/__tests__/theme-aware-button.test.ts

@ -0,0 +1,130 @@
/* eslint-disable vue/one-component-per-file, vue/require-default-prop */
import type { App, Component } from 'vue';
import { createApp, defineComponent, h } from 'vue';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { createThemeAwareButton } from '../theme-aware-button';
const mocks = vi.hoisted(() => ({
compact: false,
contextTheme: undefined as object | undefined,
isDark: false,
providerThemes: [] as any[],
tokens: { colorPrimary: '#e11d48' },
}));
vi.mock('@vben/hooks', () => ({
useAntdDesignTokens: () => ({ tokens: mocks.tokens }),
}));
vi.mock('@vben/preferences', () => ({
preferences: {
app: {
get compact() {
return mocks.compact;
},
},
},
usePreferences: () => ({
isDark: {
get value() {
return mocks.isDark;
},
},
}),
}));
vi.mock('antdv-next/config-provider/context', () => ({
useConfig: () => ({ value: { theme: mocks.contextTheme } }),
}));
vi.mock('antdv-next', async () => {
const { defineComponent, h } =
await vi.importActual<typeof import('vue')>('vue');
return {
ConfigProvider: defineComponent({
props: { theme: Object },
setup(props, { slots }) {
mocks.providerThemes.push(props.theme);
return () =>
h('section', { 'data-theme-provider': '' }, slots.default?.());
},
}),
theme: {
compactAlgorithm: 'compact',
darkAlgorithm: 'dark',
defaultAlgorithm: 'default',
},
};
});
const Button = defineComponent({
inheritAttrs: false,
props: { type: String },
setup(props, { attrs, slots }) {
return () =>
h(
'button',
{ ...attrs, 'data-button-type': props.type },
slots.default?.(),
);
},
});
let activeApp: App | undefined;
function mountButton(component: Component) {
const container = document.createElement('div');
document.body.append(container);
activeApp = createApp(() =>
h(component, { 'data-probe': 'button' }, () => 'Submit'),
);
activeApp.mount(container);
return container;
}
beforeEach(() => {
mocks.compact = false;
mocks.contextTheme = undefined;
mocks.isDark = false;
mocks.providerThemes.length = 0;
});
afterEach(() => {
activeApp?.unmount();
activeApp = undefined;
document.body.innerHTML = '';
});
describe('createThemeAwareButton', () => {
it('uses the existing ConfigProvider context without adding a wrapper', () => {
mocks.contextTheme = { token: mocks.tokens };
const container = mountButton(createThemeAwareButton(Button, 'primary'));
expect(container.querySelector('[data-theme-provider]')).toBeNull();
expect(container.querySelector('button')?.dataset.buttonType).toBe(
'primary',
);
expect(container.querySelector('button')?.dataset.probe).toBe('button');
});
it('provides the current Vben theme when no ConfigProvider is present', () => {
mocks.compact = true;
mocks.isDark = true;
const container = mountButton(createThemeAwareButton(Button, 'default'));
expect(container.querySelector('[data-theme-provider]')).not.toBeNull();
expect(mocks.providerThemes).toEqual([
{
algorithm: ['dark', 'compact'],
token: mocks.tokens,
},
]);
expect(container.querySelector('button')?.dataset.buttonType).toBe(
'default',
);
});
});

11
apps/web-antdv-next/src/adapter/component/index.ts

@ -73,6 +73,8 @@ import { isEmpty } from '@vben/utils';
import { message, Modal, notification } from 'antdv-next';
import { upload_file } from '#/api';
import { createThemeAwareButton } from './theme-aware-button';
type AdapterUploadProps = UploadProps & {
aspectRatio?: string;
crop?: boolean;
@ -89,6 +91,7 @@ const AutoComplete = defineAsyncComponent(
const Button = defineAsyncComponent(
() => import('antdv-next/dist/button/index'),
);
const Checkbox = defineAsyncComponent(
() => import('antdv-next/dist/checkbox/index'),
);
@ -716,9 +719,7 @@ async function initComponentAdapter() {
CheckboxGroup,
DatePicker,
// 自定义默认按钮
DefaultButton: (props, { attrs, slots }) => {
return h(Button, { ...props, attrs, type: 'default' }, slots);
},
DefaultButton: createThemeAwareButton(Button, 'default'),
Divider,
IconPicker: withDefaultPlaceholder(IconPicker, 'select', {
iconSlot: 'addonAfter',
@ -730,9 +731,7 @@ async function initComponentAdapter() {
InputPassword: withDefaultPlaceholder(InputPassword, 'input'),
Mentions: withDefaultPlaceholder(Mentions, 'input'),
// 自定义主要按钮
PrimaryButton: (props, { attrs, slots }) => {
return h(Button, { ...props, attrs, type: 'primary' }, slots);
},
PrimaryButton: createThemeAwareButton(Button, 'primary'),
Radio,
RadioGroup,
RangePicker,

47
apps/web-antdv-next/src/adapter/component/theme-aware-button.ts

@ -0,0 +1,47 @@
import type { Component } from 'vue';
import { computed, defineComponent, h } from 'vue';
import { useAntdDesignTokens } from '@vben/hooks';
import { preferences, usePreferences } from '@vben/preferences';
import { ConfigProvider, theme } from 'antdv-next';
import { useConfig } from 'antdv-next/config-provider/context';
function createThemeAwareButton(
Button: Component,
type: 'default' | 'primary',
) {
return defineComponent({
inheritAttrs: false,
setup(props, { attrs, slots }) {
const config = useConfig();
if (config.value?.theme) {
return () => h(Button, { ...attrs, ...props, type }, slots);
}
const { isDark } = usePreferences();
const { tokens } = useAntdDesignTokens();
const buttonTheme = computed(() => {
const algorithm = [
isDark.value ? theme.darkAlgorithm : theme.defaultAlgorithm,
];
if (preferences.app.compact) {
algorithm.push(theme.compactAlgorithm);
}
return { algorithm, token: tokens };
});
return () =>
h(
ConfigProvider,
{ theme: buttonTheme.value },
{
default: () => h(Button, { ...attrs, ...props, type }, slots),
},
);
},
});
}
export { createThemeAwareButton };

6
apps/web-antdv-next/src/app.vue

@ -30,9 +30,9 @@ const tokenTheme = computed(() => {
});
watch(
tokenTheme,
(themeConfig) => {
ConfigProvider.config({ theme: themeConfig });
[tokenTheme, antdLocale],
([themeConfig, locale]) => {
ConfigProvider.config({ theme: themeConfig, locale });
},
{ immediate: true },
);

Loading…
Cancel
Save