diff --git a/src/components/Item/Item.module.scss b/src/components/Item/Item.module.scss index f348062..32fc906 100644 --- a/src/components/Item/Item.module.scss +++ b/src/components/Item/Item.module.scss @@ -14,6 +14,24 @@ opacity: calc(var(--opacity-50) / 100); pointer-events: none; } + + // Compact density overrides + &[data-size='compact'] { + padding-block: var(--spacing-xs); + padding-inline-start: var(--spacing-md); + + .content { + gap: var(--spacing-px); + } + + .title { + @include body; + } + + .description { + @include body-sm; + } + } } .clickable { diff --git a/src/components/Item/Item.stories.tsx b/src/components/Item/Item.stories.tsx index 8e5969f..132ba7a 100644 --- a/src/components/Item/Item.stories.tsx +++ b/src/components/Item/Item.stories.tsx @@ -13,6 +13,10 @@ const meta: Meta = { argTypes: { title: { control: 'text' }, description: { control: 'text' }, + size: { + control: 'radio', + options: ['default', 'compact'], + }, clickable: { control: 'boolean' }, selected: { control: 'boolean' }, disabled: { control: 'boolean' }, @@ -88,3 +92,41 @@ function SelectableListComponent() { export const SelectableList: Story = { render: () => , }; + +const transactions = [ + { id: 'tx-1', title: 'Payment received', description: '2 minutes ago', amount: '+$120.00' }, + { id: 'tx-2', title: 'Invoice paid', description: '1 hour ago', amount: '-$45.50' }, + { id: 'tx-3', title: 'Transfer sent', description: 'Yesterday', amount: '-$300.00' }, +]; + +function SizeComparisonComponent() { + return ( +
+ {(['default', 'compact'] as const).map((size) => ( +
+ {transactions.map((transaction) => ( + {transaction.amount}} + /> + ))} +
+ ))} +
+ ); +} + +export const SizeComparison: Story = { + parameters: { + docs: { + description: { + story: + 'Default and compact densities side by side. Compact tightens padding, gap, and type scale for dense lists such as transaction rows.', + }, + }, + }, + render: () => , +}; diff --git a/src/components/Item/Item.test-stories.tsx b/src/components/Item/Item.test-stories.tsx index 0191b49..17b8bd6 100644 --- a/src/components/Item/Item.test-stories.tsx +++ b/src/components/Item/Item.test-stories.tsx @@ -58,6 +58,15 @@ export function NonClickableItem() { return ; } +// Sizes +export function CompactItem() { + return ; +} + +export function DefaultSizeItem() { + return ; +} + // States export function SelectedItem() { return ( diff --git a/src/components/Item/Item.test.tsx b/src/components/Item/Item.test.tsx index e3ce687..053011e 100644 --- a/src/components/Item/Item.test.tsx +++ b/src/components/Item/Item.test.tsx @@ -5,6 +5,8 @@ import { ItemWithLeading, ItemWithTrailing, ItemWithBothSlots, + CompactItem, + DefaultSizeItem, ClickableItem, NonClickableItem, SelectedItem, @@ -49,6 +51,19 @@ test.describe('Item', () => { }); }); + test.describe('Sizes', () => { + test('compact item has data-size="compact"', async ({ mount, page }) => { + await mount(); + await expect(page.locator('[data-size="compact"]')).toBeVisible(); + }); + + test('default item has no data-size attribute', async ({ mount, page }) => { + await mount(); + await expect(page.getByText('Payment received')).toBeVisible(); + await expect(page.locator('[data-size]')).toHaveCount(0); + }); + }); + test.describe('Clickable behavior', () => { test('clickable item responds to click', async ({ mount, page }) => { await mount(); diff --git a/src/components/Item/Item.tsx b/src/components/Item/Item.tsx index cbf042e..64b677d 100644 --- a/src/components/Item/Item.tsx +++ b/src/components/Item/Item.tsx @@ -4,12 +4,16 @@ import * as React from 'react'; import clsx from 'clsx'; import styles from './Item.module.scss'; +export type ItemSize = 'default' | 'compact'; + export interface ItemProps extends Omit, 'title'> { title: string; description?: string; leading?: React.ReactNode; trailing?: React.ReactNode; trailingPadding?: 'sm' | 'lg'; + /** Item density — compact reduces padding and type scale for dense lists */ + size?: ItemSize; clickable?: boolean; selected?: boolean; disabled?: boolean; @@ -24,6 +28,7 @@ export const Item = React.forwardRef( leading, trailing, trailingPadding, + size = 'default', clickable = true, selected, disabled, @@ -51,6 +56,7 @@ export const Item = React.forwardRef( const itemProps = { ref: forwardedRef, className: clsx(styles.root, clickable && styles.clickable, className), + 'data-size': size !== 'default' ? size : undefined, 'data-clickable': clickable ? undefined : 'false', 'data-selected': selected || undefined, 'data-disabled': disabled || undefined, diff --git a/src/components/Item/index.ts b/src/components/Item/index.ts index e4f285d..6f6e98a 100644 --- a/src/components/Item/index.ts +++ b/src/components/Item/index.ts @@ -1,2 +1,2 @@ export { Item } from './Item'; -export type { ItemProps } from './Item'; +export type { ItemProps, ItemSize } from './Item'; diff --git a/src/index.ts b/src/index.ts index 282bb56..9a36231 100644 --- a/src/index.ts +++ b/src/index.ts @@ -186,7 +186,7 @@ export type { CentralIconName, CentralIconProps } from './components/Icon'; export { Input, type InputProps } from './components/Input'; -export { Item, type ItemProps } from './components/Item'; +export { Item, type ItemProps, type ItemSize } from './components/Item'; export { Logo } from './components/Logo'; export type { LogoProps } from './components/Logo';