diff --git a/packages/component-library/.storybook/main.ts b/packages/component-library/.storybook/main.ts index 3d17cfcd817..77afada2478 100644 --- a/packages/component-library/.storybook/main.ts +++ b/packages/component-library/.storybook/main.ts @@ -26,6 +26,7 @@ const config: StorybookConfig = { core: { disableTelemetry: true, }, + staticDirs: ['./public'], async viteFinal(config) { const { mergeConfig } = await import('vite'); diff --git a/packages/component-library/.storybook/public/_headers b/packages/component-library/.storybook/public/_headers new file mode 100644 index 00000000000..b6d78c4a340 --- /dev/null +++ b/packages/component-library/.storybook/public/_headers @@ -0,0 +1,9 @@ + +# /assets folder contain processed assets with a file hash +# They are safe for immutable caching, as filename change when content change + +/assets/* + Cache-Control: public + Cache-Control: max-age=365000000 + Cache-Control: immutable + diff --git a/packages/component-library/src/AlignedText.stories.tsx b/packages/component-library/src/AlignedText.stories.tsx new file mode 100644 index 00000000000..595eacba3e2 --- /dev/null +++ b/packages/component-library/src/AlignedText.stories.tsx @@ -0,0 +1,139 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { AlignedText } from './AlignedText'; + +const meta = { + title: 'AlignedText', + component: AlignedText, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + left: 'Label', + right: 'Value', + style: { width: 300, display: 'flex' }, + }, + parameters: { + docs: { + description: { + story: + 'AlignedText displays two pieces of content aligned on opposite sides.', + }, + }, + }, +}; + +export const TruncateLeft: Story = { + args: { + left: 'This is a very long label that should be truncated on the left side', + right: '$100.00', + truncate: 'left', + style: { width: 250, display: 'flex' }, + }, + parameters: { + docs: { + description: { + story: + 'When `truncate="left"`, the left content is truncated with ellipsis.', + }, + }, + }, +}; + +export const TruncateRight: Story = { + args: { + left: 'Short Label', + right: + 'This is a very long value that should be truncated on the right side', + truncate: 'right', + style: { width: 250, display: 'flex' }, + }, + parameters: { + docs: { + description: { + story: + 'When `truncate="right"`, the right content is truncated with ellipsis.', + }, + }, + }, +}; + +export const FinancialAmount: Story = { + args: { + left: 'Groceries', + right: '$1,234.56', + style: { width: 300, display: 'flex' }, + rightStyle: { fontWeight: 'bold' }, + }, + parameters: { + docs: { + description: { + story: + 'Example showing AlignedText used for displaying financial data.', + }, + }, + }, +}; + +export const WithCustomStyles: Story = { + args: { + left: 'Category', + right: 'Amount', + style: { + width: 300, + padding: 10, + backgroundColor: '#f5f5f5', + borderRadius: 4, + display: 'flex', + }, + leftStyle: { color: '#666', fontStyle: 'italic' }, + rightStyle: { color: '#333', fontWeight: 'bold' }, + }, +}; + +export const MultipleRows: Story = { + args: { + left: 'Income', + right: '$5,000.00', + }, + render: () => ( +
+ + + +
+ ), + parameters: { + docs: { + description: { + story: + 'Multiple AlignedText components stacked to create a summary view.', + }, + }, + }, +}; diff --git a/packages/component-library/src/Block.stories.tsx b/packages/component-library/src/Block.stories.tsx new file mode 100644 index 00000000000..1da39fa88a1 --- /dev/null +++ b/packages/component-library/src/Block.stories.tsx @@ -0,0 +1,111 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { Block } from './Block'; +import { theme } from './theme'; + +const meta = { + title: 'Block', + component: Block, + parameters: { + layout: 'centered', + }, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'This is a Block component', + }, + parameters: { + docs: { + description: { + story: + 'Block is a basic div wrapper that accepts Emotion CSS styles via the `style` prop.', + }, + }, + }, + tags: ['autodocs'], +}; + +export const WithStyles: Story = { + args: { + children: 'Styled Block', + style: { + padding: 20, + backgroundColor: theme.cardBackground, + borderRadius: 8, + border: `1px solid ${theme.cardBorder}`, + color: theme.pageText, + }, + }, +}; + +export const WithFlexLayout: Story = { + render: () => ( + + + Item 1 + + + Item 2 + + + Item 3 + + + ), + parameters: { + docs: { + description: { + story: 'Block components can be nested and styled with flexbox.', + }, + }, + }, +}; + +export const AsContainer: Story = { + args: { + children: 'Container Block', + style: { + width: 300, + padding: 25, + textAlign: 'center', + backgroundColor: theme.cardBackground, + border: `2px dashed ${theme.cardBorder}`, + borderRadius: 8, + color: theme.pageText, + }, + }, +}; diff --git a/packages/component-library/src/Button.stories.ts b/packages/component-library/src/Button.stories.ts index ff1e63dd149..9325f4b6d53 100644 --- a/packages/component-library/src/Button.stories.ts +++ b/packages/component-library/src/Button.stories.ts @@ -20,7 +20,6 @@ export default meta; type Story = StoryObj; -// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args export const Primary: Story = { args: { variant: 'primary', diff --git a/packages/component-library/src/Card.stories.tsx b/packages/component-library/src/Card.stories.tsx new file mode 100644 index 00000000000..edc3221bbfc --- /dev/null +++ b/packages/component-library/src/Card.stories.tsx @@ -0,0 +1,82 @@ +import { styles } from '@actual-app/components/styles'; +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { Card } from './Card'; +import { Paragraph } from './Paragraph'; +import { theme } from './theme'; + +const meta = { + title: 'Card', + component: Card, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'Card content goes here', + style: { + padding: 20, + width: 300, + color: theme.pageText, + }, + }, + parameters: { + docs: { + description: { + story: ` +Default Card component uses the following theme CSS variables: + - \`--color-cardBackground\` + - \`--color-cardBorder\` +`, + }, + }, + }, +}; + +export const WithCustomContent: Story = { + args: { + style: { + padding: 20, + width: 300, + color: theme.pageText, + }, + }, + render: args => ( + +

