create-nexst-monorepo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1727 -0
- package/package.json +37 -0
- package/template/.agents/plugins/marketplace.json +21 -0
- package/template/.claude/settings.json +6 -0
- package/template/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/template/.claude/skills/frontend-design/SKILL.md +154 -0
- package/template/.claude/skills/theme-factory/SKILL.md +95 -0
- package/template/.claude/skills/theme-factory/references/schema.md +104 -0
- package/template/.claude/skills/theme-factory/scripts/theme.mjs +261 -0
- package/template/.dockerignore +43 -0
- package/template/.editorconfig +10 -0
- package/template/.github/workflows/ci.yaml +289 -0
- package/template/.github/workflows/publish-app.yaml +66 -0
- package/template/.github/workflows/publish.yaml +148 -0
- package/template/.github/workflows/snyk.yaml +34 -0
- package/template/.vscode/settings.json +8 -0
- package/template/AGENTS.md +42 -0
- package/template/Dockerfile.base +9 -0
- package/template/README.md +54 -0
- package/template/_gitignore +10 -0
- package/template/apps/api/.env.example +32 -0
- package/template/apps/api/AGENTS.md +44 -0
- package/template/apps/api/Dockerfile +69 -0
- package/template/apps/api/_gitignore +8 -0
- package/template/apps/api/knip.config.ts +22 -0
- package/template/apps/api/kustomize/base/application.properties +16 -0
- package/template/apps/api/kustomize/base/deployment.yaml +94 -0
- package/template/apps/api/kustomize/base/kustomization.yaml +17 -0
- package/template/apps/api/kustomize/base/service.yaml +10 -0
- package/template/apps/api/kustomize/overlays/prod/kustomization.yaml +17 -0
- package/template/apps/api/kustomize/overlays/staging/application.properties +1 -0
- package/template/apps/api/kustomize/overlays/staging/kustomization.yaml +21 -0
- package/template/apps/api/kustomize/secret.keys +14 -0
- package/template/apps/api/oxfmt.config.ts +8 -0
- package/template/apps/api/oxlint.config.ts +107 -0
- package/template/apps/api/package.json +79 -0
- package/template/apps/api/prisma/migrations/20260927000000_init/migration.sql +113 -0
- package/template/apps/api/prisma/migrations/migration_lock.toml +3 -0
- package/template/apps/api/prisma/schema.prisma +113 -0
- package/template/apps/api/prisma.config.ts +7 -0
- package/template/apps/api/scripts/auth.cli.ts +19 -0
- package/template/apps/api/scripts/seal-secret.sh +171 -0
- package/template/apps/api/sonar-project.properties +9 -0
- package/template/apps/api/src/app-info/app-info.dto.ts +10 -0
- package/template/apps/api/src/app-info/app-info.module.ts +7 -0
- package/template/apps/api/src/app-info/app-info.resolver.spec.ts +14 -0
- package/template/apps/api/src/app-info/app-info.resolver.ts +21 -0
- package/template/apps/api/src/app.module.spec.ts +93 -0
- package/template/apps/api/src/app.module.ts +77 -0
- package/template/apps/api/src/auth/auth.guard.spec.ts +90 -0
- package/template/apps/api/src/auth/auth.guard.ts +97 -0
- package/template/apps/api/src/auth/auth.ts +129 -0
- package/template/apps/api/src/auth/decorator/allow-anonymous.decorator.ts +17 -0
- package/template/apps/api/src/auth/decorator/is-email-verified.decorator.spec.ts +41 -0
- package/template/apps/api/src/auth/decorator/is-email-verified.decorator.ts +20 -0
- package/template/apps/api/src/auth/decorator/roles.decorator.ts +27 -0
- package/template/apps/api/src/auth/testing/make-session.ts +31 -0
- package/template/apps/api/src/auth/types/auth.types.ts +27 -0
- package/template/apps/api/src/bootstrap/register-graphql-enums.ts +12 -0
- package/template/apps/api/src/bootstrap/security.ts +24 -0
- package/template/apps/api/src/bootstrap/swagger.ts +28 -0
- package/template/apps/api/src/config/app.config.spec.ts +22 -0
- package/template/apps/api/src/config/app.config.ts +122 -0
- package/template/apps/api/src/config/graphql-depth-limit.ts +103 -0
- package/template/apps/api/src/config/graphql.config.ts +77 -0
- package/template/apps/api/src/config/resources/app.yaml +29 -0
- package/template/apps/api/src/errors/app-parse-uuid.pipe.ts +27 -0
- package/template/apps/api/src/errors/app.exception.ts +47 -0
- package/template/apps/api/src/errors/error-code.enum.ts +54 -0
- package/template/apps/api/src/errors/http-status-fallback-code.util.ts +27 -0
- package/template/apps/api/src/errors/validation-exception.factory.ts +43 -0
- package/template/apps/api/src/health/health.controller.spec.ts +82 -0
- package/template/apps/api/src/health/health.controller.ts +57 -0
- package/template/apps/api/src/health/health.module.ts +12 -0
- package/template/apps/api/src/logging/context-logger.ts +18 -0
- package/template/apps/api/src/logging/logger.config.ts +23 -0
- package/template/apps/api/src/logging/logging.module.ts +22 -0
- package/template/apps/api/src/mail/mail.module.ts +12 -0
- package/template/apps/api/src/mail/mail.service.spec.ts +47 -0
- package/template/apps/api/src/mail/mail.service.ts +123 -0
- package/template/apps/api/src/main.ts +41 -0
- package/template/apps/api/src/prisma/prisma.module.ts +12 -0
- package/template/apps/api/src/prisma/prisma.service.spec.ts +136 -0
- package/template/apps/api/src/prisma/prisma.service.ts +138 -0
- package/template/apps/api/src/queue/dto/enqueue-example.input.ts +11 -0
- package/template/apps/api/src/queue/example-queue.resolver.ts +14 -0
- package/template/apps/api/src/queue/example-queue.service.spec.ts +21 -0
- package/template/apps/api/src/queue/example-queue.service.ts +32 -0
- package/template/apps/api/src/queue/queue.module.ts +27 -0
- package/template/apps/api/src/r2/r2.module.ts +12 -0
- package/template/apps/api/src/r2/r2.service.spec.ts +117 -0
- package/template/apps/api/src/r2/r2.service.ts +131 -0
- package/template/apps/api/src/user/dto/user.dto.ts +26 -0
- package/template/apps/api/src/user/user.module.ts +10 -0
- package/template/apps/api/src/user/user.resolver.spec.ts +18 -0
- package/template/apps/api/src/user/user.resolver.ts +24 -0
- package/template/apps/api/src/utils/catch-everything-exception.filter.spec.ts +292 -0
- package/template/apps/api/src/utils/catch-everything-exception.filter.ts +70 -0
- package/template/apps/api/src/utils/error-handler.util.spec.ts +20 -0
- package/template/apps/api/src/utils/error-handler.util.ts +12 -0
- package/template/apps/api/src/utils/escape-like-pattern.util.spec.ts +29 -0
- package/template/apps/api/src/utils/escape-like-pattern.util.ts +11 -0
- package/template/apps/api/src/utils/pagination.input.ts +20 -0
- package/template/apps/api/src/utils/prisma-exception.filter.spec.ts +186 -0
- package/template/apps/api/src/utils/prisma-exception.filter.ts +45 -0
- package/template/apps/api/src/utils/prisma-graphql-exception.filter.spec.ts +216 -0
- package/template/apps/api/src/utils/prisma-graphql-exception.filter.ts +43 -0
- package/template/apps/api/tests/app.e2e-spec.ts +35 -0
- package/template/apps/api/tests/auth.e2e-spec.ts +38 -0
- package/template/apps/api/tests/helpers/api.ts +27 -0
- package/template/apps/api/tsconfig.json +11 -0
- package/template/apps/api/vitest.config.e2e.ts +16 -0
- package/template/apps/api/vitest.config.ts +40 -0
- package/template/apps/web/.env.example +7 -0
- package/template/apps/web/.storybook/decorators.tsx +38 -0
- package/template/apps/web/.storybook/main.ts +17 -0
- package/template/apps/web/.storybook/preview.ts +38 -0
- package/template/apps/web/.storybook/vitest.setup.ts +5 -0
- package/template/apps/web/AGENTS.md +29 -0
- package/template/apps/web/Dockerfile +49 -0
- package/template/apps/web/_gitignore +16 -0
- package/template/apps/web/knip.config.ts +15 -0
- package/template/apps/web/kustomize/base/application.properties +5 -0
- package/template/apps/web/kustomize/base/deployment.yaml +60 -0
- package/template/apps/web/kustomize/base/kustomization.yaml +17 -0
- package/template/apps/web/kustomize/base/service.yaml +10 -0
- package/template/apps/web/kustomize/overlays/prod/kustomization.yaml +12 -0
- package/template/apps/web/kustomize/overlays/staging/application.properties +2 -0
- package/template/apps/web/kustomize/overlays/staging/kustomization.yaml +18 -0
- package/template/apps/web/messages/de.json +124 -0
- package/template/apps/web/messages/en.json +124 -0
- package/template/apps/web/next-env.d.ts +7 -0
- package/template/apps/web/next.config.ts +39 -0
- package/template/apps/web/oxfmt.config.ts +9 -0
- package/template/apps/web/oxlint.config.ts +36 -0
- package/template/apps/web/package.json +80 -0
- package/template/apps/web/playwright.config.ts +34 -0
- package/template/apps/web/postcss.config.mjs +7 -0
- package/template/apps/web/sonar-project.properties +9 -0
- package/template/apps/web/src/app/[locale]/(auth)/recovery/page.tsx +22 -0
- package/template/apps/web/src/app/[locale]/(auth)/reset-password/page.tsx +29 -0
- package/template/apps/web/src/app/[locale]/(auth)/sign-in/page.tsx +24 -0
- package/template/apps/web/src/app/[locale]/(auth)/sign-up/page.tsx +24 -0
- package/template/apps/web/src/app/[locale]/(auth)/verify/page.tsx +24 -0
- package/template/apps/web/src/app/[locale]/dashboard/page.tsx +34 -0
- package/template/apps/web/src/app/[locale]/layout.tsx +60 -0
- package/template/apps/web/src/app/[locale]/page.tsx +23 -0
- package/template/apps/web/src/app/api/health/route.ts +3 -0
- package/template/apps/web/src/app/global-error.tsx +19 -0
- package/template/apps/web/src/app/globals.css +98 -0
- package/template/apps/web/src/app/icon.svg +1 -0
- package/template/apps/web/src/app/robots.ts +15 -0
- package/template/apps/web/src/app/sitemap.ts +24 -0
- package/template/apps/web/src/components/alert/alert.stories.tsx +25 -0
- package/template/apps/web/src/components/alert/alert.tsx +50 -0
- package/template/apps/web/src/components/avatar/avatar.stories.tsx +116 -0
- package/template/apps/web/src/components/avatar/avatar.tsx +88 -0
- package/template/apps/web/src/components/button/button-variants.ts +28 -0
- package/template/apps/web/src/components/button/button.stories.tsx +33 -0
- package/template/apps/web/src/components/button/button.tsx +32 -0
- package/template/apps/web/src/components/checkbox/checkbox.stories.tsx +312 -0
- package/template/apps/web/src/components/checkbox/checkbox.tsx +38 -0
- package/template/apps/web/src/components/dialog/danger-panel.stories.tsx +41 -0
- package/template/apps/web/src/components/dialog/danger-panel.tsx +32 -0
- package/template/apps/web/src/components/dialog/dialog.stories.tsx +239 -0
- package/template/apps/web/src/components/dialog/dialog.tsx +136 -0
- package/template/apps/web/src/components/dialog-buttons.stories.tsx +55 -0
- package/template/apps/web/src/components/dialog-buttons.tsx +48 -0
- package/template/apps/web/src/components/drawer/drawer.stories.tsx +251 -0
- package/template/apps/web/src/components/drawer/drawer.tsx +120 -0
- package/template/apps/web/src/components/dropdown-menu/dropdown-menu.stories.tsx +329 -0
- package/template/apps/web/src/components/dropdown-menu/dropdown-menu.tsx +263 -0
- package/template/apps/web/src/components/empty-state.stories.tsx +49 -0
- package/template/apps/web/src/components/empty-state.tsx +33 -0
- package/template/apps/web/src/components/field-group.stories.tsx +47 -0
- package/template/apps/web/src/components/field-group.tsx +20 -0
- package/template/apps/web/src/components/field-label.stories.tsx +34 -0
- package/template/apps/web/src/components/field-label.tsx +28 -0
- package/template/apps/web/src/components/icons.tsx +25 -0
- package/template/apps/web/src/components/input/input-otp.stories.tsx +86 -0
- package/template/apps/web/src/components/input/input-otp.tsx +184 -0
- package/template/apps/web/src/components/input/input.stories.tsx +35 -0
- package/template/apps/web/src/components/input/input.tsx +180 -0
- package/template/apps/web/src/components/popover/popover.stories.tsx +479 -0
- package/template/apps/web/src/components/popover/popover.tsx +45 -0
- package/template/apps/web/src/components/responsive-dialog/responsive-dialog.stories.tsx +307 -0
- package/template/apps/web/src/components/responsive-dialog/responsive-dialog.tsx +96 -0
- package/template/apps/web/src/components/searchbar/searchbar.stories.tsx +59 -0
- package/template/apps/web/src/components/searchbar/searchbar.tsx +64 -0
- package/template/apps/web/src/components/select/select.stories.tsx +293 -0
- package/template/apps/web/src/components/select/select.tsx +445 -0
- package/template/apps/web/src/components/skeleton/skeleton.stories.tsx +76 -0
- package/template/apps/web/src/components/skeleton/skeleton.tsx +13 -0
- package/template/apps/web/src/components/spinner/spinner.tsx +18 -0
- package/template/apps/web/src/components/switch/switch.stories.tsx +71 -0
- package/template/apps/web/src/components/switch/switch.tsx +35 -0
- package/template/apps/web/src/components/tabs/tabs.stories.tsx +209 -0
- package/template/apps/web/src/components/tabs/tabs.tsx +69 -0
- package/template/apps/web/src/components/tabs/url-tabs.stories.tsx +68 -0
- package/template/apps/web/src/components/tabs/url-tabs.tsx +42 -0
- package/template/apps/web/src/components/textarea/textarea.stories.tsx +144 -0
- package/template/apps/web/src/components/textarea/textarea.tsx +115 -0
- package/template/apps/web/src/components/toast/toaster.tsx +38 -0
- package/template/apps/web/src/components/tooltip/tooltip.stories.tsx +237 -0
- package/template/apps/web/src/components/tooltip/tooltip.tsx +49 -0
- package/template/apps/web/src/config/csp.ts +59 -0
- package/template/apps/web/src/config/routes.ts +14 -0
- package/template/apps/web/src/config/site.ts +5 -0
- package/template/apps/web/src/env.ts +15 -0
- package/template/apps/web/src/features/auth/auth-card.tsx +20 -0
- package/template/apps/web/src/features/auth/auth-divider.tsx +14 -0
- package/template/apps/web/src/features/auth/google-auth-button.tsx +49 -0
- package/template/apps/web/src/features/auth/google-one-tap.tsx +23 -0
- package/template/apps/web/src/features/auth/lib/auth-error.test.ts +22 -0
- package/template/apps/web/src/features/auth/lib/auth-error.ts +16 -0
- package/template/apps/web/src/features/auth/lib/return-to.test.ts +41 -0
- package/template/apps/web/src/features/auth/lib/return-to.ts +19 -0
- package/template/apps/web/src/features/auth/lib/validator.test.ts +45 -0
- package/template/apps/web/src/features/auth/lib/validator.ts +49 -0
- package/template/apps/web/src/features/auth/passkey-settings.tsx +106 -0
- package/template/apps/web/src/features/auth/recovery-form.tsx +61 -0
- package/template/apps/web/src/features/auth/resend-verification-button.tsx +36 -0
- package/template/apps/web/src/features/auth/reset-password-form.tsx +62 -0
- package/template/apps/web/src/features/auth/sign-in-form.stories.tsx +22 -0
- package/template/apps/web/src/features/auth/sign-in-form.tsx +116 -0
- package/template/apps/web/src/features/auth/sign-out-button.tsx +26 -0
- package/template/apps/web/src/features/auth/sign-up-form.tsx +107 -0
- package/template/apps/web/src/features/shared/provider.tsx +16 -0
- package/template/apps/web/src/features/shared/site-header.tsx +48 -0
- package/template/apps/web/src/features/shared/theme-toggle.tsx +24 -0
- package/template/apps/web/src/i18n/navigation.ts +5 -0
- package/template/apps/web/src/i18n/request.ts +21 -0
- package/template/apps/web/src/i18n/routing.ts +6 -0
- package/template/apps/web/src/lib/auth-client.ts +19 -0
- package/template/apps/web/src/lib/auth-server.ts +46 -0
- package/template/apps/web/src/lib/auth.ts +8 -0
- package/template/apps/web/src/lib/codegen.ts +30 -0
- package/template/apps/web/src/lib/graphql-client.ts +29 -0
- package/template/apps/web/src/lib/graphql-server.ts +28 -0
- package/template/apps/web/src/lib/graphql-utils.test.ts +32 -0
- package/template/apps/web/src/lib/graphql-utils.ts +76 -0
- package/template/apps/web/src/lib/hooks/use-media-query.ts +21 -0
- package/template/apps/web/src/lib/metadata.ts +17 -0
- package/template/apps/web/src/lib/network-error.test.ts +30 -0
- package/template/apps/web/src/lib/network-error.ts +17 -0
- package/template/apps/web/src/lib/proxy-utils.ts +42 -0
- package/template/apps/web/src/lib/query-client.ts +29 -0
- package/template/apps/web/src/lib/utils.ts +5 -0
- package/template/apps/web/src/proxy.ts +57 -0
- package/template/apps/web/test-results/.last-run.json +4 -0
- package/template/apps/web/tests/auth.spec.ts +17 -0
- package/template/apps/web/tests/csp-theme-script.spec.ts +37 -0
- package/template/apps/web/tests/home.spec.ts +25 -0
- package/template/apps/web/tsconfig.build.json +4 -0
- package/template/apps/web/tsconfig.json +28 -0
- package/template/apps/web/vitest.config.ts +40 -0
- package/template/apps/web/vitest.shims.d.ts +1 -0
- package/template/apps/worker/.env.example +4 -0
- package/template/apps/worker/AGENTS.md +11 -0
- package/template/apps/worker/Dockerfile +35 -0
- package/template/apps/worker/knip.config.ts +3 -0
- package/template/apps/worker/kustomize/base/application.properties +4 -0
- package/template/apps/worker/kustomize/base/deployment.yaml +62 -0
- package/template/apps/worker/kustomize/base/kustomization.yaml +16 -0
- package/template/apps/worker/kustomize/overlays/prod/kustomization.yaml +12 -0
- package/template/apps/worker/kustomize/overlays/staging/application.properties +1 -0
- package/template/apps/worker/kustomize/overlays/staging/kustomization.yaml +18 -0
- package/template/apps/worker/oxfmt.config.ts +8 -0
- package/template/apps/worker/oxlint.config.ts +15 -0
- package/template/apps/worker/package.json +33 -0
- package/template/apps/worker/sonar-project.properties +8 -0
- package/template/apps/worker/src/env.ts +13 -0
- package/template/apps/worker/src/lib/graceful-shutdown.ts +27 -0
- package/template/apps/worker/src/lib/logger.ts +20 -0
- package/template/apps/worker/src/main.ts +17 -0
- package/template/apps/worker/src/processors/example-processor.ts +15 -0
- package/template/apps/worker/src/worker/redis.ts +19 -0
- package/template/apps/worker/src/worker/worker.ts +37 -0
- package/template/apps/worker/tests/example-processor.test.ts +13 -0
- package/template/apps/worker/tests/graceful-shutdown.test.ts +34 -0
- package/template/apps/worker/tsconfig.json +4 -0
- package/template/apps/worker/vitest.config.ts +13 -0
- package/template/bun.lock +3053 -0
- package/template/bunfig.toml +2 -0
- package/template/compose.yaml +53 -0
- package/template/docker-bake.hcl +86 -0
- package/template/package.json +47 -0
- package/template/packages/lint/knip.ts +40 -0
- package/template/packages/lint/oxfmt.ts +8 -0
- package/template/packages/lint/oxlint.ts +136 -0
- package/template/packages/lint/package.json +13 -0
- package/template/packages/queues/oxfmt.config.ts +8 -0
- package/template/packages/queues/oxlint.config.ts +7 -0
- package/template/packages/queues/package.json +15 -0
- package/template/packages/queues/src/example-job.ts +11 -0
- package/template/packages/queues/src/index.ts +3 -0
- package/template/packages/queues/src/queue-names.ts +2 -0
- package/template/packages/queues/src/redis-connection.ts +27 -0
- package/template/packages/queues/tsconfig.json +4 -0
- package/template/packages/tsconfig/base.json +23 -0
- package/template/packages/tsconfig/node.json +11 -0
- package/template/packages/tsconfig/package.json +4 -0
- package/template/turbo.json +50 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
+
import { Filter, Menu, Settings, User } from "lucide-react";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { expect, fn, userEvent, within } from "storybook/test";
|
|
5
|
+
import { Button } from "../button/button";
|
|
6
|
+
import {
|
|
7
|
+
Drawer,
|
|
8
|
+
DrawerClose,
|
|
9
|
+
DrawerContent,
|
|
10
|
+
DrawerDescription,
|
|
11
|
+
DrawerFooter,
|
|
12
|
+
DrawerHeader,
|
|
13
|
+
DrawerTitle,
|
|
14
|
+
DrawerTrigger,
|
|
15
|
+
} from "./drawer";
|
|
16
|
+
|
|
17
|
+
const meta = {
|
|
18
|
+
component: Drawer,
|
|
19
|
+
parameters: {
|
|
20
|
+
layout: "centered",
|
|
21
|
+
viewport: {
|
|
22
|
+
defaultViewport: "mobile1",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
tags: ["autodocs"],
|
|
26
|
+
} satisfies Meta<typeof Drawer>;
|
|
27
|
+
|
|
28
|
+
export default meta;
|
|
29
|
+
type Story = StoryObj<typeof meta>;
|
|
30
|
+
|
|
31
|
+
export const Default: Story = {
|
|
32
|
+
args: {},
|
|
33
|
+
play: async ({ canvasElement }) => {
|
|
34
|
+
const canvas = within(canvasElement);
|
|
35
|
+
const trigger = canvas.getByRole("button", { name: /open drawer/i });
|
|
36
|
+
|
|
37
|
+
await expect(trigger).toBeInTheDocument();
|
|
38
|
+
|
|
39
|
+
await userEvent.click(trigger);
|
|
40
|
+
},
|
|
41
|
+
render: () => (
|
|
42
|
+
<Drawer>
|
|
43
|
+
<DrawerTrigger asChild>
|
|
44
|
+
<Button>
|
|
45
|
+
<Menu size={16} />
|
|
46
|
+
Open Drawer
|
|
47
|
+
</Button>
|
|
48
|
+
</DrawerTrigger>
|
|
49
|
+
<DrawerContent>
|
|
50
|
+
<DrawerHeader>
|
|
51
|
+
<DrawerTitle>Drawer Title</DrawerTitle>
|
|
52
|
+
<DrawerDescription>
|
|
53
|
+
This is a drawer component that slides up from the bottom of the screen.
|
|
54
|
+
</DrawerDescription>
|
|
55
|
+
</DrawerHeader>
|
|
56
|
+
<div className="px-4 py-6">
|
|
57
|
+
<p className="text-sm">
|
|
58
|
+
Drawers are commonly used on mobile devices to show additional options or forms.
|
|
59
|
+
</p>
|
|
60
|
+
</div>
|
|
61
|
+
<DrawerFooter>
|
|
62
|
+
<Button onClick={fn()}>Confirm</Button>
|
|
63
|
+
<DrawerClose asChild>
|
|
64
|
+
<Button variant="secondary">Cancel</Button>
|
|
65
|
+
</DrawerClose>
|
|
66
|
+
</DrawerFooter>
|
|
67
|
+
</DrawerContent>
|
|
68
|
+
</Drawer>
|
|
69
|
+
),
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const WithForm: Story = {
|
|
73
|
+
args: {},
|
|
74
|
+
render: () => {
|
|
75
|
+
const [open, setOpen] = useState(false);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Drawer onOpenChange={setOpen} open={open}>
|
|
79
|
+
<DrawerTrigger asChild>
|
|
80
|
+
<Button>
|
|
81
|
+
<User size={16} />
|
|
82
|
+
Edit Profile
|
|
83
|
+
</Button>
|
|
84
|
+
</DrawerTrigger>
|
|
85
|
+
<DrawerContent>
|
|
86
|
+
<DrawerHeader className="text-left">
|
|
87
|
+
<DrawerTitle>Edit Profile</DrawerTitle>
|
|
88
|
+
<DrawerDescription>Make changes to your profile information.</DrawerDescription>
|
|
89
|
+
</DrawerHeader>
|
|
90
|
+
<div className="space-y-4 px-4 py-4">
|
|
91
|
+
<div className="space-y-2">
|
|
92
|
+
<label className="text-sm font-medium" htmlFor="name">
|
|
93
|
+
Name
|
|
94
|
+
</label>
|
|
95
|
+
<input
|
|
96
|
+
aria-label="Name"
|
|
97
|
+
className="border-input bg-background ring-offset-background focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none"
|
|
98
|
+
defaultValue="John Doe"
|
|
99
|
+
id="name"
|
|
100
|
+
placeholder="Enter your name"
|
|
101
|
+
type="text"
|
|
102
|
+
/>
|
|
103
|
+
</div>
|
|
104
|
+
<div className="space-y-2">
|
|
105
|
+
<label className="text-sm font-medium" htmlFor="email">
|
|
106
|
+
Email
|
|
107
|
+
</label>
|
|
108
|
+
<input
|
|
109
|
+
aria-label="Email"
|
|
110
|
+
className="border-input bg-background ring-offset-background focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none"
|
|
111
|
+
defaultValue="john@example.com"
|
|
112
|
+
id="email"
|
|
113
|
+
placeholder="your.email@example.com"
|
|
114
|
+
type="email"
|
|
115
|
+
/>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
<DrawerFooter>
|
|
119
|
+
<Button onClick={() => setOpen(false)}>Save Changes</Button>
|
|
120
|
+
<DrawerClose asChild>
|
|
121
|
+
<Button variant="secondary">Cancel</Button>
|
|
122
|
+
</DrawerClose>
|
|
123
|
+
</DrawerFooter>
|
|
124
|
+
</DrawerContent>
|
|
125
|
+
</Drawer>
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const FilterDrawer: Story = {
|
|
131
|
+
args: {},
|
|
132
|
+
render: () => (
|
|
133
|
+
<Drawer>
|
|
134
|
+
<DrawerTrigger asChild>
|
|
135
|
+
<Button variant="outline">
|
|
136
|
+
<Filter size={16} />
|
|
137
|
+
Filters
|
|
138
|
+
</Button>
|
|
139
|
+
</DrawerTrigger>
|
|
140
|
+
<DrawerContent>
|
|
141
|
+
<DrawerHeader className="text-left">
|
|
142
|
+
<DrawerTitle>Filter Options</DrawerTitle>
|
|
143
|
+
<DrawerDescription>Customize your search with these filter options.</DrawerDescription>
|
|
144
|
+
</DrawerHeader>
|
|
145
|
+
<div className="space-y-4 px-4 py-4">
|
|
146
|
+
<div className="space-y-2">
|
|
147
|
+
<p className="text-sm font-medium">Category</p>
|
|
148
|
+
<select className="border-input bg-background ring-offset-background focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none">
|
|
149
|
+
<option>All Categories</option>
|
|
150
|
+
<option>Documents</option>
|
|
151
|
+
<option>Images</option>
|
|
152
|
+
<option>Videos</option>
|
|
153
|
+
</select>
|
|
154
|
+
</div>
|
|
155
|
+
<div className="space-y-2">
|
|
156
|
+
<p className="text-sm font-medium">Date Range</p>
|
|
157
|
+
<select className="border-input bg-background ring-offset-background focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-2 focus-visible:outline-none">
|
|
158
|
+
<option>Any Time</option>
|
|
159
|
+
<option>Last 7 days</option>
|
|
160
|
+
<option>Last 30 days</option>
|
|
161
|
+
<option>Last Year</option>
|
|
162
|
+
</select>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
<DrawerFooter>
|
|
166
|
+
<Button>Apply Filters</Button>
|
|
167
|
+
<DrawerClose asChild>
|
|
168
|
+
<Button variant="secondary">Reset</Button>
|
|
169
|
+
</DrawerClose>
|
|
170
|
+
</DrawerFooter>
|
|
171
|
+
</DrawerContent>
|
|
172
|
+
</Drawer>
|
|
173
|
+
),
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const SettingsDrawer: Story = {
|
|
177
|
+
args: {},
|
|
178
|
+
render: () => (
|
|
179
|
+
<Drawer>
|
|
180
|
+
<DrawerTrigger asChild>
|
|
181
|
+
<Button variant="ghost">
|
|
182
|
+
<Settings size={16} />
|
|
183
|
+
Settings
|
|
184
|
+
</Button>
|
|
185
|
+
</DrawerTrigger>
|
|
186
|
+
<DrawerContent>
|
|
187
|
+
<DrawerHeader className="text-left">
|
|
188
|
+
<DrawerTitle>Settings</DrawerTitle>
|
|
189
|
+
<DrawerDescription>Manage your application preferences.</DrawerDescription>
|
|
190
|
+
</DrawerHeader>
|
|
191
|
+
<div className="space-y-3 px-4 py-4">
|
|
192
|
+
<div className="border-border bg-accent/50 flex items-center justify-between rounded-lg border p-3">
|
|
193
|
+
<div>
|
|
194
|
+
<p className="text-sm font-medium">Enable Notifications</p>
|
|
195
|
+
<p className="text-muted-foreground text-xs">Receive updates about your activity</p>
|
|
196
|
+
</div>
|
|
197
|
+
<input
|
|
198
|
+
aria-label="Enable Notifications"
|
|
199
|
+
className="h-4 w-4"
|
|
200
|
+
defaultChecked
|
|
201
|
+
type="checkbox"
|
|
202
|
+
/>
|
|
203
|
+
</div>
|
|
204
|
+
<div className="border-border bg-accent/50 flex items-center justify-between rounded-lg border p-3">
|
|
205
|
+
<div>
|
|
206
|
+
<p className="text-sm font-medium">Dark Mode</p>
|
|
207
|
+
<p className="text-muted-foreground text-xs">Switch to dark theme</p>
|
|
208
|
+
</div>
|
|
209
|
+
<input aria-label="Dark Mode" className="h-4 w-4" type="checkbox" />
|
|
210
|
+
</div>
|
|
211
|
+
<div className="border-border bg-accent/50 flex items-center justify-between rounded-lg border p-3">
|
|
212
|
+
<div>
|
|
213
|
+
<p className="text-sm font-medium">Auto-Save</p>
|
|
214
|
+
<p className="text-muted-foreground text-xs">Automatically save changes</p>
|
|
215
|
+
</div>
|
|
216
|
+
<input aria-label="Auto-Save" className="h-4 w-4" defaultChecked type="checkbox" />
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
<DrawerFooter>
|
|
220
|
+
<DrawerClose asChild>
|
|
221
|
+
<Button>Close</Button>
|
|
222
|
+
</DrawerClose>
|
|
223
|
+
</DrawerFooter>
|
|
224
|
+
</DrawerContent>
|
|
225
|
+
</Drawer>
|
|
226
|
+
),
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export const NoDescription: Story = {
|
|
230
|
+
args: {},
|
|
231
|
+
render: () => (
|
|
232
|
+
<Drawer>
|
|
233
|
+
<DrawerTrigger asChild>
|
|
234
|
+
<Button variant="outline">Open Simple Drawer</Button>
|
|
235
|
+
</DrawerTrigger>
|
|
236
|
+
<DrawerContent>
|
|
237
|
+
<DrawerHeader className="text-left">
|
|
238
|
+
<DrawerTitle>Simple Drawer</DrawerTitle>
|
|
239
|
+
</DrawerHeader>
|
|
240
|
+
<div className="px-4 py-6">
|
|
241
|
+
<p className="text-sm">This drawer doesn't have a description.</p>
|
|
242
|
+
</div>
|
|
243
|
+
<DrawerFooter>
|
|
244
|
+
<DrawerClose asChild>
|
|
245
|
+
<Button>Close</Button>
|
|
246
|
+
</DrawerClose>
|
|
247
|
+
</DrawerFooter>
|
|
248
|
+
</DrawerContent>
|
|
249
|
+
</Drawer>
|
|
250
|
+
),
|
|
251
|
+
};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Drawer as DrawerPrimitive } from "vaul";
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
type DrawerProps = React.ComponentProps<typeof DrawerPrimitive.Root>;
|
|
8
|
+
|
|
9
|
+
const Drawer: React.FC<DrawerProps> = ({ shouldScaleBackground = true, ...props }) => {
|
|
10
|
+
return <DrawerPrimitive.Root shouldScaleBackground={shouldScaleBackground} {...props} />;
|
|
11
|
+
};
|
|
12
|
+
Drawer.displayName = "Drawer";
|
|
13
|
+
|
|
14
|
+
const DrawerTrigger = DrawerPrimitive.Trigger;
|
|
15
|
+
|
|
16
|
+
const DrawerPortal = DrawerPrimitive.Portal;
|
|
17
|
+
|
|
18
|
+
const DrawerClose = DrawerPrimitive.Close;
|
|
19
|
+
|
|
20
|
+
function DrawerOverlay({
|
|
21
|
+
className,
|
|
22
|
+
ref,
|
|
23
|
+
...props
|
|
24
|
+
}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay> & {
|
|
25
|
+
ref?: React.RefObject<null | React.ComponentRef<typeof DrawerPrimitive.Overlay>>;
|
|
26
|
+
}) {
|
|
27
|
+
return (
|
|
28
|
+
<DrawerPrimitive.Overlay
|
|
29
|
+
className={cn("fixed inset-0 z-50 bg-black/60 backdrop-blur-sm", className)}
|
|
30
|
+
ref={ref}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
|
|
36
|
+
|
|
37
|
+
function DrawerContent({
|
|
38
|
+
children,
|
|
39
|
+
className,
|
|
40
|
+
ref,
|
|
41
|
+
...props
|
|
42
|
+
}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> & {
|
|
43
|
+
ref?: React.RefObject<null | React.ComponentRef<typeof DrawerPrimitive.Content>>;
|
|
44
|
+
}) {
|
|
45
|
+
return (
|
|
46
|
+
<DrawerPortal>
|
|
47
|
+
<DrawerOverlay />
|
|
48
|
+
<DrawerPrimitive.Content
|
|
49
|
+
className={cn(
|
|
50
|
+
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-3xl border border-border/70 bg-popover shadow-2xl",
|
|
51
|
+
className,
|
|
52
|
+
)}
|
|
53
|
+
ref={ref}
|
|
54
|
+
{...props}
|
|
55
|
+
>
|
|
56
|
+
<div className="bg-border mx-auto mt-4 h-1.5 w-12 rounded-full" />
|
|
57
|
+
{children}
|
|
58
|
+
</DrawerPrimitive.Content>
|
|
59
|
+
</DrawerPortal>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
DrawerContent.displayName = "DrawerContent";
|
|
63
|
+
|
|
64
|
+
function DrawerHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
65
|
+
return <div className={cn("grid gap-2 p-4 text-center sm:text-left", className)} {...props} />;
|
|
66
|
+
}
|
|
67
|
+
DrawerHeader.displayName = "DrawerHeader";
|
|
68
|
+
|
|
69
|
+
function DrawerFooter({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
70
|
+
return <div className={cn("mt-auto flex flex-col gap-2 p-4", className)} {...props} />;
|
|
71
|
+
}
|
|
72
|
+
DrawerFooter.displayName = "DrawerFooter";
|
|
73
|
+
|
|
74
|
+
function DrawerTitle({
|
|
75
|
+
className,
|
|
76
|
+
ref,
|
|
77
|
+
...props
|
|
78
|
+
}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Title> & {
|
|
79
|
+
ref?: React.RefObject<null | React.ComponentRef<typeof DrawerPrimitive.Title>>;
|
|
80
|
+
}) {
|
|
81
|
+
return (
|
|
82
|
+
<DrawerPrimitive.Title
|
|
83
|
+
className={cn(
|
|
84
|
+
"text-xl font-semibold leading-tight tracking-tight text-foreground",
|
|
85
|
+
className,
|
|
86
|
+
)}
|
|
87
|
+
ref={ref}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
|
|
93
|
+
|
|
94
|
+
function DrawerDescription({
|
|
95
|
+
className,
|
|
96
|
+
ref,
|
|
97
|
+
...props
|
|
98
|
+
}: React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Description> & {
|
|
99
|
+
ref?: React.RefObject<null | React.ComponentRef<typeof DrawerPrimitive.Description>>;
|
|
100
|
+
}) {
|
|
101
|
+
return (
|
|
102
|
+
<DrawerPrimitive.Description
|
|
103
|
+
className={cn("text-sm leading-relaxed text-muted-foreground", className)}
|
|
104
|
+
ref={ref}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
|
|
110
|
+
|
|
111
|
+
export {
|
|
112
|
+
Drawer,
|
|
113
|
+
DrawerClose,
|
|
114
|
+
DrawerContent,
|
|
115
|
+
DrawerDescription,
|
|
116
|
+
DrawerFooter,
|
|
117
|
+
DrawerHeader,
|
|
118
|
+
DrawerTitle,
|
|
119
|
+
DrawerTrigger,
|
|
120
|
+
};
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/nextjs-vite";
|
|
2
|
+
import {
|
|
3
|
+
Cloud,
|
|
4
|
+
CreditCard,
|
|
5
|
+
Globe,
|
|
6
|
+
Keyboard,
|
|
7
|
+
LifeBuoy,
|
|
8
|
+
LogOut,
|
|
9
|
+
Mail,
|
|
10
|
+
MessageSquare,
|
|
11
|
+
Moon,
|
|
12
|
+
Plus,
|
|
13
|
+
PlusCircle,
|
|
14
|
+
Settings,
|
|
15
|
+
Sun,
|
|
16
|
+
Trash2,
|
|
17
|
+
User,
|
|
18
|
+
UserPlus,
|
|
19
|
+
Users,
|
|
20
|
+
} from "lucide-react";
|
|
21
|
+
import { useState } from "react";
|
|
22
|
+
import { expect, userEvent, within } from "storybook/test";
|
|
23
|
+
import { Avatar } from "@/components/avatar/avatar";
|
|
24
|
+
import { Button } from "@/components/button/button";
|
|
25
|
+
import {
|
|
26
|
+
DropdownMenu,
|
|
27
|
+
DropdownMenuCheckboxItem,
|
|
28
|
+
DropdownMenuContent,
|
|
29
|
+
DropdownMenuGroup,
|
|
30
|
+
DropdownMenuItem,
|
|
31
|
+
DropdownMenuLabel,
|
|
32
|
+
DropdownMenuPortal,
|
|
33
|
+
DropdownMenuRadioGroup,
|
|
34
|
+
DropdownMenuRadioItem,
|
|
35
|
+
DropdownMenuSeparator,
|
|
36
|
+
DropdownMenuShortcut,
|
|
37
|
+
DropdownMenuSub,
|
|
38
|
+
DropdownMenuSubContent,
|
|
39
|
+
DropdownMenuSubTrigger,
|
|
40
|
+
DropdownMenuTrigger,
|
|
41
|
+
} from "./dropdown-menu";
|
|
42
|
+
|
|
43
|
+
const meta: Meta<typeof DropdownMenu> = {
|
|
44
|
+
component: DropdownMenu,
|
|
45
|
+
parameters: {
|
|
46
|
+
// Radix hides the rest of the page (aria-hidden) while the popup is open; axe then flags
|
|
47
|
+
// the story root's focusable elements. Focus is trapped in the popup, so nothing is reachable.
|
|
48
|
+
a11y: { config: { rules: [{ enabled: false, id: "aria-hidden-focus" }] } },
|
|
49
|
+
layout: "centered",
|
|
50
|
+
},
|
|
51
|
+
tags: ["autodocs"],
|
|
52
|
+
} satisfies Meta<typeof DropdownMenu>;
|
|
53
|
+
|
|
54
|
+
export default meta;
|
|
55
|
+
type Story = StoryObj<typeof meta>;
|
|
56
|
+
|
|
57
|
+
export const Default: Story = {
|
|
58
|
+
play: async ({ canvasElement }) => {
|
|
59
|
+
await userEvent.click(within(canvasElement).getByRole("button", { name: "Open Menu" }));
|
|
60
|
+
|
|
61
|
+
const menu = await within(document.body).findByRole("menu");
|
|
62
|
+
await expect(within(menu).getByText("Profile")).toBeInTheDocument();
|
|
63
|
+
await expect(within(menu).getByText("Billing")).toBeInTheDocument();
|
|
64
|
+
},
|
|
65
|
+
render: () => (
|
|
66
|
+
<DropdownMenu>
|
|
67
|
+
<DropdownMenuTrigger asChild>
|
|
68
|
+
<Button variant="outline">Open Menu</Button>
|
|
69
|
+
</DropdownMenuTrigger>
|
|
70
|
+
<DropdownMenuContent className="w-56">
|
|
71
|
+
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
|
72
|
+
<DropdownMenuSeparator />
|
|
73
|
+
<DropdownMenuGroup>
|
|
74
|
+
<DropdownMenuItem>
|
|
75
|
+
<User />
|
|
76
|
+
Profile
|
|
77
|
+
<DropdownMenuShortcut>⇧⌘P</DropdownMenuShortcut>
|
|
78
|
+
</DropdownMenuItem>
|
|
79
|
+
<DropdownMenuItem>
|
|
80
|
+
<CreditCard />
|
|
81
|
+
Billing
|
|
82
|
+
<DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
|
|
83
|
+
</DropdownMenuItem>
|
|
84
|
+
<DropdownMenuItem>
|
|
85
|
+
<Settings />
|
|
86
|
+
Settings
|
|
87
|
+
<DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
|
|
88
|
+
</DropdownMenuItem>
|
|
89
|
+
<DropdownMenuItem>
|
|
90
|
+
<Keyboard />
|
|
91
|
+
Keyboard shortcuts
|
|
92
|
+
<DropdownMenuShortcut>⌘K</DropdownMenuShortcut>
|
|
93
|
+
</DropdownMenuItem>
|
|
94
|
+
</DropdownMenuGroup>
|
|
95
|
+
<DropdownMenuSeparator />
|
|
96
|
+
<DropdownMenuGroup>
|
|
97
|
+
<DropdownMenuItem>
|
|
98
|
+
<Users />
|
|
99
|
+
Team
|
|
100
|
+
</DropdownMenuItem>
|
|
101
|
+
<DropdownMenuSub>
|
|
102
|
+
<DropdownMenuSubTrigger>
|
|
103
|
+
<UserPlus />
|
|
104
|
+
Invite users
|
|
105
|
+
</DropdownMenuSubTrigger>
|
|
106
|
+
<DropdownMenuPortal>
|
|
107
|
+
<DropdownMenuSubContent>
|
|
108
|
+
<DropdownMenuItem>
|
|
109
|
+
<Mail />
|
|
110
|
+
Email
|
|
111
|
+
</DropdownMenuItem>
|
|
112
|
+
<DropdownMenuItem>
|
|
113
|
+
<MessageSquare />
|
|
114
|
+
Message
|
|
115
|
+
</DropdownMenuItem>
|
|
116
|
+
<DropdownMenuSeparator />
|
|
117
|
+
<DropdownMenuItem>
|
|
118
|
+
<PlusCircle />
|
|
119
|
+
More...
|
|
120
|
+
</DropdownMenuItem>
|
|
121
|
+
</DropdownMenuSubContent>
|
|
122
|
+
</DropdownMenuPortal>
|
|
123
|
+
</DropdownMenuSub>
|
|
124
|
+
<DropdownMenuItem>
|
|
125
|
+
<Plus />
|
|
126
|
+
New Team
|
|
127
|
+
<DropdownMenuShortcut>⌘+T</DropdownMenuShortcut>
|
|
128
|
+
</DropdownMenuItem>
|
|
129
|
+
</DropdownMenuGroup>
|
|
130
|
+
<DropdownMenuSeparator />
|
|
131
|
+
<DropdownMenuItem>
|
|
132
|
+
<Globe />
|
|
133
|
+
GitHub
|
|
134
|
+
</DropdownMenuItem>
|
|
135
|
+
<DropdownMenuItem>
|
|
136
|
+
<LifeBuoy />
|
|
137
|
+
Support
|
|
138
|
+
</DropdownMenuItem>
|
|
139
|
+
<DropdownMenuItem disabled>
|
|
140
|
+
<Cloud />
|
|
141
|
+
API
|
|
142
|
+
</DropdownMenuItem>
|
|
143
|
+
<DropdownMenuSeparator />
|
|
144
|
+
<DropdownMenuItem>
|
|
145
|
+
<LogOut />
|
|
146
|
+
Log out
|
|
147
|
+
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
|
148
|
+
</DropdownMenuItem>
|
|
149
|
+
</DropdownMenuContent>
|
|
150
|
+
</DropdownMenu>
|
|
151
|
+
),
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const UserProfile: Story = {
|
|
155
|
+
render: () => (
|
|
156
|
+
<DropdownMenu>
|
|
157
|
+
<DropdownMenuTrigger asChild>
|
|
158
|
+
<button
|
|
159
|
+
className="focus-visible:ring-brand-400 rounded-full outline-none focus-visible:ring-2"
|
|
160
|
+
type="button"
|
|
161
|
+
>
|
|
162
|
+
<Avatar color="blue" fallback="JD" size="md" />
|
|
163
|
+
</button>
|
|
164
|
+
</DropdownMenuTrigger>
|
|
165
|
+
<DropdownMenuContent align="end" className="w-56">
|
|
166
|
+
<DropdownMenuLabel>
|
|
167
|
+
<div className="flex flex-col space-y-1">
|
|
168
|
+
<p className="text-sm leading-none font-medium">John Doe</p>
|
|
169
|
+
<p className="text-muted-foreground text-xs leading-none">john.doe@example.com</p>
|
|
170
|
+
</div>
|
|
171
|
+
</DropdownMenuLabel>
|
|
172
|
+
<DropdownMenuSeparator />
|
|
173
|
+
<DropdownMenuGroup>
|
|
174
|
+
<DropdownMenuItem>
|
|
175
|
+
<User />
|
|
176
|
+
My Profile
|
|
177
|
+
</DropdownMenuItem>
|
|
178
|
+
<DropdownMenuItem>
|
|
179
|
+
<Settings />
|
|
180
|
+
Settings
|
|
181
|
+
</DropdownMenuItem>
|
|
182
|
+
</DropdownMenuGroup>
|
|
183
|
+
<DropdownMenuSeparator />
|
|
184
|
+
<DropdownMenuSub>
|
|
185
|
+
<DropdownMenuSubTrigger>
|
|
186
|
+
<Sun className="scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90" />
|
|
187
|
+
<Moon className="absolute scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0" />
|
|
188
|
+
Theme
|
|
189
|
+
</DropdownMenuSubTrigger>
|
|
190
|
+
<DropdownMenuPortal>
|
|
191
|
+
<DropdownMenuSubContent>
|
|
192
|
+
<DropdownMenuItem>
|
|
193
|
+
<Sun />
|
|
194
|
+
Light
|
|
195
|
+
</DropdownMenuItem>
|
|
196
|
+
<DropdownMenuItem>
|
|
197
|
+
<Moon />
|
|
198
|
+
Dark
|
|
199
|
+
</DropdownMenuItem>
|
|
200
|
+
<DropdownMenuItem>
|
|
201
|
+
<Settings />
|
|
202
|
+
System
|
|
203
|
+
</DropdownMenuItem>
|
|
204
|
+
</DropdownMenuSubContent>
|
|
205
|
+
</DropdownMenuPortal>
|
|
206
|
+
</DropdownMenuSub>
|
|
207
|
+
<DropdownMenuSeparator />
|
|
208
|
+
<DropdownMenuItem>
|
|
209
|
+
<LogOut />
|
|
210
|
+
Log out
|
|
211
|
+
</DropdownMenuItem>
|
|
212
|
+
</DropdownMenuContent>
|
|
213
|
+
</DropdownMenu>
|
|
214
|
+
),
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export const ContextMenu: Story = {
|
|
218
|
+
render: () => (
|
|
219
|
+
<div className="flex flex-col items-center gap-4">
|
|
220
|
+
<p className="text-muted-foreground mb-2 text-sm">Right-click on the card</p>
|
|
221
|
+
<DropdownMenu>
|
|
222
|
+
<DropdownMenuTrigger asChild>
|
|
223
|
+
<button
|
|
224
|
+
aria-label="Document.pdf actions"
|
|
225
|
+
className="focus-visible:ring-ring/40 w-64 cursor-context-menu rounded-[18px] border border-white/50 bg-white/30 p-6 text-left backdrop-blur-[26px] outline-none focus-visible:ring-2 dark:border-white/20 dark:bg-white/5"
|
|
226
|
+
type="button"
|
|
227
|
+
>
|
|
228
|
+
<span className="mb-2 block font-semibold">Document.pdf</span>
|
|
229
|
+
<span className="text-muted-foreground block text-sm">Click to open context menu</span>
|
|
230
|
+
</button>
|
|
231
|
+
</DropdownMenuTrigger>
|
|
232
|
+
<DropdownMenuContent className="w-48">
|
|
233
|
+
<DropdownMenuItem>
|
|
234
|
+
<User />
|
|
235
|
+
Open
|
|
236
|
+
</DropdownMenuItem>
|
|
237
|
+
<DropdownMenuItem>
|
|
238
|
+
<Settings />
|
|
239
|
+
Rename
|
|
240
|
+
</DropdownMenuItem>
|
|
241
|
+
<DropdownMenuSeparator />
|
|
242
|
+
<DropdownMenuItem>
|
|
243
|
+
<CreditCard />
|
|
244
|
+
Share
|
|
245
|
+
</DropdownMenuItem>
|
|
246
|
+
<DropdownMenuItem>
|
|
247
|
+
<Users />
|
|
248
|
+
Collaborate
|
|
249
|
+
</DropdownMenuItem>
|
|
250
|
+
<DropdownMenuSeparator />
|
|
251
|
+
<DropdownMenuItem destructive>
|
|
252
|
+
<Trash2 />
|
|
253
|
+
Delete
|
|
254
|
+
</DropdownMenuItem>
|
|
255
|
+
</DropdownMenuContent>
|
|
256
|
+
</DropdownMenu>
|
|
257
|
+
</div>
|
|
258
|
+
),
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export const WithCheckboxes: Story = {
|
|
262
|
+
render: () => {
|
|
263
|
+
const [showStatusBar, setShowStatusBar] = useState(true);
|
|
264
|
+
const [showActivityBar, setShowActivityBar] = useState(false);
|
|
265
|
+
const [showPanel, setShowPanel] = useState(false);
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<DropdownMenu>
|
|
269
|
+
<DropdownMenuTrigger asChild>
|
|
270
|
+
<Button variant="outline">View Options</Button>
|
|
271
|
+
</DropdownMenuTrigger>
|
|
272
|
+
<DropdownMenuContent className="w-56">
|
|
273
|
+
<DropdownMenuLabel>Appearance</DropdownMenuLabel>
|
|
274
|
+
<DropdownMenuSeparator />
|
|
275
|
+
<DropdownMenuCheckboxItem checked={showStatusBar} onCheckedChange={setShowStatusBar}>
|
|
276
|
+
Status Bar
|
|
277
|
+
</DropdownMenuCheckboxItem>
|
|
278
|
+
<DropdownMenuCheckboxItem checked={showActivityBar} onCheckedChange={setShowActivityBar}>
|
|
279
|
+
Activity Bar
|
|
280
|
+
</DropdownMenuCheckboxItem>
|
|
281
|
+
<DropdownMenuCheckboxItem checked={showPanel} onCheckedChange={setShowPanel}>
|
|
282
|
+
Panel
|
|
283
|
+
</DropdownMenuCheckboxItem>
|
|
284
|
+
</DropdownMenuContent>
|
|
285
|
+
</DropdownMenu>
|
|
286
|
+
);
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
export const WithRadioGroup: Story = {
|
|
291
|
+
render: () => {
|
|
292
|
+
const [position, setPosition] = useState("bottom");
|
|
293
|
+
|
|
294
|
+
return (
|
|
295
|
+
<DropdownMenu>
|
|
296
|
+
<DropdownMenuTrigger asChild>
|
|
297
|
+
<Button variant="outline">Position: {position}</Button>
|
|
298
|
+
</DropdownMenuTrigger>
|
|
299
|
+
<DropdownMenuContent className="w-56">
|
|
300
|
+
<DropdownMenuLabel>Panel Position</DropdownMenuLabel>
|
|
301
|
+
<DropdownMenuSeparator />
|
|
302
|
+
<DropdownMenuRadioGroup onValueChange={setPosition} value={position}>
|
|
303
|
+
<DropdownMenuRadioItem value="top">Top</DropdownMenuRadioItem>
|
|
304
|
+
<DropdownMenuRadioItem value="bottom">Bottom</DropdownMenuRadioItem>
|
|
305
|
+
<DropdownMenuRadioItem value="right">Right</DropdownMenuRadioItem>
|
|
306
|
+
</DropdownMenuRadioGroup>
|
|
307
|
+
</DropdownMenuContent>
|
|
308
|
+
</DropdownMenu>
|
|
309
|
+
);
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
export const Simple: Story = {
|
|
314
|
+
render: () => (
|
|
315
|
+
<DropdownMenu>
|
|
316
|
+
<DropdownMenuTrigger asChild>
|
|
317
|
+
<Button size="sm" variant="outline">
|
|
318
|
+
Actions
|
|
319
|
+
</Button>
|
|
320
|
+
</DropdownMenuTrigger>
|
|
321
|
+
<DropdownMenuContent>
|
|
322
|
+
<DropdownMenuItem>Edit</DropdownMenuItem>
|
|
323
|
+
<DropdownMenuItem>Duplicate</DropdownMenuItem>
|
|
324
|
+
<DropdownMenuSeparator />
|
|
325
|
+
<DropdownMenuItem destructive>Delete</DropdownMenuItem>
|
|
326
|
+
</DropdownMenuContent>
|
|
327
|
+
</DropdownMenu>
|
|
328
|
+
),
|
|
329
|
+
};
|