import { Meta, StoryObj } from '@storybook/react-webpack5'; import { useEffect, useState } from 'react'; import { Button } from '@@/buttons/Button'; import { FileNode } from '@@/form-components/FilePicker/types'; import { FilePicker } from './FilePicker'; import { FilePickerSkeleton } from './FilePickerSkeleton'; const meta: Meta = { title: 'Components/Forms/File Picker', component: FilePicker, tags: [], decorators: [ (Story, { viewMode }) => (
), ], parameters: { layout: 'fullscreen', docs: { description: { component: '`FilePicker` lets users select individual files or build wildcard expressions (e.g. `*.yml`, `src/**/*.ts`) from a repository tree. Selected items surface as **File** or **Expression** chips. A blue dot marks every tree row that is selected or matched by an expression. Typing in the filter bar shows a live preview of matching files and offers a one-click "Add expression (N matches)" button.', }, }, }, }; export default meta; type Story = StoryObj; // ─── Demo data ──────────────────────────────────────────────────────────────── const REPO_NODES: FileNode[] = [ { name: '.github', children: [ { name: 'workflows', children: [ { name: 'ci.yml' }, { name: 'deploy.yml' }, { name: 'release.yml' }, ], }, ], }, { name: 'config', children: [ { name: 'app.yml' }, { name: 'database.yml' }, { name: 'cache.yml' }, { name: 'logging.yml' }, ], }, { name: 'deploy', children: [ { name: 'k8s.yaml' }, { name: 'helm-values.yaml' }, { name: 'terraform.tfvars' }, ], }, { name: 'docs', children: [ { name: 'README.md' }, { name: 'architecture.md' }, { name: 'CONTRIBUTING.md' }, ], }, { name: 'scripts', children: [ { name: 'build.sh' }, { name: 'deploy.sh' }, { name: 'seed.sh' }, ], }, { name: 'src', children: [ { name: 'api', children: [ { name: 'routes.ts' }, { name: 'handlers.ts' }, { name: 'middleware.ts' }, ], }, { name: 'components', children: [ { name: 'Button.tsx' }, { name: 'Modal.tsx' }, { name: 'Table.tsx' }, ], }, { name: 'utils', children: [{ name: 'helpers.ts' }, { name: 'types.ts' }], }, ], }, { name: 'tests', children: [ { name: 'setup.ts' }, { name: 'integration.ts' }, { name: 'e2e.ts' }, ], }, { name: '.gitignore' }, { name: 'docker-compose.yml' }, { name: 'docker-compose.prod.yml' }, { name: 'docker-compose.staging.yml' }, { name: 'Dockerfile' }, { name: 'package.json' }, { name: 'tsconfig.json' }, ]; // ─── Skeleton ───────────────────────────────────────────────────────────────── export const Skeleton: StoryObj<{ loading: boolean }> = { render: () => { // eslint-disable-next-line react-hooks/rules-of-hooks const [loading, setLoading] = useState(true); // eslint-disable-next-line react-hooks/rules-of-hooks const [value, setValue] = useState([]); // eslint-disable-next-line react-hooks/rules-of-hooks useEffect(() => { const id = setTimeout(() => setLoading(false), 2000); return () => clearTimeout(id); }, []); return (
{loading ? ( ) : ( )}
); }, parameters: { docs: { description: { story: 'Shows a skeleton placeholder while file tree data is loading, then transitions to the real `FilePicker` after 2 seconds. Reload the story to replay the transition.', }, }, }, }; // ─── Default ────────────────────────────────────────────────────────────────── export const Default: Story = { args: { files: REPO_NODES, exampleExpressions: ['*.yml', '*.yaml', '*.ts', 'src/**', 'deploy/**'], }, render: (args) => { // eslint-disable-next-line react-hooks/rules-of-hooks const [value, setValue] = useState([]); return (
); }, parameters: { docs: { description: { story: 'Default state — header shows total file count, suggested pattern chips for one-click expression adding, and an empty tree with all folders collapsed. Click a pattern chip (e.g. `*.yml`) to add it as an expression immediately.', }, source: { code: ``, }, }, }, };