diff --git a/src/components/Divider/Divider.module.css b/src/components/Divider/Divider.module.css new file mode 100644 index 00000000..d2fa7808 --- /dev/null +++ b/src/components/Divider/Divider.module.css @@ -0,0 +1,16 @@ +.divider { + width: var(--width); + min-width: var(--min-width); + max-width: var(--max-width); + height: 1px; + margin: var(--margin-top) var(--margin-right) var(--margin-bottom) var(--margin-left); + border: none; +} + +.divider.gray { + background-color: var(--color-border); +} + +.divider.primary { + background-color: var(--color-primary); +} diff --git a/src/components/Divider/Divider.spec.tsx b/src/components/Divider/Divider.spec.tsx new file mode 100644 index 00000000..70f8ea84 --- /dev/null +++ b/src/components/Divider/Divider.spec.tsx @@ -0,0 +1,42 @@ +import { render, screen } from '@testing-library/react'; +import { Divider } from './Divider'; + +describe('', () => { + it('receives date attribute', async () => { + render(); + const divider = screen.getByTestId('divider'); + + expect(divider).toBeInTheDocument(); + }); + + it('receives max-width', async () => { + render(); + const divider = screen.getByTestId('divider'); + + expect(divider).toHaveStyle('--max-width: 400px'); + }); + + it('receives min-width', async () => { + render(); + const divider = screen.getByTestId('divider'); + + expect(divider).toHaveStyle('--min-width: 400px'); + }); + + it('receives width', async () => { + render(); + const divider = screen.getByTestId('divider'); + + expect(divider).toHaveStyle('--width: 400px'); + }); + + it('receives margins', async () => { + render(); + const divider = screen.getByTestId('divider'); + + expect(divider).toHaveStyle('--margin-top: var(--size-spacing-xxs)'); + expect(divider).toHaveStyle('--margin-right: var(--size-spacing-xs)'); + expect(divider).toHaveStyle('--margin-bottom: var(--size-spacing-sm)'); + expect(divider).toHaveStyle('--margin-left: var(--size-spacing-md)'); + }); +}); diff --git a/src/components/Divider/Divider.stories.tsx b/src/components/Divider/Divider.stories.tsx new file mode 100644 index 00000000..ac40ca75 --- /dev/null +++ b/src/components/Divider/Divider.stories.tsx @@ -0,0 +1,51 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { ComponentPropsWithoutRef } from 'react'; +import { Divider } from './Divider'; +import { Stack } from '../Stack/Stack'; + +export default { + component: Divider, +} satisfies Meta; + +type Props = ComponentPropsWithoutRef; + +const defaultArgs: Props = { + border: 'gray', +}; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => , + args: defaultArgs, +}; + +export const BorderVariants: Story = { + render: () => { + return ( + + + + + ); + }, +}; + +export const Margins: Story = { + render: (args) => , + args: { + ...defaultArgs, + mt: 'md', + mr: 'lg', + mb: 'xl', + ml: 'xxl', + }, +}; + +export const Width: Story = { + render: (args) => , + args: { + ...defaultArgs, + width: '50%', + }, +}; diff --git a/src/components/Divider/Divider.tsx b/src/components/Divider/Divider.tsx new file mode 100644 index 00000000..85644dd8 --- /dev/null +++ b/src/components/Divider/Divider.tsx @@ -0,0 +1,48 @@ +'use client'; + +import { clsx } from 'clsx'; +import { forwardRef, type HTMLAttributes } from 'react'; +import styles from './Divider.module.css'; +import { MarginProps, WidthProps } from '../../types/style'; +import { marginVariables, widthVariables } from '../../utils/style'; + +type AllowedHRAttributes = Omit, 'className'>; + +type Props = { + /** + * ボーダーの種類 + */ + border?: 'gray' | 'primary'; +} & WidthProps & + MarginProps & + AllowedHRAttributes; + +export const Divider = forwardRef( + ({ border = 'gray', m, mx, my, mt, mr, mb, ml, width, minWidth, maxWidth, ...rest }, ref) => { + return ( +
+ ); + }, +); + +Divider.displayName = 'Divider'; diff --git a/src/components/FlexItem/FlexItem.tsx b/src/components/FlexItem/FlexItem.tsx index 7b654a0d..2c2ea671 100644 --- a/src/components/FlexItem/FlexItem.tsx +++ b/src/components/FlexItem/FlexItem.tsx @@ -3,9 +3,8 @@ import { clsx } from 'clsx'; import { CSSProperties, forwardRef, type PropsWithChildren, type HTMLAttributes } from 'react'; import styles from './FlexItem.module.css'; -import { MarginProps, PaddingProps } from '../../types/style'; +import { CSSWitdh, MarginProps, PaddingProps, WidthProps } from '../../types/style'; import { marginVariables, paddingVariables } from '../../utils/style'; -import { CSSWitdh, CSSMaxWidth, CSSMinWidth } from '../../utils/types'; type FlexProperty = { grow?: number; @@ -21,17 +20,8 @@ type Props = { * @defaultValue none */ flex?: 'none' | FlexProperty; - /** - * 最小幅 - * @defaultValue auto - */ - minWidth?: CSSMinWidth; - /** - * 最大幅 - * @defaultValue none - */ - maxWidth?: CSSMaxWidth; -} & MarginProps & +} & Omit & + MarginProps & PaddingProps & AllowedDivAttributes; diff --git a/src/index.ts b/src/index.ts index d6a12e40..5f645e1d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ export { Box } from './components/Box/Box'; export { Button } from './components/Button/Button'; export { ButtonCard } from './components/ButtonCard/ButtonCard'; export { Center } from './components/Center/Center'; +export { Divider } from './components/Divider/Divider'; export { Heading } from './components/Heading/Heading'; export { LinkButton } from './components/Button/LinkButton'; export { ErrorMessage } from './components/ErrorMessage/ErrorMessage'; diff --git a/src/types/style.ts b/src/types/style.ts index e27b4594..22e8f459 100644 --- a/src/types/style.ts +++ b/src/types/style.ts @@ -128,3 +128,88 @@ export type RadiusProp = { }; export type BackgroundColor = 'primary' | 'primaryDarken' | 'accent' | 'accentDarken' | 'alert' | 'gray' | 'white'; + +export type CSSVariable = `var(--${string})`; + +// ref: https://developer.mozilla.org/en-US/docs/Web/CSS/flex-basis +export type CSSLength = + | `${string}cap` + | `${string}ch` + | `${string}em` + | `${string}ex` + | `${string}ic` + | `${string}lh` + | `${string}rcap` + | `${string}rem` + | `${string}rex` + | `${string}ric` + | `${string}rlh` + | `${string}vh` + | `${string}vmax` + | `${string}vmin` + | `${string}vw` + | `${string}vb` + | `${string}vi` + | `${string}cqw` + | `${string}cqh` + | `${string}cqi` + | `${string}cqb` + | `${string}cqmin` + | `${string}cqmax` + | `${string}px` + | `${string}cm` + | `${string}mm` + | `${string}q` + | `${string}in` + | `${string}pc` + | `${string}pt`; + +export type CSSPercentage = `${string}%`; + +export type CSSLengthPercentage = CSSLength | CSSPercentage; + +export type CSSWitdh = + | CSSLength + | CSSPercentage + | 'auto' + | 'fit-content' + | `fit-content(${CSSLengthPercentage})` + | 'min-content' + | 'max-content' + | CSSVariable; + +export type CSSMaxWidth = + | 'none' + | CSSLengthPercentage + | 'min-content' + | 'max-content' + | 'fit-content' + | `fit-content(${CSSLengthPercentage})` + | CSSVariable; + +export type CSSMinWidth = + | 'auto' + | CSSLengthPercentage + | 'min-content' + | 'max-content' + | 'fit-content' + | `fit-content(${CSSLengthPercentage})` + | CSSVariable; + +export type WidthProps = { + /** + * 幅を指定 + * @default 'auto' + */ + width?: CSSWitdh; + /** + * 最小幅 + * @default 'auto' + */ + minWidth?: CSSMinWidth; + /** + * 最大幅 + * @default 'none' + */ + maxWidth?: CSSMaxWidth; +}; diff --git a/src/utils/style.ts b/src/utils/style.ts index a1c5cf23..1edd037b 100644 --- a/src/utils/style.ts +++ b/src/utils/style.ts @@ -13,6 +13,9 @@ import type { HeadingLeading, TagLeading, TextColor, + CSSMinWidth, + CSSMaxWidth, + CSSWitdh, } from '../types/style'; import type { CSSProperties } from 'react'; @@ -239,3 +242,19 @@ export const gapVariables = (spacing?: Spacing) => { '--gap': spacing ? `var(--size-spacing-${spacing})` : '0', } as CSSProperties; }; + +export const widthVariables = ({ + width = 'auto', + minWidth = 'auto', + maxWidth = 'none', +}: { + width?: CSSWitdh; + minWidth?: CSSMinWidth; + maxWidth?: CSSMaxWidth; +}) => { + return { + '--width': width, + '--min-width': minWidth, + '--max-width': maxWidth, + } as CSSProperties; +}; diff --git a/src/utils/types.ts b/src/utils/types.ts index 2b6270dc..25541ca2 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -4,70 +4,3 @@ export type AllOrNone = T | Partial>; export type DistributiveOmit = T extends any ? Omit : never; export type HTMLTagname = keyof HTMLElementTagNameMap; - -export type CSSVariable = `var(--${string})`; - -// ref: https://developer.mozilla.org/en-US/docs/Web/CSS/flex-basis -export type CSSLength = - | `${string}cap` - | `${string}ch` - | `${string}em` - | `${string}ex` - | `${string}ic` - | `${string}lh` - | `${string}rcap` - | `${string}rem` - | `${string}rex` - | `${string}ric` - | `${string}rlh` - | `${string}vh` - | `${string}vmax` - | `${string}vmin` - | `${string}vw` - | `${string}vb` - | `${string}vi` - | `${string}cqw` - | `${string}cqh` - | `${string}cqi` - | `${string}cqb` - | `${string}cqmin` - | `${string}cqmax` - | `${string}px` - | `${string}cm` - | `${string}mm` - | `${string}q` - | `${string}in` - | `${string}pc` - | `${string}pt`; - -export type CSSPercentage = `${string}%`; - -export type CSSLengthPercentage = CSSLength | CSSPercentage; - -export type CSSWitdh = - | CSSLength - | CSSPercentage - | 'auto' - | 'fit-content' - | `fit-content(${CSSLengthPercentage})` - | 'min-content' - | 'max-content' - | CSSVariable; - -export type CSSMaxWidth = - | 'none' - | CSSLengthPercentage - | 'min-content' - | 'max-content' - | 'fit-content' - | `fit-content(${CSSLengthPercentage})` - | CSSVariable; - -export type CSSMinWidth = - | 'auto' - | CSSLengthPercentage - | 'min-content' - | 'max-content' - | 'fit-content' - | `fit-content(${CSSLengthPercentage})` - | CSSVariable;