Card Title

+ + This is a card with more complex content including a title and + paragraph. + +
+ ), +}; + +export const Narrow: Story = { + args: { + children: 'Narrow card', + style: { + padding: 15, + width: 150, + color: theme.pageText, + }, + }, +}; + +export const Wide: Story = { + args: { + children: 'Wide card with more content space', + style: { + padding: 25, + width: 500, + color: theme.pageText, + }, + }, +}; diff --git a/packages/component-library/src/ColorPicker.stories.tsx b/packages/component-library/src/ColorPicker.stories.tsx new file mode 100644 index 00000000000..a838e644474 --- /dev/null +++ b/packages/component-library/src/ColorPicker.stories.tsx @@ -0,0 +1,108 @@ +import { useState } from 'react'; +import { ColorSwatch } from 'react-aria-components'; + +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { fn } from 'storybook/test'; + +import { Button } from './Button'; +import { ColorPicker } from './ColorPicker'; + +const meta = { + title: 'ColorPicker', + component: ColorPicker, + parameters: { + layout: 'centered', + }, + args: { + onChange: fn(), + children: , + }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + defaultValue: '#690CB0', + children: , + }, +}; + +export const WithColorSwatch: Story = { + args: { + defaultValue: '#1976D2', + children: ( + + ), + }, +}; + +export const CustomColorSet: Story = { + args: { + defaultValue: '#FF0000', + columns: 4, + colorset: [ + '#FF0000', + '#00FF00', + '#0000FF', + '#FFFF00', + '#FF00FF', + '#00FFFF', + '#FFA500', + '#800080', + ], + children: , + }, + parameters: { + docs: { + description: { + story: + 'ColorPicker with a custom color set and different column layout.', + }, + }, + }, +}; + +export const Controlled: Story = { + args: { + children: , + }, + render: () => { + const [color, setColor] = useState('#388E3C'); + return ( +
+ setColor(c.toString('hex'))}> + + + Selected: {color} +
+ ); + }, + parameters: { + docs: { + description: { + story: 'Controlled ColorPicker with external state management.', + }, + }, + }, +}; diff --git a/packages/component-library/src/FormError.stories.tsx b/packages/component-library/src/FormError.stories.tsx new file mode 100644 index 00000000000..a76ff9054f8 --- /dev/null +++ b/packages/component-library/src/FormError.stories.tsx @@ -0,0 +1,90 @@ +import type { Meta, StoryObj } from '@storybook/react-vite'; + +import { FormError } from './FormError'; +import { Input } from './Input'; +import { View } from './View'; + +const meta = { + title: 'FormError', + component: FormError, + parameters: { + layout: 'centered', + }, + tags: ['autodocs'], +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + children: 'This field is required', + }, + parameters: { + docs: { + description: { + story: 'FormError displays validation error messages in red text.', + }, + }, + }, +}; + +export const InFormContext: Story = { + render: () => ( + + + Please enter a valid email address + + ), + parameters: { + docs: { + description: { + story: + 'FormError displayed below an input field with validation error.', + }, + }, + }, +}; + +export const MultipleErrors: Story = { + render: () => ( + + Password must be at least 8 characters + Password must contain a number + Password must contain a special character + + ), + parameters: { + docs: { + description: { + story: + 'Multiple FormError components for displaying several validation errors.', + }, + }, + }, +}; + +export const CustomStyle: Story = { + args: { + children: 'Custom styled error message', + style: { + fontSize: 14, + fontWeight: 'bold', + padding: 10, + backgroundColor: '#ffebee', + borderRadius: 4, + border: '1px solid red', + }, + }, +}; + +export const LongErrorMessage: Story = { + args: { + children: + 'This is a longer error message that explains the validation issue in more detail. Please correct the input and try again.', + style: { maxWidth: 300 }, + }, +}; diff --git a/upcoming-release-notes/6874.md b/upcoming-release-notes/6874.md new file mode 100644 index 00000000000..405dd0fa110 --- /dev/null +++ b/upcoming-release-notes/6874.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [MikesGlitch] +--- + +Adding more components to the Storybook docs & improving storybook caching