feat: SectionAccordion inactive state uses Link href instead of button onClick
This commit is contained in:
@@ -23,7 +23,7 @@ export const Active: Story = {
|
||||
title: 'Biography',
|
||||
id: 'bio',
|
||||
isActive: true,
|
||||
onClick: () => {},
|
||||
href: '/bio',
|
||||
children: <p>This is the expanded section content. It is visible because isActive is true.</p>,
|
||||
},
|
||||
};
|
||||
@@ -34,7 +34,7 @@ export const Collapsed: Story = {
|
||||
title: 'Work',
|
||||
id: 'work',
|
||||
isActive: false,
|
||||
onClick: () => console.log('section clicked'),
|
||||
href: '/work',
|
||||
children: <p>This content is hidden in collapsed state.</p>,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { SectionAccordion } from './SectionAccordion';
|
||||
|
||||
const defaultProps = {
|
||||
@@ -7,7 +6,7 @@ const defaultProps = {
|
||||
title: 'About',
|
||||
id: 'about',
|
||||
isActive: false,
|
||||
onClick: vi.fn(),
|
||||
href: '/about',
|
||||
children: <p>Content here</p>,
|
||||
};
|
||||
|
||||
@@ -17,19 +16,25 @@ describe('SectionAccordion', () => {
|
||||
const { container } = render(<SectionAccordion {...defaultProps} />);
|
||||
expect(container.querySelector('section#about')).toBeInTheDocument();
|
||||
});
|
||||
it('renders a button with number and title', () => {
|
||||
|
||||
it('renders a link with number and title', () => {
|
||||
render(<SectionAccordion {...defaultProps} />);
|
||||
expect(screen.getByRole('button', { name: /01.*About/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: /01.*About/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('link points to the correct href', () => {
|
||||
render(<SectionAccordion {...defaultProps} />);
|
||||
expect(screen.getByRole('link', { name: /01.*About/i })).toHaveAttribute('href', '/about');
|
||||
});
|
||||
|
||||
it('does not render children', () => {
|
||||
render(<SectionAccordion {...defaultProps} />);
|
||||
expect(screen.queryByText('Content here')).not.toBeInTheDocument();
|
||||
});
|
||||
it('calls onClick when button is clicked', async () => {
|
||||
const onClick = vi.fn();
|
||||
render(<SectionAccordion {...defaultProps} onClick={onClick} />);
|
||||
await userEvent.click(screen.getByRole('button'));
|
||||
expect(onClick).toHaveBeenCalledOnce();
|
||||
|
||||
it('does not render a button', () => {
|
||||
render(<SectionAccordion {...defaultProps} />);
|
||||
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,14 +45,17 @@ describe('SectionAccordion', () => {
|
||||
render(<SectionAccordion {...activeProps} />);
|
||||
expect(screen.getByRole('heading', { level: 1, name: /01.*About/i })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders children', () => {
|
||||
render(<SectionAccordion {...activeProps} />);
|
||||
expect(screen.getByText('Content here')).toBeInTheDocument();
|
||||
});
|
||||
it('does not render a button', () => {
|
||||
|
||||
it('does not render a link', () => {
|
||||
render(<SectionAccordion {...activeProps} />);
|
||||
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole('link')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('content wrapper has animate-fadeIn class', () => {
|
||||
const { container } = render(<SectionAccordion {...activeProps} />);
|
||||
expect(container.querySelector('.animate-fadeIn')).toBeInTheDocument();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Link from 'next/link';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
interface SectionAccordionProps {
|
||||
@@ -18,9 +19,9 @@ interface SectionAccordionProps {
|
||||
*/
|
||||
isActive: boolean;
|
||||
/**
|
||||
* Called when the collapsed header is clicked
|
||||
* Navigation URL for the collapsed heading link
|
||||
*/
|
||||
onClick: () => void;
|
||||
href: string;
|
||||
/**
|
||||
* Section content, shown when active
|
||||
*/
|
||||
@@ -28,9 +29,9 @@ interface SectionAccordionProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* Accordion-style section that collapses to a heading button when inactive.
|
||||
* Accordion-style section that collapses to a navigation link when inactive.
|
||||
*/
|
||||
export function SectionAccordion({ number, title, id, isActive, onClick, children }: SectionAccordionProps) {
|
||||
export function SectionAccordion({ number, title, id, isActive, href, children }: SectionAccordionProps) {
|
||||
return (
|
||||
<section id={id} className="scroll-mt-8">
|
||||
{isActive ? (
|
||||
@@ -46,10 +47,9 @@ export function SectionAccordion({ number, title, id, isActive, onClick, childre
|
||||
<div className="animate-fadeIn">{children}</div>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="w-full text-left mb-3 py-3 transition-all duration-200 hover:opacity-60 group"
|
||||
<Link
|
||||
href={href}
|
||||
className="block w-full text-left mb-3 py-3 transition-all duration-200 hover:opacity-60 group"
|
||||
>
|
||||
<h2
|
||||
className="font-heading font-black text-2xl sm:text-3xl opacity-30 group-hover:opacity-50 transition-opacity"
|
||||
@@ -57,7 +57,7 @@ export function SectionAccordion({ number, title, id, isActive, onClick, childre
|
||||
>
|
||||
{number}. {title}
|
||||
</h2>
|
||||
</button>
|
||||
</Link>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user