From 205e08706d44011ee222141aea827a992174f95b Mon Sep 17 00:00:00 2001 From: tylerapfledderer Date: Tue, 17 Dec 2024 12:13:17 -0500 Subject: [PATCH 1/4] fix(ButtonTwoLines): split renders of button and link --- .../ButtonLinkTwoLines.stories.tsx | 35 ++++ .../ui/__stories__/ButtonTwoLines.stories.tsx | 7 +- src/components/ui/buttons/Button.tsx | 3 +- src/components/ui/buttons/ButtonTwoLines.tsx | 170 ++++++++++++------ 4 files changed, 155 insertions(+), 60 deletions(-) create mode 100644 src/components/ui/__stories__/ButtonLinkTwoLines.stories.tsx diff --git a/src/components/ui/__stories__/ButtonLinkTwoLines.stories.tsx b/src/components/ui/__stories__/ButtonLinkTwoLines.stories.tsx new file mode 100644 index 00000000000..0c0ffaaf90e --- /dev/null +++ b/src/components/ui/__stories__/ButtonLinkTwoLines.stories.tsx @@ -0,0 +1,35 @@ +import { BiCircle } from "react-icons/bi" +import type { Meta, StoryObj } from "@storybook/react" + +import { ButtonLinkTwoLines as ButtonLinkTwoLinesComponent } from "../buttons/ButtonTwoLines" +import { Stack } from "../flex" + +const meta = { + title: "Atoms / Form / Buttons / ButtonTwoLines", + component: ButtonLinkTwoLinesComponent, +} satisfies Meta + +export default meta + +type Story = StoryObj + +export const ButtonLinkTwoLines: Story = { + args: { + icon: BiCircle, + mainText: "Main Text", + helperText: "Helper Text", + className: "w-[300px]", + href: "#", + }, + render: (args) => ( + + + + + ), +} diff --git a/src/components/ui/__stories__/ButtonTwoLines.stories.tsx b/src/components/ui/__stories__/ButtonTwoLines.stories.tsx index 07606c0f1bd..dc3850cc3c9 100644 --- a/src/components/ui/__stories__/ButtonTwoLines.stories.tsx +++ b/src/components/ui/__stories__/ButtonTwoLines.stories.tsx @@ -1,8 +1,8 @@ import { BiCircle } from "react-icons/bi" -import { Stack } from "@chakra-ui/react" import { Meta, StoryObj } from "@storybook/react" -import ButtonTwoLinesComponent from "../buttons/ButtonTwoLines" +import { ButtonTwoLines as ButtonTwoLinesComponent } from "../buttons/ButtonTwoLines" +import { Stack } from "../flex" const meta = { title: "Atoms / Form / Buttons / ButtonTwoLines", @@ -15,14 +15,13 @@ type Story = StoryObj export const ButtonTwoLines: Story = { args: { - componentType: "button", icon: BiCircle, mainText: "Main Text", helperText: "Helper Text", className: "w-[300px]", }, render: (args) => ( - + ( ) Button.displayName = "Button" -type ButtonLinkProps = LinkProps & +type ButtonLinkProps = Omit & Pick & { + href: string buttonProps?: Omit customEventOptions?: MatomoEventOptions } diff --git a/src/components/ui/buttons/ButtonTwoLines.tsx b/src/components/ui/buttons/ButtonTwoLines.tsx index 332290ba5bc..c5dbc95b056 100644 --- a/src/components/ui/buttons/ButtonTwoLines.tsx +++ b/src/components/ui/buttons/ButtonTwoLines.tsx @@ -36,76 +36,103 @@ type CommonProps = { type OmittedTypes = "variant" | "size" | "children" -type ButtonTypeProps = CommonProps & - Omit & { - componentType: "button" - } +type ButtonTwoLinesProps = Omit & CommonProps -type ButtonLinkTypeProps = CommonProps & - Omit & { - componentType: "link" - } - -type ButtonTwoLinesProps = ButtonTypeProps | ButtonLinkTypeProps - -const ButtonTwoLines = ({ - iconAlignment = "start", +/** + * Button that renders two styled lines of text + */ +export const ButtonTwoLines = ({ className, + iconAlignment = "start", size = "md", ...props }: ButtonTwoLinesProps) => { const isIconLeft = ["left", "start"].includes(iconAlignment) - const commonClassStyles = cn( - isIconLeft ? "text-start justify-start" : "text-end justify-end", - size === "md" ? "py-4" : "py-2", - className + const [childProps, ownProps] = createSplitProps()( + { ...props, isIconLeft }, + [ + "reverseTextOrder", + "mainText", + "helperText", + "variant", + "icon", + "isIconLeft", + "isSecondary", + "size", + ] ) - if (props.componentType === "link") { - const { buttonProps, ...rest } = props - return ( - - - - ) - } return ( - ) } -export default ButtonTwoLines - -const ChildContent = ( - props: Omit & { - isIconLeft: boolean - isSecondary?: boolean - } -) => { - const { - reverseTextOrder = false, - size, - mainText, - helperText, - icon: Icon, - isIconLeft, - isSecondary, - variant, - } = props +type ButtonLinkTwoLinesProps = Omit & CommonProps + +/** + * ButtonLink that renders two styled lines of text + */ +export const ButtonLinkTwoLines = ({ + className, + iconAlignment = "start", + size = "md", + ...props +}: ButtonLinkTwoLinesProps) => { + const isIconLeft = ["left", "start"].includes(iconAlignment) + + const [childProps, ownProps] = createSplitProps()( + { ...props, isIconLeft }, + [ + "reverseTextOrder", + "mainText", + "helperText", + "variant", + "icon", + "isIconLeft", + "isSecondary", + "size", + ] + ) + + return ( + + + + ) +} + +type ChildContentProps = Omit & { + isIconLeft: boolean + isSecondary?: boolean +} +const ChildContent = ({ + helperText, + icon: Icon, + mainText, + reverseTextOrder = false, + size, + variant, + isIconLeft, + isSecondary, +}: ChildContentProps) => { const ButtonIcon = () => ( ) } + +/** + * Split props ripped from Ark UI and simplified: + * https://github.com/chakra-ui/ark/blob/main/packages/react/src/utils/create-split-props.ts + */ +type EnsureKeys = + keyof ChildContentProps extends ExpectedKeys[number] + ? unknown + : `Missing required keys: ${Exclude & string}` + +function createSplitProps() { + return < + Keys extends (keyof ChildContentProps)[], + Props = Required, + >( + props: Props, + keys: Keys & EnsureKeys + ) => + (keys as string[]).reduce< + [ChildContentProps, Omit>] + >( + (previousValue, currentValue) => { + const [target, source] = previousValue + const key = currentValue + if (source[key] !== undefined) { + target[key] = source[key] + } + delete source[key] + return [target, source] + }, + [{} as ChildContentProps, { ...props }] + ) +} From 6c0907445223b0f57c0f3d2df4576ce6f5bc103e Mon Sep 17 00:00:00 2001 From: tylerapfledderer Date: Tue, 17 Dec 2024 12:13:37 -0500 Subject: [PATCH 2/4] chore: remove old version of ButtonTwoLines --- .../Buttons/ButtonTwoLines/index.tsx | 113 ------------------ src/components/Buttons/index.ts | 1 - 2 files changed, 114 deletions(-) delete mode 100644 src/components/Buttons/ButtonTwoLines/index.tsx diff --git a/src/components/Buttons/ButtonTwoLines/index.tsx b/src/components/Buttons/ButtonTwoLines/index.tsx deleted file mode 100644 index 03210dbe166..00000000000 --- a/src/components/Buttons/ButtonTwoLines/index.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import type { IconType } from "react-icons/lib" -import { Icon, Stack, Text } from "@chakra-ui/react" - -import Button, { type ButtonProps } from "../Button" -import ButtonLink, { type ButtonLinkProps } from "../ButtonLink" - -type CommonProps = { - icon: IconType | typeof Icon - iconAlignment?: "left" | "right" | "start" | "end" - /** - * Reduced choices of the button variant. - * - * This component only accepts the `solid` or `outline` variant - */ - variant?: "solid" | "outline" - /** - * Reduced choices of the button size - * - * This component only accepts the `md` or `sm` sizes - */ - size?: "md" | "sm" - mainText: string - helperText: string - /** - * Should the main text be below the helper text instead of ab? - */ - reverseTextOrder?: boolean -} - -type OmittedTypes = "variant" | "size" - -type ButtonTypeProps = CommonProps & - Omit & { - href?: never - } - -type ButtonLinkTypeProps = CommonProps & - Omit & { - toId?: never - } - -type ButtonTwoLinesProps = ButtonTypeProps | ButtonLinkTypeProps - -const hasHref = (props: ButtonTwoLinesProps): props is ButtonLinkTypeProps => { - return "href" in props -} - -const ButtonTwoLines = (props: ButtonTwoLinesProps) => { - const { - icon: Icon, - iconAlignment = "start", - mainText, - helperText, - reverseTextOrder = false, - size = "md", - ...rest - } = props - - const isIconLeft = ["left", "start"].includes(iconAlignment) - - const vertPadding: ButtonTwoLinesProps["py"] = size === "md" ? "4" : "2" - - const buttonStyles: ButtonProps = { - [isIconLeft ? "leftIcon" : "rightIcon"]: , - textAlign: isIconLeft ? "start" : "end", - justifyContent: isIconLeft ? "flex-start" : "flex-end", - } - - const Component = hasHref(props) ? ButtonLink : Button - - return ( - // TODO: fix type error - // @ts-expect-error incompatible prop type shapes - - - - {mainText} - - - {helperText} - - - - ) -} - -export default ButtonTwoLines diff --git a/src/components/Buttons/index.ts b/src/components/Buttons/index.ts index 73e0e2bcbb1..6f05838f36d 100644 --- a/src/components/Buttons/index.ts +++ b/src/components/Buttons/index.ts @@ -1,4 +1,3 @@ export { default as Button, type ButtonProps, checkIsSecondary } from "./Button" export { default as ButtonLink, type ButtonLinkProps } from "./ButtonLink" -export { default as ButtonTwoLines } from "./ButtonTwoLines" export { default as IconButton } from "./IconButton" From 70ea4cb7ffc6badc8edb6c2820442d84a7f67c37 Mon Sep 17 00:00:00 2001 From: tylerapfledderer Date: Tue, 17 Dec 2024 12:14:20 -0500 Subject: [PATCH 3/4] fix(BugBountyCards): update typing of `href` prop for submit button --- src/components/BugBountyCards.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/BugBountyCards.tsx b/src/components/BugBountyCards.tsx index fb017ea3611..56786711e3e 100644 --- a/src/components/BugBountyCards.tsx +++ b/src/components/BugBountyCards.tsx @@ -14,7 +14,10 @@ const CardRow = ({ children }: ChildOnlyProp) => ( {children} ) -const SubmitBugBountyButton = ({ children, ...props }: ButtonLinkProps) => ( +const SubmitBugBountyButton = ({ + children, + ...props +}: Omit) => ( Date: Sat, 21 Dec 2024 23:40:08 -0500 Subject: [PATCH 4/4] fix(ButtonTwoLines): send `size` to `createSplitProps` --- src/components/ui/buttons/ButtonTwoLines.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ui/buttons/ButtonTwoLines.tsx b/src/components/ui/buttons/ButtonTwoLines.tsx index c5dbc95b056..b2b5dcd84bf 100644 --- a/src/components/ui/buttons/ButtonTwoLines.tsx +++ b/src/components/ui/buttons/ButtonTwoLines.tsx @@ -50,7 +50,7 @@ export const ButtonTwoLines = ({ const isIconLeft = ["left", "start"].includes(iconAlignment) const [childProps, ownProps] = createSplitProps()( - { ...props, isIconLeft }, + { ...props, isIconLeft, size }, [ "reverseTextOrder", "mainText", @@ -91,7 +91,7 @@ export const ButtonLinkTwoLines = ({ const isIconLeft = ["left", "start"].includes(iconAlignment) const [childProps, ownProps] = createSplitProps()( - { ...props, isIconLeft }, + { ...props, isIconLeft, size }, [ "reverseTextOrder", "mainText",