From f080e39e7344ea1fd6784bf1659923bb55f85f4f Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 4 Aug 2021 09:56:15 -0500 Subject: [PATCH 1/6] feat: Add Button.disabledReason. --- src/components/Button.stories.tsx | 6 +++++- src/components/Button.tsx | 29 +++++++++++++++++++++-------- src/interfaces.ts | 2 ++ 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/components/Button.stories.tsx b/src/components/Button.stories.tsx index 9659b2e3b..c808e9ebc 100644 --- a/src/components/Button.stories.tsx +++ b/src/components/Button.stories.tsx @@ -20,7 +20,7 @@ export default { }, } as Meta; -export function Buttons(args: ButtonProps) { +export function ButtonVariations(args: ButtonProps) { const buttonRowStyles = Css.df.childGap1.my1.$; return (
@@ -130,3 +130,7 @@ export function Buttons(args: ButtonProps) {
); } + +export function ButtonWithTooltip() { + return ); + + if (disabledReason) { + return ( + + {button} + + ); + } + + return button; } function getButtonStyles(variant: ButtonVariant, size: ButtonSize) { diff --git a/src/interfaces.ts b/src/interfaces.ts index f90a90866..4799c81e6 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -10,6 +10,8 @@ export interface BeamFocusableProps { export interface BeamButtonProps { /** Whether the interactive element is disabled. */ disabled?: boolean; + /** If set, will show a tooltip about why the button is disabled. */ + disabledReason?: string; /** Handler that is called when the press is released over the target. */ onClick?: (e: PressEvent) => void; } From 7fef0bbfa3c6dcfa84ed46fa77de0e69a13d8950 Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 4 Aug 2021 13:40:40 -0500 Subject: [PATCH 2/6] Combine disabledReason into reason. --- src/components/Button.stories.tsx | 2 +- src/components/Button.tsx | 15 +++++---------- src/components/Tooltip.stories.tsx | 18 +++++++++++++----- src/components/Tooltip.tsx | 13 ++++++++----- src/interfaces.ts | 10 ++++++---- 5 files changed, 33 insertions(+), 25 deletions(-) diff --git a/src/components/Button.stories.tsx b/src/components/Button.stories.tsx index c808e9ebc..cb75b5b6b 100644 --- a/src/components/Button.stories.tsx +++ b/src/components/Button.stories.tsx @@ -132,5 +132,5 @@ export function ButtonVariations(args: ButtonProps) { } export function ButtonWithTooltip() { - return ); - if (disabledReason) { + // If we're disabled b/c of a non-boolean ReactNode, show it in a tooltip + if (isDisabled && typeof disabled !== "boolean") { return ( - + {button} ); diff --git a/src/components/Tooltip.stories.tsx b/src/components/Tooltip.stories.tsx index 8896953fb..3b2bc989a 100644 --- a/src/components/Tooltip.stories.tsx +++ b/src/components/Tooltip.stories.tsx @@ -1,23 +1,31 @@ import { Meta } from "@storybook/react"; import { Css } from "src/Css"; -import { Placement, Tooltip as TooltipComponent } from "./Tooltip"; +import { Placement, Tooltip } from "./Tooltip"; export default { - component: TooltipComponent, + component: Tooltip, title: "Components/Tooltip", } as Meta; -export function Tooltip() { +export function TooltipPlacements() { const placements: Placement[] = ["auto", "bottom", "left", "right", "top"]; return (
{placements.map((placement, i) => ( - + This tooltip is positioned at: {placement} - + ))}
); } + +export function TooltipDisabled() { + return ( + + Content + + ); +} diff --git a/src/components/Tooltip.tsx b/src/components/Tooltip.tsx index ccc70cb16..3fed11f0a 100644 --- a/src/components/Tooltip.tsx +++ b/src/components/Tooltip.tsx @@ -10,7 +10,7 @@ import { Css } from "src/Css"; interface TooltipProps { /** The content that shows up when hovered */ - title: string; + title: ReactNode; children: ReactElement; placement?: Placement; delay?: number; @@ -18,11 +18,12 @@ interface TooltipProps { } export function Tooltip(props: TooltipProps) { - const state = useTooltipTriggerState({ delay: 500, ...props }); + const { placement, children, title, disabled, delay } = props; + + const state = useTooltipTriggerState({ delay, isDisabled: disabled }); const triggerRef = React.useRef(null); - const { placement, children, title, disabled } = props; const { triggerProps, tooltipProps: _tooltipProps } = useTooltipTrigger( - { ...props, isDisabled: disabled }, + { delay, isDisabled: disabled }, state, triggerRef, ); @@ -30,7 +31,9 @@ export function Tooltip(props: TooltipProps) { return ( <> - {React.cloneElement(children, { ref: triggerRef, ...triggerProps })} + + {children} + {state.isOpen && ( void; } From c3de1e293cc632042c2108def9b1448ed5aae400 Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 4 Aug 2021 14:16:12 -0500 Subject: [PATCH 3/6] Show the disabled can be a ReactNode. --- src/components/Button.stories.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/Button.stories.tsx b/src/components/Button.stories.tsx index cb75b5b6b..3a5412115 100644 --- a/src/components/Button.stories.tsx +++ b/src/components/Button.stories.tsx @@ -132,5 +132,18 @@ export function ButtonVariations(args: ButtonProps) { } export function ButtonWithTooltip() { - return ); + + // If we're disabled b/c of a non-boolean ReactNode, show it in a tooltip + if (isDisabled && typeof disabled !== "boolean") { + return ( + + {button} + + ); + } + + return button; } const iconButtonStylesReset = Css.hPx(28).wPx(28).br8.bTransparent.bsSolid.bw2.bgTransparent.cursorPointer.outline0.p0 - .df.itemsCenter.justifyCenter.transition.$; + .dif.itemsCenter.justifyCenter.transition.$; export const iconButtonStylesHover = Css.bgGray100.$; const iconButtonStylesFocus = Css.bLightBlue700.$; const iconButtonStylesDisabled = Css.cursorNotAllowed.$; From 40f42159d9d02a41b7817c378e92db0679407de5 Mon Sep 17 00:00:00 2001 From: Stephen Haberman Date: Wed, 4 Aug 2021 15:20:29 -0500 Subject: [PATCH 5/6] Fix ReactNode error in ButtonGroup. --- src/components/ButtonGroup.tsx | 39 +++++++++++++++------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/components/ButtonGroup.tsx b/src/components/ButtonGroup.tsx index c179cbccb..e2ee7d92f 100644 --- a/src/components/ButtonGroup.tsx +++ b/src/components/ButtonGroup.tsx @@ -2,33 +2,31 @@ import React, { useRef } from "react"; import { useButton, useFocusRing, useHover } from "react-aria"; import { Icon, IconProps } from "src/components/Icon"; import { Css } from "src/Css"; -import { BeamButtonProps, BeamFocusableProps } from "src/interfaces"; +import { Callback } from "src/types"; export interface ButtonGroupProps { + buttons: ButtonGroupButton[]; /** Disables all buttons in ButtonGroup */ disabled?: boolean; - /** - * ButtonGroupButtonProps in an internal API. - * This is only exposing props that will be publicly accessible. - */ - buttons: Pick[]; size?: ButtonGroupSize; } -interface ButtonGroupButtonProps extends BeamButtonProps, BeamFocusableProps { - text?: string; +export type ButtonGroupButton = { icon?: IconProps["icon"]; - // Active is used to indicate the active/selected button, as in a tab or toggle. + text?: string; + onClick?: Callback; + /** Disables the button. Note we don't support the `disabled: ReactNode`/tooltip for now. */ + disabled?: boolean; + /** Indicates the active/selected button, as in a tab or toggle. */ active?: boolean; - size: ButtonGroupSize; -} +}; export function ButtonGroup(props: ButtonGroupProps) { const { buttons, disabled = false, size = "sm" } = props; return (
{buttons.map(({ disabled: buttonDisabled, ...buttonProps }, i) => ( - Date: Wed, 4 Aug 2021 16:01:38 -0500 Subject: [PATCH 6/6] Go back to cloneElement. --- src/components/Tooltip.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/Tooltip.tsx b/src/components/Tooltip.tsx index 3fed11f0a..919419121 100644 --- a/src/components/Tooltip.tsx +++ b/src/components/Tooltip.tsx @@ -1,4 +1,4 @@ -import React, { ReactElement, ReactNode, useRef, useState } from "react"; +import React, { cloneElement, ReactElement, ReactNode, useRef, useState } from "react"; import { mergeProps, useTooltip, useTooltipTrigger } from "react-aria"; import { usePopper } from "react-popper"; import { useTooltipTriggerState } from "react-stately"; @@ -31,9 +31,7 @@ export function Tooltip(props: TooltipProps) { return ( <> - - {children} - + {cloneElement(children, { ref: triggerRef, ...triggerProps })} {state.isOpen && (