From 702be45ce38bcf244ee99af213534b76e2f10593 Mon Sep 17 00:00:00 2001 From: Ilia Mashkov Date: Tue, 14 Jul 2026 17:05:31 +0300 Subject: [PATCH] refactor: rename section item to SectionPanel, list to SectionAccordion --- app/[[...slug]]/page.tsx | 6 ++-- .../Section/ui/SectionAccordion/index.ts | 1 - .../SectionPanel.stories.tsx} | 10 +++--- .../SectionPanel.test.tsx} | 20 ++++++------ .../SectionPanel.tsx} | 4 +-- src/entities/Section/ui/SectionPanel/index.ts | 1 + src/entities/Section/ui/index.ts | 2 +- src/widgets/SectionAccordion/index.ts | 1 + .../SectionAccordion.test.tsx} | 32 +++++++++---------- .../ui/SectionAccordion/SectionAccordion.tsx} | 8 ++--- src/widgets/SectionsAccordion/index.ts | 1 - 11 files changed, 43 insertions(+), 43 deletions(-) delete mode 100644 src/entities/Section/ui/SectionAccordion/index.ts rename src/entities/Section/ui/{SectionAccordion/SectionAccordion.stories.tsx => SectionPanel/SectionPanel.stories.tsx} (75%) rename src/entities/Section/ui/{SectionAccordion/SectionAccordion.test.tsx => SectionPanel/SectionPanel.test.tsx} (73%) rename src/entities/Section/ui/{SectionAccordion/SectionAccordion.tsx => SectionPanel/SectionPanel.tsx} (92%) create mode 100644 src/entities/Section/ui/SectionPanel/index.ts create mode 100644 src/widgets/SectionAccordion/index.ts rename src/widgets/{SectionsAccordion/ui/SectionsAccordion/SectionsAccordion.test.tsx => SectionAccordion/ui/SectionAccordion/SectionAccordion.test.tsx} (77%) rename src/widgets/{SectionsAccordion/ui/SectionsAccordion/SectionsAccordion.tsx => SectionAccordion/ui/SectionAccordion/SectionAccordion.tsx} (85%) delete mode 100644 src/widgets/SectionsAccordion/index.ts diff --git a/app/[[...slug]]/page.tsx b/app/[[...slug]]/page.tsx index 8bb725a..8c54b61 100644 --- a/app/[[...slug]]/page.tsx +++ b/app/[[...slug]]/page.tsx @@ -1,8 +1,8 @@ import { notFound } from 'next/navigation'; import type { SectionRecord } from '$entities/Section'; import { getCollection } from '$shared/api'; +import { SectionAccordion } from '$widgets/SectionAccordion'; import { SectionFactory } from '$widgets/SectionFactory'; -import { SectionsAccordion } from '$widgets/SectionsAccordion'; /** * Optional catchall: `/` → first section, `/:slug` → that section. @@ -46,11 +46,11 @@ export default async function SectionPage({ params }: Props) { return (
- + {sections.map((s) => ( ))} - +
); } diff --git a/src/entities/Section/ui/SectionAccordion/index.ts b/src/entities/Section/ui/SectionAccordion/index.ts deleted file mode 100644 index 25000d2..0000000 --- a/src/entities/Section/ui/SectionAccordion/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './SectionAccordion'; diff --git a/src/entities/Section/ui/SectionAccordion/SectionAccordion.stories.tsx b/src/entities/Section/ui/SectionPanel/SectionPanel.stories.tsx similarity index 75% rename from src/entities/Section/ui/SectionAccordion/SectionAccordion.stories.tsx rename to src/entities/Section/ui/SectionPanel/SectionPanel.stories.tsx index 3523df2..70b2185 100644 --- a/src/entities/Section/ui/SectionAccordion/SectionAccordion.stories.tsx +++ b/src/entities/Section/ui/SectionPanel/SectionPanel.stories.tsx @@ -1,9 +1,9 @@ import type { Meta, StoryObj } from '@storybook/nextjs-vite'; -import { SectionAccordion } from './SectionAccordion'; +import { SectionPanel } from './SectionPanel'; -const meta: Meta = { - title: 'Shared/SectionAccordion', - component: SectionAccordion, +const meta: Meta = { + title: 'Shared/SectionPanel', + component: SectionPanel, decorators: [ (Story) => (
@@ -15,7 +15,7 @@ const meta: Meta = { export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Active: Story = { args: { diff --git a/src/entities/Section/ui/SectionAccordion/SectionAccordion.test.tsx b/src/entities/Section/ui/SectionPanel/SectionPanel.test.tsx similarity index 73% rename from src/entities/Section/ui/SectionAccordion/SectionAccordion.test.tsx rename to src/entities/Section/ui/SectionPanel/SectionPanel.test.tsx index 6c5d182..2de2a63 100644 --- a/src/entities/Section/ui/SectionAccordion/SectionAccordion.test.tsx +++ b/src/entities/Section/ui/SectionPanel/SectionPanel.test.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react'; -import { SectionAccordion } from './SectionAccordion'; +import { SectionPanel } from './SectionPanel'; const defaultProps = { number: '01', @@ -10,30 +10,30 @@ const defaultProps = { children:

Content here

, }; -describe('SectionAccordion', () => { +describe('SectionPanel', () => { describe('collapsed state (isActive=false)', () => { it('renders a section element with the given id', () => { - const { container } = render(); + const { container } = render(); expect(container.querySelector('section#about')).toBeInTheDocument(); }); it('renders a link with number and title', () => { - render(); + render(); expect(screen.getByRole('link', { name: /01.*About/i })).toBeInTheDocument(); }); it('link points to the correct href', () => { - render(); + render(); expect(screen.getByRole('link', { name: /01.*About/i })).toHaveAttribute('href', '/about'); }); it('does not render children', () => { - render(); + render(); expect(screen.queryByText('Content here')).not.toBeInTheDocument(); }); it('does not render a button', () => { - render(); + render(); expect(screen.queryByRole('button')).not.toBeInTheDocument(); }); }); @@ -42,17 +42,17 @@ describe('SectionAccordion', () => { const activeProps = { ...defaultProps, isActive: true }; it('renders an h1 with number and title', () => { - render(); + render(); expect(screen.getByRole('heading', { level: 1, name: /01.*About/i })).toBeInTheDocument(); }); it('renders children', () => { - render(); + render(); expect(screen.getByText('Content here')).toBeInTheDocument(); }); it('does not render a link', () => { - render(); + render(); expect(screen.queryByRole('link')).not.toBeInTheDocument(); }); }); diff --git a/src/entities/Section/ui/SectionAccordion/SectionAccordion.tsx b/src/entities/Section/ui/SectionPanel/SectionPanel.tsx similarity index 92% rename from src/entities/Section/ui/SectionAccordion/SectionAccordion.tsx rename to src/entities/Section/ui/SectionPanel/SectionPanel.tsx index d02d582..31c6495 100644 --- a/src/entities/Section/ui/SectionAccordion/SectionAccordion.tsx +++ b/src/entities/Section/ui/SectionPanel/SectionPanel.tsx @@ -2,7 +2,7 @@ import Link from 'next/link'; import type { ReactNode } from 'react'; import { ViewTransitionWrapper } from '$shared/ui'; -interface SectionAccordionProps { +interface SectionPanelProps { /** * Display number prefix (e.g. "01") */ @@ -32,7 +32,7 @@ interface SectionAccordionProps { /** * Accordion-style section that collapses to a navigation link when inactive. */ -export function SectionAccordion({ number, title, id, isActive, href, children }: SectionAccordionProps) { +export function SectionPanel({ number, title, id, isActive, href, children }: SectionPanelProps) { const heading = `${number}. ${title}`; return ( diff --git a/src/entities/Section/ui/SectionPanel/index.ts b/src/entities/Section/ui/SectionPanel/index.ts new file mode 100644 index 0000000..6281730 --- /dev/null +++ b/src/entities/Section/ui/SectionPanel/index.ts @@ -0,0 +1 @@ +export * from './SectionPanel'; diff --git a/src/entities/Section/ui/index.ts b/src/entities/Section/ui/index.ts index 25000d2..6281730 100644 --- a/src/entities/Section/ui/index.ts +++ b/src/entities/Section/ui/index.ts @@ -1 +1 @@ -export * from './SectionAccordion'; +export * from './SectionPanel'; diff --git a/src/widgets/SectionAccordion/index.ts b/src/widgets/SectionAccordion/index.ts new file mode 100644 index 0000000..e999d50 --- /dev/null +++ b/src/widgets/SectionAccordion/index.ts @@ -0,0 +1 @@ +export { SectionAccordion } from './ui/SectionAccordion/SectionAccordion'; diff --git a/src/widgets/SectionsAccordion/ui/SectionsAccordion/SectionsAccordion.test.tsx b/src/widgets/SectionAccordion/ui/SectionAccordion/SectionAccordion.test.tsx similarity index 77% rename from src/widgets/SectionsAccordion/ui/SectionsAccordion/SectionsAccordion.test.tsx rename to src/widgets/SectionAccordion/ui/SectionAccordion/SectionAccordion.test.tsx index 918c46b..0a52144 100644 --- a/src/widgets/SectionsAccordion/ui/SectionsAccordion/SectionsAccordion.test.tsx +++ b/src/widgets/SectionAccordion/ui/SectionAccordion/SectionAccordion.test.tsx @@ -1,6 +1,6 @@ import { render, screen } from '@testing-library/react'; import type { SectionRecord } from '$entities/Section'; -import { SectionsAccordion } from './SectionsAccordion'; +import { SectionAccordion } from './SectionAccordion'; const baseRecord = { collectionId: 'c1', collectionName: 'sections', created: '', updated: '' }; @@ -10,26 +10,26 @@ const sections: SectionRecord[] = [ { ...baseRecord, id: '3', slug: 'skills', title: 'Skills', order: 3 }, ]; -describe('SectionsAccordion', () => { +describe('SectionAccordion', () => { describe('active section rendering', () => { it('renders the active section as an h1', () => { render( - +
Intro content
Bio content
Skills content
-
, +
, ); expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('01. Intro'); }); it('renders inactive sections as links', () => { render( - +
Intro content
Bio content
Skills content
-
, +
, ); const links = screen.getAllByRole('link'); expect(links).toHaveLength(2); @@ -37,11 +37,11 @@ describe('SectionsAccordion', () => { it('inactive section links point to correct hrefs', () => { render( - +
Intro content
Bio content
Skills content
-
, +
, ); expect(screen.getByRole('link', { name: /02.*Bio/i })).toHaveAttribute('href', '/bio'); expect(screen.getByRole('link', { name: /03.*Skills/i })).toHaveAttribute('href', '/skills'); @@ -49,22 +49,22 @@ describe('SectionsAccordion', () => { it('renders the correct active section for a given activeSlug', () => { render( - +
Intro content
Bio content
Skills content
-
, +
, ); expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('02. Bio'); }); it('only one section is active at a time', () => { render( - +
Intro content
Bio content
Skills content
-
, +
, ); expect(screen.getAllByRole('heading', { level: 1 })).toHaveLength(1); }); @@ -73,22 +73,22 @@ describe('SectionsAccordion', () => { describe('content slots', () => { it('shows active section content', () => { render( - +
Intro content
Bio content
Skills content
-
, +
, ); expect(screen.getByText('Intro content')).toBeInTheDocument(); }); it('does not show inactive section content', () => { render( - +
Intro content
Bio content
Skills content
-
, +
, ); expect(screen.queryByText('Bio content')).not.toBeInTheDocument(); expect(screen.queryByText('Skills content')).not.toBeInTheDocument(); diff --git a/src/widgets/SectionsAccordion/ui/SectionsAccordion/SectionsAccordion.tsx b/src/widgets/SectionAccordion/ui/SectionAccordion/SectionAccordion.tsx similarity index 85% rename from src/widgets/SectionsAccordion/ui/SectionsAccordion/SectionsAccordion.tsx rename to src/widgets/SectionAccordion/ui/SectionAccordion/SectionAccordion.tsx index 73cffcd..56df0eb 100644 --- a/src/widgets/SectionsAccordion/ui/SectionsAccordion/SectionsAccordion.tsx +++ b/src/widgets/SectionAccordion/ui/SectionAccordion/SectionAccordion.tsx @@ -1,7 +1,7 @@ import type { ReactNode } from 'react'; import { Children } from 'react'; import type { SectionRecord } from '$entities/Section'; -import { SectionAccordion } from '$entities/Section'; +import { SectionPanel } from '$entities/Section'; export interface Props { /** @@ -24,13 +24,13 @@ export interface Props { * Active section is determined by the URL (activeSlug prop); inactive sections * render as navigation links so the browser handles routing. */ -export function SectionsAccordion({ sections, activeSlug, children }: Props) { +export function SectionAccordion({ sections, activeSlug, children }: Props) { const slots = Children.toArray(children); return (
{sections.map((section, i) => ( - {slots[i]} - + ))}
); diff --git a/src/widgets/SectionsAccordion/index.ts b/src/widgets/SectionsAccordion/index.ts deleted file mode 100644 index 4a468c4..0000000 --- a/src/widgets/SectionsAccordion/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { SectionsAccordion } from './ui/SectionsAccordion/SectionsAccordion';