mirror of
https://github.com/formbricks/formbricks.git
synced 2025-12-22 14:10:45 -06:00
Compare commits
1 Commits
fix/sdk-ra
...
feat/butto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fdaf88572 |
@@ -1,6 +1,7 @@
|
||||
import type { StorybookConfig } from "@storybook/react-vite";
|
||||
import { createRequire } from "module";
|
||||
import { dirname, join } from "path";
|
||||
import { mergeConfig } from "vite";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
@@ -13,7 +14,11 @@ function getAbsolutePath(value: string): any {
|
||||
}
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ["../src/**/*.mdx", "../../web/modules/ui/**/stories.@(js|jsx|mjs|ts|tsx)"],
|
||||
stories: [
|
||||
"../src/**/*.mdx",
|
||||
"../../web/modules/ui/**/stories.@(js|jsx|mjs|ts|tsx)",
|
||||
"../../../packages/surveys/src/components/**/stories.@(js|jsx|mjs|ts|tsx)",
|
||||
],
|
||||
addons: [
|
||||
getAbsolutePath("@storybook/addon-onboarding"),
|
||||
getAbsolutePath("@storybook/addon-links"),
|
||||
@@ -25,5 +30,16 @@ const config: StorybookConfig = {
|
||||
name: getAbsolutePath("@storybook/react-vite"),
|
||||
options: {},
|
||||
},
|
||||
async viteFinal(config) {
|
||||
return mergeConfig(config, {
|
||||
resolve: {
|
||||
alias: {
|
||||
preact: "react",
|
||||
"preact/hooks": "react",
|
||||
"preact/jsx-runtime": "react/jsx-runtime",
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
export default config;
|
||||
|
||||
60
packages/surveys/src/components/v5/button.tsx
Normal file
60
packages/surveys/src/components/v5/button.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
import { type JSX } from "preact";
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||
destructive:
|
||||
"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||
outline:
|
||||
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||
link: "text-primary underline-offset-4 hover:underline",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
interface ButtonProps extends JSX.HTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
||||
isLoading?: boolean;
|
||||
disabled?: boolean;
|
||||
style?: JSX.CSSProperties;
|
||||
}
|
||||
|
||||
function Button({
|
||||
className,
|
||||
variant,
|
||||
size,
|
||||
onClick,
|
||||
children,
|
||||
isLoading,
|
||||
disabled,
|
||||
style,
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
return (
|
||||
<button
|
||||
data-slot="button"
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
onClick={onClick}
|
||||
disabled={isLoading || disabled}
|
||||
style={style}
|
||||
{...props}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export { Button, buttonVariants, type ButtonProps };
|
||||
210
packages/surveys/src/components/v5/stories.tsx
Normal file
210
packages/surveys/src/components/v5/stories.tsx
Normal file
@@ -0,0 +1,210 @@
|
||||
import { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react";
|
||||
import { type JSX } from "preact";
|
||||
import { fn } from "storybook/test";
|
||||
import { Button, type ButtonProps } from "./button";
|
||||
|
||||
const meta: Meta<ButtonProps> = {
|
||||
title: "Surveys/V5/Button",
|
||||
component: Button as any,
|
||||
tags: ["autodocs"],
|
||||
parameters: {
|
||||
layout: "centered",
|
||||
controls: { sort: "alpha", exclude: [] },
|
||||
docs: {
|
||||
description: {
|
||||
component:
|
||||
"The **Button** component for survey interfaces provides clickable actions with multiple variants and sizes. Built with Preact for optimal performance in embedded survey widgets.",
|
||||
},
|
||||
},
|
||||
},
|
||||
argTypes: {
|
||||
onClick: {
|
||||
action: "clicked",
|
||||
description: "Click handler function",
|
||||
table: {
|
||||
category: "Behavior",
|
||||
type: { summary: "function" },
|
||||
},
|
||||
order: 1,
|
||||
},
|
||||
variant: {
|
||||
control: "select",
|
||||
options: ["default", "destructive", "outline", "secondary", "ghost", "link"],
|
||||
description: "Visual style variant of the button",
|
||||
table: {
|
||||
category: "Appearance",
|
||||
type: { summary: "string" },
|
||||
defaultValue: { summary: "default" },
|
||||
},
|
||||
order: 1,
|
||||
},
|
||||
style: {
|
||||
control: "object",
|
||||
description: "Inline style object for custom CSS styling",
|
||||
table: {
|
||||
category: "Appearance",
|
||||
type: { summary: "object" },
|
||||
},
|
||||
order: 4,
|
||||
},
|
||||
},
|
||||
args: { onClick: fn() },
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<ButtonProps>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
children: "Submit Response",
|
||||
variant: "default",
|
||||
},
|
||||
};
|
||||
|
||||
export const Destructive: Story = {
|
||||
args: {
|
||||
children: "Skip Survey",
|
||||
variant: "destructive",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use for actions that are destructive or exit flows, like skipping a survey or canceling progress.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Outline: Story = {
|
||||
args: {
|
||||
children: "Back",
|
||||
variant: "outline",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use for secondary actions like navigation or when you need a button with less visual weight than the primary action.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Secondary: Story = {
|
||||
args: {
|
||||
children: "Save Draft",
|
||||
variant: "secondary",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use for secondary actions that are less important than the primary submit action.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Ghost: Story = {
|
||||
args: {
|
||||
children: "Skip Question",
|
||||
variant: "ghost",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use for subtle actions or when you need minimal visual impact in the survey flow.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Link: Story = {
|
||||
args: {
|
||||
children: "Learn more",
|
||||
variant: "link",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Use when you want button functionality but link appearance, like for help text or additional information.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const Icon: Story = {
|
||||
args: {
|
||||
children: "→",
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Square button for icon-only actions. Default size for icon buttons.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const textWithIconOnRight: Story = {
|
||||
args: {
|
||||
children: (
|
||||
<div className="flex items-center gap-2">
|
||||
<span>Next</span>
|
||||
<ArrowRightIcon className="size-4" />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
export const textWithIconOnLeft: Story = {
|
||||
args: {
|
||||
children: (
|
||||
<div className="flex items-center gap-2">
|
||||
<ArrowLeftIcon className="size-4" />
|
||||
<span>Previous</span>
|
||||
</div>
|
||||
),
|
||||
variant: "secondary",
|
||||
},
|
||||
};
|
||||
|
||||
export const Disabled: Story = {
|
||||
args: {
|
||||
children: "Submit",
|
||||
disabled: true,
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story: "Use when the button action is temporarily unavailable, such as when survey validation fails.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const InlineStyleWithGradient: Story = {
|
||||
args: {
|
||||
children: "Gradient Button",
|
||||
style: {
|
||||
background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
|
||||
color: "white",
|
||||
padding: "12px 32px",
|
||||
fontSize: "16px",
|
||||
fontWeight: "600",
|
||||
border: "none",
|
||||
cursor: "pointer",
|
||||
boxShadow: "0 8px 15px rgba(102, 126, 234, 0.4)",
|
||||
} as JSX.CSSProperties,
|
||||
},
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
story:
|
||||
"Inline styles can include complex CSS like gradients, perfect for creating visually striking buttons with custom theming.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user