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>
This commit is contained in:
1 parent
a9758a92f9
commit
5b400cb161
19 files changed
+881
No files matched your search
@@ -0,0 +1,48 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { Carrusel } from '../Carrusel';
|
||||
|
||||
describe('<Carrusel />', () => {
|
||||
it('renders every slide', () => {
|
||||
render(<Carrusel ariaLabel="test" slides={[<p key="a">Uno</p>, <p key="b">Dos</p>, <p key="c">Tres</p>]} />);
|
||||
expect(screen.getByText('Uno')).toBeInTheDocument();
|
||||
expect(screen.getByText('Dos')).toBeInTheDocument();
|
||||
expect(screen.getByText('Tres')).toBeInTheDocument();
|
||||
});
|
||||
it('renders one position dot per slide, and marks the current one', () => {
|
||||
render(<Carrusel ariaLabel="test" slides={[<p key="a">Uno</p>, <p key="b">Dos</p>]} />);
|
||||
const dots = screen.getAllByRole('button', { name: /ir a la tarjeta/i });
|
||||
expect(dots).toHaveLength(2);
|
||||
expect(dots[0]).toHaveAttribute('aria-current', 'true');
|
||||
});
|
||||
it('moves to a slide when its dot is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<Carrusel ariaLabel="test" slides={[<p key="a">Uno</p>, <p key="b">Dos</p>]} />);
|
||||
const dots = screen.getAllByRole('button', { name: /ir a la tarjeta/i });
|
||||
await user.click(dots[1]);
|
||||
expect(dots[1]).toHaveAttribute('aria-current', 'true');
|
||||
});
|
||||
it('hides the dots entirely for a single slide', () => {
|
||||
render(<Carrusel ariaLabel="test" slides={[<p key="a">Solo una</p>]} />);
|
||||
expect(screen.queryByRole('button', { name: /ir a la tarjeta/i })).not.toBeInTheDocument();
|
||||
});
|
||||
it('exposes the slide group with the given accessible name', () => {
|
||||
render(<Carrusel ariaLabel="Frases de ejemplo" slides={[<p key="a">Uno</p>]} />);
|
||||
expect(screen.getByRole('group', { name: 'Frases de ejemplo' })).toBeInTheDocument();
|
||||
});
|
||||
it('shows a numbered counter with prev/next arrows instead of dots when mostrarContador is set', () => {
|
||||
render(<Carrusel ariaLabel="test" mostrarContador slides={[<p key="a">Uno</p>, <p key="b">Dos</p>, <p key="c">Tres</p>]} />);
|
||||
expect(screen.getByText('01')).toBeInTheDocument();
|
||||
expect(screen.getByText(/\/ 03/)).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Siguiente' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'Anterior' })).toBeInTheDocument();
|
||||
expect(screen.queryByRole('button', { name: /ir a la tarjeta/i })).not.toBeInTheDocument();
|
||||
});
|
||||
it('advances the counter when the next arrow is clicked', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<Carrusel ariaLabel="test" mostrarContador slides={[<p key="a">Uno</p>, <p key="b">Dos</p>]} />);
|
||||
await user.click(screen.getByRole('button', { name: 'Siguiente' }));
|
||||
expect(screen.getByText('02')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user