From abf6adb7b4071a828c8e668d22d3235f4bb7ac0f Mon Sep 17 00:00:00 2001 From: Makenna Smutz <32865577+KenzoBenzo@users.noreply.github.com> Date: Thu, 9 May 2024 22:08:29 -0400 Subject: [PATCH] fix & document: Dropdown component (#839) --- .../types/type-shorthand/TypeShorthand.tsx | 2 +- .../src/components/FernDropdown.stories.tsx | 154 ++++++++++++++++++ .../ui/app/src/components/FernDropdown.tsx | 50 +++--- 3 files changed, 182 insertions(+), 24 deletions(-) create mode 100644 packages/ui/app/src/components/FernDropdown.stories.tsx diff --git a/packages/ui/app/src/api-page/types/type-shorthand/TypeShorthand.tsx b/packages/ui/app/src/api-page/types/type-shorthand/TypeShorthand.tsx index cadcd8bca8..1a0cd9975a 100644 --- a/packages/ui/app/src/api-page/types/type-shorthand/TypeShorthand.tsx +++ b/packages/ui/app/src/api-page/types/type-shorthand/TypeShorthand.tsx @@ -23,7 +23,7 @@ export function renderTypeShorthandRoot( const typeShorthand = renderTypeShorthand(unwrapOptional(shape, types), { nullable: isResponse }, types); const unaliasedShape = unwrapAlias(shape, types); return ( - + {typeShorthand} {unaliasedShape.type === "optional" ? ( {isResponse ? "Optional" : "Optional"} diff --git a/packages/ui/app/src/components/FernDropdown.stories.tsx b/packages/ui/app/src/components/FernDropdown.stories.tsx new file mode 100644 index 0000000000..33bb7eaf61 --- /dev/null +++ b/packages/ui/app/src/components/FernDropdown.stories.tsx @@ -0,0 +1,154 @@ +import { CaretDownIcon } from "@radix-ui/react-icons"; +import type { Meta, StoryObj } from "@storybook/react"; +import { useState } from "react"; +import { FernButton } from "./FernButton"; +import { FernDropdown } from "./FernDropdown"; + +const meta: Meta = { + title: "General/FernDropdown", + component: FernDropdown, + parameters: { + layout: "centered", + }, + tags: ["autodocs"], + args: { + defaultOpen: true, + usePortal: true, + children: ( + Select an enum...} + variant="outlined" + rightIcon={} + className="w-full text-left" + /> + ), + options: [ + { + type: "value", + label: "Option 1", + value: "option1", + helperText: "This is a helper text", + }, + { + type: "value", + label: "Option 2", + value: "option2", + helperText: "This is a helper text", + }, + { + type: "separator", + }, + { + type: "value", + label: "Option 3", + value: "option3", + tooltip: "This is a tooltip", + helperText: "This is a helper text", + }, + { + type: "value", + label: "Option 4", + value: "option4", + helperText: ( + + helloWorld + Optional + Object + + ), + }, + ], + }, +}; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + defaultOpen: true, + }, + render: (args) => { + const [value, setValue] = useState(); + return ; + }, +}; + +export const RealWorldExample: Story = { + args: { + options: [ + { + type: "value", + value: "mp3_22050_32", + label: "mp3_22050_32", + helperText: "Output format, mp3 with 22.05kHz sample rate at 32kbps", + }, + { + type: "value", + value: "mp3_44100_128", + label: "mp3_44100_128", + helperText: "Default output format, mp3 with 44.1kHz sample rate at 128kbps", + }, + { + type: "value", + value: "mp3_44100_192", + label: "mp3_44100_192", + helperText: "Output format, mp3 with 44.1kHz sample rate at 192kbps.", + }, + { + type: "value", + value: "mp3_44100_32", + label: "mp3_44100_32", + helperText: "Output format, mp3 with 44.1kHz sample rate at 32kbps", + }, + { + type: "value", + value: "mp3_44100_64", + label: "mp3_44100_64", + helperText: "Output format, mp3 with 44.1kHz sample rate at 64kbps", + }, + { + type: "value", + value: "mp3_44100_96", + label: "mp3_44100_96", + helperText: "Output format, mp3 with 44.1kHz sample rate at 96kbps", + }, + { + type: "value", + value: "pcm_16000", + label: "pcm_16000", + helperText: "PCM format (S16LE) with 16kHz sample rate.", + }, + { + type: "value", + value: "pcm_22050", + label: "pcm_22050", + helperText: "PCM format (S16LE) with 22.05kHz sample rate.", + }, + { + type: "value", + value: "pcm_24000", + label: "pcm_24000", + helperText: "PCM format (S16LE) with 24kHz sample rate.", + }, + { + type: "value", + value: "pcm_44100", + label: "pcm_44100", + helperText: + "PCM format (S16LE) with 44.1kHz sample rate. Requires you to be subscribed to Independent Publisher tier or above.", + }, + { + type: "value", + value: "ulaw_8000", + label: "ulaw_8000", + helperText: + "μ-law format (sometimes written mu-law, often approximated as u-law) with 8kHz sample rate. Note that this format is commonly used for Twilio audio inputs.", + }, + ], + }, + render: (args) => { + const [value, setValue] = useState(); + return ; + }, +}; diff --git a/packages/ui/app/src/components/FernDropdown.tsx b/packages/ui/app/src/components/FernDropdown.tsx index 3f1fea7585..219392006d 100644 --- a/packages/ui/app/src/components/FernDropdown.tsx +++ b/packages/ui/app/src/components/FernDropdown.tsx @@ -34,6 +34,7 @@ export declare namespace FernDropdown { usePortal?: boolean; side?: "top" | "right" | "bottom" | "left"; align?: "start" | "center" | "end"; + defaultOpen?: boolean; } } @@ -46,8 +47,9 @@ export function FernDropdown({ usePortal = true, side, align, + defaultOpen = false, }: PropsWithChildren): ReactElement { - const [isOpen, setOpen] = useState(false); + const [isOpen, setOpen] = useState(defaultOpen); const handleOpenChange = useCallback( (toOpen: boolean) => { setOpen(toOpen); @@ -76,7 +78,7 @@ export function FernDropdown({ ); return ( - + {children} {usePortal ? {renderDropdownContent()} : renderDropdownContent()} @@ -84,7 +86,7 @@ export function FernDropdown({ } function FernDropdownItemValue({ option, value }: { option: FernDropdown.ValueOption; value: string | undefined }) { - const helperTextRef = useRef(null); + const helperTextRef = useRef(null); const activeRef = useRef(null); useEffect(() => { if (option.value === value) { @@ -112,29 +114,31 @@ function FernDropdownItemValue({ option, value }: { option: FernDropdown.ValueOp function renderButtonContent() { return ( - <> - {value != null && ( +
+
- - - + {value != null && ( + + + + )} - )} - {option.icon && {option.icon}} - - {option.label ?? option.value} - {option.helperText != null && ( - - {option.helperText} - - )} - - - {option.rightElement && {option.rightElement}} - {(isEllipsisActive || (option.tooltip != null && option.tooltip !== "")) && } - - + {option.icon && {option.icon}} + +
{option.label ?? option.value}
+ + {option.rightElement && {option.rightElement}} + {(isEllipsisActive || (option.tooltip != null && option.tooltip !== "")) && } + +
+ + {option.helperText != null && ( +
+ {option.helperText} +
+ )} +
); }