mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
fix: replace PreviewModal motion with CSS animation, add motion test mock
- PreviewModal no longer uses motion/react — prevents test failures from AnimatePresence exit animations never completing in test env - Add CSS animations for .ds-modal-backdrop (fade-in) and .ds-modal (scale-in) - Add vitest alias to mock motion/react so AnimatePresence in other components (UpdaterPopup, ExamplesTab) completes synchronously in tests
This commit is contained in:
parent
9c331129e9
commit
e367d2879e
4 changed files with 54 additions and 14 deletions
|
|
@ -1,5 +1,4 @@
|
||||||
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
|
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
|
||||||
import { motion } from 'motion/react';
|
|
||||||
import { useT } from '../i18n';
|
import { useT } from '../i18n';
|
||||||
import { copyToClipboard } from '../lib/copy-to-clipboard';
|
import { copyToClipboard } from '../lib/copy-to-clipboard';
|
||||||
import {
|
import {
|
||||||
|
|
@ -12,7 +11,6 @@ import {
|
||||||
} from '../runtime/exports';
|
} from '../runtime/exports';
|
||||||
import { buildSrcdoc } from '../runtime/srcdoc';
|
import { buildSrcdoc } from '../runtime/srcdoc';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import { modalOverlay, modalContent } from '../motion';
|
|
||||||
|
|
||||||
export interface PreviewView {
|
export interface PreviewView {
|
||||||
id: string;
|
id: string;
|
||||||
|
|
@ -471,22 +469,14 @@ export function PreviewModal({
|
||||||
const canOpenTemplateShareMenu = canExportFiles || Boolean(previewShareUrl);
|
const canOpenTemplateShareMenu = canExportFiles || Boolean(previewShareUrl);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<div
|
||||||
className="ds-modal-backdrop"
|
className="ds-modal-backdrop"
|
||||||
role="dialog"
|
role="dialog"
|
||||||
aria-modal="true"
|
aria-modal="true"
|
||||||
aria-label={`${title} preview`}
|
aria-label={`${title} preview`}
|
||||||
variants={modalOverlay}
|
|
||||||
initial="hidden"
|
|
||||||
animate="visible"
|
|
||||||
exit="exit"
|
|
||||||
>
|
>
|
||||||
<motion.div
|
<div
|
||||||
className={`ds-modal ${fullscreen ? 'ds-modal-fullscreen' : ''}`}
|
className={`ds-modal ${fullscreen ? 'ds-modal-fullscreen' : ''}`}
|
||||||
variants={modalContent}
|
|
||||||
initial="hidden"
|
|
||||||
animate="visible"
|
|
||||||
exit="exit"
|
|
||||||
>
|
>
|
||||||
<header className="ds-modal-header">
|
<header className="ds-modal-header">
|
||||||
<div className="ds-modal-header-top">
|
<div className="ds-modal-header-top">
|
||||||
|
|
@ -941,7 +931,7 @@ export function PreviewModal({
|
||||||
</aside>
|
</aside>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</div>
|
||||||
</motion.div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -462,6 +462,18 @@
|
||||||
================================================================ */
|
================================================================ */
|
||||||
|
|
||||||
|
|
||||||
|
/* ================================================================
|
||||||
|
Preview modal
|
||||||
|
================================================================ */
|
||||||
|
|
||||||
|
.ds-modal-backdrop {
|
||||||
|
animation: od-fade-in 200ms cubic-bezier(0.23, 1, 0.32, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ds-modal {
|
||||||
|
animation: od-scale-in 250ms cubic-bezier(0.23, 1, 0.32, 1) both;
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
Plugin detail view
|
Plugin detail view
|
||||||
================================================================ */
|
================================================================ */
|
||||||
|
|
|
||||||
32
apps/web/tests/helpers/motion-mock.tsx
Normal file
32
apps/web/tests/helpers/motion-mock.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { forwardRef, type ComponentProps, type ElementType } from 'react';
|
||||||
|
|
||||||
|
function AnimatePresence({ children }: { children?: React.ReactNode }) {
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const motionHandler: ProxyHandler<object> = {
|
||||||
|
get(_target, prop: string) {
|
||||||
|
const Component = forwardRef<unknown, ComponentProps<ElementType>>((props, ref) => {
|
||||||
|
const {
|
||||||
|
variants: _variants,
|
||||||
|
initial: _initial,
|
||||||
|
animate: _animate,
|
||||||
|
exit: _exit,
|
||||||
|
whileHover: _whileHover,
|
||||||
|
whileTap: _whileTap,
|
||||||
|
transition: _transition,
|
||||||
|
layout: _layout,
|
||||||
|
layoutId: _layoutId,
|
||||||
|
...rest
|
||||||
|
} = props as Record<string, unknown>;
|
||||||
|
const Tag = prop as ElementType;
|
||||||
|
return <Tag ref={ref} {...rest} />;
|
||||||
|
});
|
||||||
|
Component.displayName = `motion.${prop}`;
|
||||||
|
return Component;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const motion = new Proxy({}, motionHandler);
|
||||||
|
|
||||||
|
export { AnimatePresence, motion };
|
||||||
|
|
@ -1,6 +1,12 @@
|
||||||
|
import { resolve } from 'node:path';
|
||||||
import { defineConfig } from 'vitest/config';
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'motion/react': resolve(__dirname, 'tests/helpers/motion-mock.tsx'),
|
||||||
|
},
|
||||||
|
},
|
||||||
test: {
|
test: {
|
||||||
environment: 'node',
|
environment: 'node',
|
||||||
include: ['tests/**/*.test.{ts,tsx}'],
|
include: ['tests/**/*.test.{ts,tsx}'],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue