Files
erikghandClaude Opus 5 5b400cb161 feat(primitivas): capa gráfica y de movimiento reutilizable
Diez primitivas que dan a la landing el "gráfico y movimiento" que pidió
la dueña, todas con guarda de prefers-reduced-motion:

- FondoPuntos / PatronPuntos — el patrón de puntos del manual
- TarjetaSpotlight, TextoDesenfoque, ContadorNumero — realce y reveal
- Carrusel — deslizable, con contador, autoplay pausable
- Icono, SimboloTresLineas, IlustracionInflexion — línea, sin relleno
- FondoFoto — duotono azul/dorado sobre foto

useAutoStep en lib/motion.ts comparte la forma del autoplay del carrusel
para indicadores de paso que no son carrusel.

IlustracionInflexion documenta un detalle que muerde: motion.path aplica
su propio andamiaje de dasharray aunque no reciba initial/animate, así
que la rama de reduced motion renderiza <path> planos en vez de intentar
neutralizar los animados.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 10:44:28 -06:00

29 lines
1.2 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { render } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { TextoDesenfoque } from '../TextoDesenfoque';
describe('<TextoDesenfoque />', () => {
it('renders the full phrase across all segments', () => {
const { container } = render(
<TextoDesenfoque
segments={[{ text: 'Tu negocio no necesita' }, { text: 'más esfuerzo.', className: 'text-dorado' }]}
/>,
);
expect(container.textContent).toBe('Tu negocio no necesita más esfuerzo.');
});
it('joins words with a plain space, not a stray non-breaking space', () => {
const { container } = render(<TextoDesenfoque segments={[{ text: 'Uno dos tres' }]} />);
expect(container.textContent).toBe('Uno dos tres');
expect(container.textContent).not.toMatch(/ /);
});
it('applies each segment\'s className to its own words only', () => {
const { container } = render(
<TextoDesenfoque segments={[{ text: 'texto plano' }, { text: 'texto dorado', className: 'text-dorado' }]} />,
);
const goldWords = Array.from(container.querySelectorAll('.text-dorado')).map((el) => el.textContent?.trim());
expect(goldWords).toEqual(['texto', 'dorado']);
});
});