mirror of
https://github.com/formbricks/formbricks.git
synced 2026-03-07 01:02:21 -06:00
Compare commits
1 Commits
4.7.5
...
feat/butto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2fdaf88572 |
@@ -1,6 +1,7 @@
|
|||||||
import type { StorybookConfig } from "@storybook/react-vite";
|
import type { StorybookConfig } from "@storybook/react-vite";
|
||||||
import { createRequire } from "module";
|
import { createRequire } from "module";
|
||||||
import { dirname, join } from "path";
|
import { dirname, join } from "path";
|
||||||
|
import { mergeConfig } from "vite";
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
@@ -13,7 +14,11 @@ function getAbsolutePath(value: string): any {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const config: StorybookConfig = {
|
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: [
|
addons: [
|
||||||
getAbsolutePath("@storybook/addon-onboarding"),
|
getAbsolutePath("@storybook/addon-onboarding"),
|
||||||
getAbsolutePath("@storybook/addon-links"),
|
getAbsolutePath("@storybook/addon-links"),
|
||||||
@@ -25,5 +30,16 @@ const config: StorybookConfig = {
|
|||||||
name: getAbsolutePath("@storybook/react-vite"),
|
name: getAbsolutePath("@storybook/react-vite"),
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
|
async viteFinal(config) {
|
||||||
|
return mergeConfig(config, {
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
preact: "react",
|
||||||
|
"preact/hooks": "react",
|
||||||
|
"preact/jsx-runtime": "react/jsx-runtime",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
export default config;
|
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