chore: format codebase and move SectionAccordion to entities/Section
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
export { Section, Container } from './ui/Section'
|
||||
export type { SectionBackground, ContainerSize } from './ui/Section'
|
||||
export { Section, Container } from './ui/Section';
|
||||
export type { SectionBackground, ContainerSize } from './ui/Section';
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
||||
import { Section, Container } from './Section'
|
||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite';
|
||||
import { Section, Container } from './Section';
|
||||
|
||||
const meta: Meta<typeof Section> = {
|
||||
title: 'Shared/Section',
|
||||
component: Section,
|
||||
}
|
||||
};
|
||||
|
||||
export default meta
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof Section>
|
||||
type Story = StoryObj<typeof Section>;
|
||||
|
||||
export const AllBackgrounds: Story = {
|
||||
render: () => (
|
||||
@@ -16,32 +16,44 @@ export const AllBackgrounds: Story = {
|
||||
<Section background="ochre" className="py-12">
|
||||
<Container>
|
||||
<h2>Ochre Section</h2>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et
|
||||
dolore magna aliqua.
|
||||
</p>
|
||||
</Container>
|
||||
</Section>
|
||||
<Section background="slate" className="py-12">
|
||||
<Container>
|
||||
<h2>Slate Section</h2>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et
|
||||
dolore magna aliqua.
|
||||
</p>
|
||||
</Container>
|
||||
</Section>
|
||||
<Section background="white" className="py-12">
|
||||
<Container>
|
||||
<h2>White Section</h2>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et
|
||||
dolore magna aliqua.
|
||||
</p>
|
||||
</Container>
|
||||
</Section>
|
||||
</div>
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
export const Bordered: Story = {
|
||||
render: () => (
|
||||
<Section background="ochre" bordered className="py-12">
|
||||
<Container>
|
||||
<h2>Bordered Section</h2>
|
||||
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore
|
||||
magna aliqua.
|
||||
</p>
|
||||
</Container>
|
||||
</Section>
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,95 +1,103 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { Section, Container } from './Section'
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { Section, Container } from './Section';
|
||||
|
||||
describe('Section', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders a section element', () => {
|
||||
const { container } = render(<Section>content</Section>)
|
||||
expect(container.querySelector('section')).toBeInTheDocument()
|
||||
})
|
||||
const { container } = render(<Section>content</Section>);
|
||||
expect(container.querySelector('section')).toBeInTheDocument();
|
||||
});
|
||||
it('renders children', () => {
|
||||
render(<Section><span>hello</span></Section>)
|
||||
expect(screen.getByText('hello')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
render(
|
||||
<Section>
|
||||
<span>hello</span>
|
||||
</Section>,
|
||||
);
|
||||
expect(screen.getByText('hello')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('background variants', () => {
|
||||
it('defaults to ochre background', () => {
|
||||
const { container } = render(<Section>x</Section>)
|
||||
expect(container.querySelector('section')).toHaveClass('bg-ochre-clay', 'text-carbon-black')
|
||||
})
|
||||
const { container } = render(<Section>x</Section>);
|
||||
expect(container.querySelector('section')).toHaveClass('bg-ochre-clay', 'text-carbon-black');
|
||||
});
|
||||
it('applies slate background', () => {
|
||||
const { container } = render(<Section background="slate">x</Section>)
|
||||
expect(container.querySelector('section')).toHaveClass('bg-slate-indigo', 'text-ochre-clay')
|
||||
})
|
||||
const { container } = render(<Section background="slate">x</Section>);
|
||||
expect(container.querySelector('section')).toHaveClass('bg-slate-indigo', 'text-ochre-clay');
|
||||
});
|
||||
it('applies white background', () => {
|
||||
const { container } = render(<Section background="white">x</Section>)
|
||||
expect(container.querySelector('section')).toHaveClass('bg-white', 'text-carbon-black')
|
||||
})
|
||||
})
|
||||
const { container } = render(<Section background="white">x</Section>);
|
||||
expect(container.querySelector('section')).toHaveClass('bg-white', 'text-carbon-black');
|
||||
});
|
||||
});
|
||||
|
||||
describe('bordered', () => {
|
||||
it('no border classes by default', () => {
|
||||
const { container } = render(<Section>x</Section>)
|
||||
const el = container.querySelector('section')!
|
||||
expect(el).not.toHaveClass('brutal-border-top')
|
||||
expect(el).not.toHaveClass('brutal-border-bottom')
|
||||
})
|
||||
const { container } = render(<Section>x</Section>);
|
||||
const el = container.querySelector('section')!;
|
||||
expect(el).not.toHaveClass('brutal-border-top');
|
||||
expect(el).not.toHaveClass('brutal-border-bottom');
|
||||
});
|
||||
it('adds top and bottom borders when bordered=true', () => {
|
||||
const { container } = render(<Section bordered>x</Section>)
|
||||
const el = container.querySelector('section')!
|
||||
expect(el).toHaveClass('brutal-border-top')
|
||||
expect(el).toHaveClass('brutal-border-bottom')
|
||||
})
|
||||
})
|
||||
const { container } = render(<Section bordered>x</Section>);
|
||||
const el = container.querySelector('section')!;
|
||||
expect(el).toHaveClass('brutal-border-top');
|
||||
expect(el).toHaveClass('brutal-border-bottom');
|
||||
});
|
||||
});
|
||||
|
||||
describe('className', () => {
|
||||
it('applies custom className', () => {
|
||||
const { container } = render(<Section className="py-16">x</Section>)
|
||||
expect(container.querySelector('section')).toHaveClass('py-16')
|
||||
})
|
||||
})
|
||||
})
|
||||
const { container } = render(<Section className="py-16">x</Section>);
|
||||
expect(container.querySelector('section')).toHaveClass('py-16');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Container', () => {
|
||||
describe('rendering', () => {
|
||||
it('renders a div with children', () => {
|
||||
render(<Container><span>inner</span></Container>)
|
||||
expect(screen.getByText('inner')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
render(
|
||||
<Container>
|
||||
<span>inner</span>
|
||||
</Container>,
|
||||
);
|
||||
expect(screen.getByText('inner')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('size variants', () => {
|
||||
it('defaults to max-w-7xl', () => {
|
||||
const { container } = render(<Container>x</Container>)
|
||||
expect(container.firstChild).toHaveClass('max-w-7xl')
|
||||
})
|
||||
const { container } = render(<Container>x</Container>);
|
||||
expect(container.firstChild).toHaveClass('max-w-7xl');
|
||||
});
|
||||
it('wide applies max-w-[1920px]', () => {
|
||||
const { container } = render(<Container size="wide">x</Container>)
|
||||
expect(container.firstChild).toHaveClass('max-w-[1920px]')
|
||||
})
|
||||
const { container } = render(<Container size="wide">x</Container>);
|
||||
expect(container.firstChild).toHaveClass('max-w-[1920px]');
|
||||
});
|
||||
it('ultra-wide applies max-w-[2560px]', () => {
|
||||
const { container } = render(<Container size="ultra-wide">x</Container>)
|
||||
expect(container.firstChild).toHaveClass('max-w-[2560px]')
|
||||
})
|
||||
})
|
||||
const { container } = render(<Container size="ultra-wide">x</Container>);
|
||||
expect(container.firstChild).toHaveClass('max-w-[2560px]');
|
||||
});
|
||||
});
|
||||
|
||||
describe('layout', () => {
|
||||
it('centers content horizontally', () => {
|
||||
const { container } = render(<Container>x</Container>)
|
||||
expect(container.firstChild).toHaveClass('mx-auto')
|
||||
})
|
||||
const { container } = render(<Container>x</Container>);
|
||||
expect(container.firstChild).toHaveClass('mx-auto');
|
||||
});
|
||||
it('applies horizontal padding', () => {
|
||||
const { container } = render(<Container>x</Container>)
|
||||
expect(container.firstChild).toHaveClass('px-6')
|
||||
})
|
||||
})
|
||||
const { container } = render(<Container>x</Container>);
|
||||
expect(container.firstChild).toHaveClass('px-6');
|
||||
});
|
||||
});
|
||||
|
||||
describe('className', () => {
|
||||
it('applies custom className', () => {
|
||||
const { container } = render(<Container className="my-custom">x</Container>)
|
||||
expect(container.firstChild).toHaveClass('my-custom')
|
||||
})
|
||||
})
|
||||
})
|
||||
const { container } = render(<Container className="my-custom">x</Container>);
|
||||
expect(container.firstChild).toHaveClass('my-custom');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,82 +1,72 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import { cn } from '$shared/lib'
|
||||
import type { ReactNode } from 'react';
|
||||
import { cn } from '$shared/lib';
|
||||
|
||||
export type SectionBackground = 'ochre' | 'slate' | 'white'
|
||||
export type ContainerSize = 'default' | 'wide' | 'ultra-wide'
|
||||
export type SectionBackground = 'ochre' | 'slate' | 'white';
|
||||
export type ContainerSize = 'default' | 'wide' | 'ultra-wide';
|
||||
|
||||
interface SectionProps {
|
||||
/**
|
||||
* Section content
|
||||
*/
|
||||
children: ReactNode
|
||||
children: ReactNode;
|
||||
/**
|
||||
* Background color variant
|
||||
* @default 'ochre'
|
||||
*/
|
||||
background?: SectionBackground
|
||||
background?: SectionBackground;
|
||||
/**
|
||||
* Adds top and bottom brutal borders
|
||||
* @default false
|
||||
*/
|
||||
bordered?: boolean
|
||||
bordered?: boolean;
|
||||
/**
|
||||
* CSS classes
|
||||
*/
|
||||
className?: string
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const BACKGROUNDS: Record<SectionBackground, string> = {
|
||||
ochre: 'bg-ochre-clay text-carbon-black',
|
||||
slate: 'bg-slate-indigo text-ochre-clay',
|
||||
white: 'bg-white text-carbon-black',
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Full-width page section with background and optional borders.
|
||||
*/
|
||||
export function Section({ children, background = 'ochre', bordered = false, className }: SectionProps) {
|
||||
return (
|
||||
<section
|
||||
className={cn(
|
||||
BACKGROUNDS[background],
|
||||
bordered && 'brutal-border-top brutal-border-bottom',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<section className={cn(BACKGROUNDS[background], bordered && 'brutal-border-top brutal-border-bottom', className)}>
|
||||
{children}
|
||||
</section>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
interface ContainerProps {
|
||||
/**
|
||||
* Container content
|
||||
*/
|
||||
children: ReactNode
|
||||
children: ReactNode;
|
||||
/**
|
||||
* Max-width constraint
|
||||
* @default 'default'
|
||||
*/
|
||||
size?: ContainerSize
|
||||
size?: ContainerSize;
|
||||
/**
|
||||
* CSS classes
|
||||
*/
|
||||
className?: string
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const SIZES: Record<ContainerSize, string> = {
|
||||
'default': 'max-w-7xl',
|
||||
'wide': 'max-w-[1920px]',
|
||||
default: 'max-w-7xl',
|
||||
wide: 'max-w-[1920px]',
|
||||
'ultra-wide': 'max-w-[2560px]',
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Centered content container with responsive horizontal padding.
|
||||
*/
|
||||
export function Container({ children, size = 'default', className }: ContainerProps) {
|
||||
return (
|
||||
<div className={cn(SIZES[size], 'mx-auto px-6 md:px-12 lg:px-16', className)}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
return <div className={cn(SIZES[size], 'mx-auto px-6 md:px-12 lg:px-16', className)}>{children}</div>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user