diff --git a/inertia/components/add-media-dialog.stories.tsx b/inertia/components/add-media-dialog.stories.tsx new file mode 100644 index 0000000..e731cf5 --- /dev/null +++ b/inertia/components/add-media-dialog.stories.tsx @@ -0,0 +1,90 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { action } from 'storybook/actions' +import { AddMediaDialog } from './add-media-dialog' +import type { QualityProfile } from './add-media-dialog' + +const sampleProfiles: QualityProfile[] = [ + { id: '1', name: 'HD-1080p', mediaType: 'movie' }, + { id: '2', name: 'HD-720p', mediaType: 'movie' }, + { id: '3', name: 'SD', mediaType: 'movie' }, +] + +const meta: Meta = { + component: AddMediaDialog, + tags: ['autodocs'], + args: { + open: true, + onOpenChange: action('onOpenChange'), + onAdd: action('onAdd'), + qualityProfiles: sampleProfiles, + }, + argTypes: { + mediaType: { + control: 'select', + options: ['artist', 'album', 'movie', 'tvshow', 'author', 'book'], + }, + loading: { control: 'boolean' }, + adding: { control: 'boolean' }, + }, +} +export default meta + +type Story = StoryObj + +export const Movie: Story = { + args: { + mediaType: 'movie', + title: 'Inception', + description: 'Add this movie to your library and start monitoring for downloads.', + }, +} + +export const Album: Story = { + args: { + mediaType: 'album', + title: 'Dark Side of the Moon', + description: 'Add this album to your library.', + qualityProfiles: [ + { id: '1', name: 'Lossless', mediaType: 'album' }, + { id: '2', name: 'High Quality', mediaType: 'album' }, + { id: '3', name: 'Standard', mediaType: 'album' }, + ], + }, +} + +export const TvShowWithEpisodes: Story = { + args: { + mediaType: 'tvshow', + title: 'Breaking Bad', + description: 'Add this TV show to your library.', + episodeSelectionSummary: '62 of 62 episodes selected (5 seasons)', + onChangeEpisodeSelection: action('onChangeEpisodeSelection'), + }, +} + +export const AuthorWithBooks: Story = { + args: { + mediaType: 'author', + title: 'Frank Herbert', + description: 'Add this author to your library.', + showAddBooksOption: true, + }, +} + +export const Loading: Story = { + args: { + mediaType: 'movie', + title: 'Inception', + description: 'Add this movie to your library.', + loading: true, + }, +} + +export const Adding: Story = { + args: { + mediaType: 'movie', + title: 'Inception', + description: 'Add this movie to your library.', + adding: true, + }, +} diff --git a/inertia/components/confirm-dialog.stories.tsx b/inertia/components/confirm-dialog.stories.tsx new file mode 100644 index 0000000..dc0e25b --- /dev/null +++ b/inertia/components/confirm-dialog.stories.tsx @@ -0,0 +1,53 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { action } from 'storybook/actions' +import { ConfirmDialog } from './confirm-dialog' + +const meta: Meta = { + component: ConfirmDialog, + tags: ['autodocs'], + args: { + close: action('close'), + handleConfirm: action('handleConfirm'), + loading: false, + }, +} +export default meta + +type Story = StoryObj + +export const Default: Story = { + args: { + state: { + open: true, + title: 'Remove from library?', + description: 'This action cannot be undone. The item will be removed from your library.', + confirmLabel: 'Remove', + loadingLabel: 'Removing...', + }, + }, +} + +export const Loading: Story = { + args: { + loading: true, + state: { + open: true, + title: 'Delete all files?', + description: 'This will permanently delete all selected files from disk.', + confirmLabel: 'Delete All', + loadingLabel: 'Deleting...', + }, + }, +} + +export const RefreshLibrary: Story = { + args: { + state: { + open: true, + title: 'Refresh library?', + description: 'This will rescan all media folders and update your library. This may take a while.', + confirmLabel: 'Refresh', + loadingLabel: 'Refreshing...', + }, + }, +} diff --git a/inertia/components/error-boundary.stories.tsx b/inertia/components/error-boundary.stories.tsx new file mode 100644 index 0000000..789f792 --- /dev/null +++ b/inertia/components/error-boundary.stories.tsx @@ -0,0 +1,62 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { ErrorBoundary } from './error-boundary' + +const meta: Meta = { + component: ErrorBoundary, + tags: ['autodocs'], + parameters: { + layout: 'centered', + }, +} +export default meta + +type Story = StoryObj + +function ThrowError(): never { + throw new Error('Test error for Storybook') +} + +export const Default: Story = { + render: () => ( + + + + ), +} + +export const FullPage: Story = { + parameters: { + layout: 'fullscreen', + }, + render: () => ( + + + + ), +} + +export const CustomFallback: Story = { + render: () => ( + + Custom error fallback content + + } + > + + + ), +} + +export const NoError: Story = { + render: () => ( + +
+

+ This content renders normally when there is no error. +

+
+
+ ), +} diff --git a/inertia/components/library/delete-media-dialog.stories.tsx b/inertia/components/library/delete-media-dialog.stories.tsx new file mode 100644 index 0000000..67e5b14 --- /dev/null +++ b/inertia/components/library/delete-media-dialog.stories.tsx @@ -0,0 +1,65 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { action } from 'storybook/actions' +import { DeleteMediaDialog } from './delete-media-dialog' + +const meta: Meta = { + component: DeleteMediaDialog, + tags: ['autodocs'], + args: { + open: true, + onOpenChange: action('onOpenChange'), + onConfirm: () => new Promise((r) => setTimeout(r, 1000)), + }, + argTypes: { + mode: { control: 'select', options: ['remove', 'deleteFile'] }, + hasFile: { control: 'boolean' }, + }, +} +export default meta + +type Story = StoryObj + +export const RemoveWithFile: Story = { + args: { + title: 'Inception', + mediaType: 'movie', + hasFile: true, + mode: 'remove', + }, +} + +export const RemoveWithoutFile: Story = { + args: { + title: 'The Matrix', + mediaType: 'movie', + hasFile: false, + mode: 'remove', + }, +} + +export const DeleteFileMode: Story = { + args: { + title: 'Dark Side of the Moon', + mediaType: 'album', + hasFile: true, + mode: 'deleteFile', + }, +} + +export const BookRemove: Story = { + args: { + title: 'Dune', + mediaType: 'book', + hasFile: true, + mode: 'remove', + }, +} + +export const EpisodeDeleteFile: Story = { + args: { + title: 'S01E01 - Pilot', + mediaType: 'episode', + hasFile: true, + mode: 'deleteFile', + }, +} diff --git a/inertia/components/library/download-progress-card.stories.tsx b/inertia/components/library/download-progress-card.stories.tsx new file mode 100644 index 0000000..e707db8 --- /dev/null +++ b/inertia/components/library/download-progress-card.stories.tsx @@ -0,0 +1,72 @@ +import type { Meta, StoryObj } from '@storybook/react' +import { DownloadProgressCard } from './download-progress-card' +import type { ActiveDownloadInfo } from '@/hooks/use_active_downloads' + +const meta: Meta = { + component: DownloadProgressCard, + tags: ['autodocs'], + decorators: [ + (Story) => ( +
+ +
+ ), + ], +} +export default meta + +type Story = StoryObj + +const downloading: ActiveDownloadInfo = { + progress: 45, + status: 'downloading', + title: 'Inception.2010.1080p.BluRay.x264', + size: 8_589_934_592, + remaining: 4_724_464_025, + eta: 3725, + downloadClient: 'SABnzbd', +} + +const almostDone: ActiveDownloadInfo = { + progress: 92, + status: 'downloading', + title: 'Breaking.Bad.S01E01.720p', + size: 1_073_741_824, + remaining: 85_899_345, + eta: 120, + downloadClient: 'SABnzbd', +} + +const importing: ActiveDownloadInfo = { + progress: 100, + status: 'importing', + title: 'Dark.Side.of.the.Moon.FLAC', + size: 734_003_200, + remaining: 0, + eta: null, + downloadClient: 'SABnzbd', +} + +export const SingleDownload: Story = { + args: { + downloads: [downloading], + }, +} + +export const MultipleDownloads: Story = { + args: { + downloads: [downloading, almostDone, importing], + }, +} + +export const Importing: Story = { + args: { + downloads: [importing], + }, +} + +export const Empty: Story = { + args: { + downloads: [], + }, +}