-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Add Button.disabled can be a tooltip #202
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🍣
src/interfaces.ts
Outdated
disabled?: boolean; | ||
/** If set, will show a tooltip about why the button is disabled. */ | ||
disabledReason?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW: I had been musing with @KoltonG about whether it would be possible/good to have semi-direct integration with potential operations. Like:
disabled?: boolean; | |
/** If set, will show a tooltip about why the button is disabled. */ | |
disabledReason?: string; | |
/** If a potential operation is provided, will show a tooltip about why the button is disabled when the operation is not allowed. */ | |
disabled?: boolean | PotentialOperation; |
I think whether this is a good idea is questionable, since it would directly tie the tooltip content to the potential operation disabled reasons... when in most cases you'd probably want some additional processing of those reasons 🤔
🤷
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hrm... yeah, that would be pretty cute, would be a easy if the reasons were just strings:
type PotentialOperation2 {
allowed: Boolean!
disabledReasons: [Reason!]!
}
type Reason {
message: String!
entity: HasBlueprintUrl
}
interface HasBlueprintUrl {
blueprintUrl: Url!
}
But since they're this complex Reason
type, with pretty bespoke Blueprint-y things in it, yeah I'm not sure (as you already mentioned) we'd want to leak that directly into Beam...
Maybe if it was:
disabled: boolean | ReactNode
Where if disabled
is ever "not a boolean" / i.e. a ReactNode
(which could be just a string, or a complex structure) it knows that means "put the react node in the tooltip".
And then we had an adapter like:
<Button disabled=renderPotentialOperation(query.canDoFoo) />
Where the helper method would do app-specific rendering of the PotentialOperation2
instead "just some react nodes to put into the tooltip"...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks awesome!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Will be nice to have this built in!
src/components/Button.stories.tsx
Outdated
@@ -130,3 +130,7 @@ export function Buttons(args: ButtonProps) { | |||
</div> | |||
); | |||
} | |||
|
|||
export function ButtonWithTooltip() { | |||
return <Button disabled={true} disabledReason="You cannot currently perform this operation." label="Upload" />; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts: On having the disabled prop take in a boolean
or a string for the message? Just thinking that disabled and disabledReason would always be in sync.
|
||
export function TooltipDisabled() { | ||
return ( | ||
<Tooltip title="Tooltip Info" disabled={true}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting! I never knew a Tooltip could be disabled!
@@ -10,27 +10,30 @@ import { Css } from "src/Css"; | |||
|
|||
interface TooltipProps { | |||
/** The content that shows up when hovered */ | |||
title: string; | |||
title: ReactNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you! @kbesingeryeh the fix got in!
src/components/Tooltip.tsx
Outdated
state, | ||
triggerRef, | ||
); | ||
const { tooltipProps } = useTooltip(_tooltipProps, state); | ||
|
||
return ( | ||
<> | ||
{React.cloneElement(children, { ref: triggerRef, ...triggerProps })} | ||
<span ref={triggerRef} {...triggerProps}> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: Any advantage to adding a wrapping span
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure cloneElement
ignores the ref
prop (both ref
and key
are ignored, and the children's original ref/key are still used). I'm not really sure if this fixes anything per se, I'd just noticed that "cloneElement
doesn't support ref" while doing other things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that cloneElement supports ref
s otherwise the Tooltip wouldn't work (maybe its ignored if it is given) 😢 Are you facing a case that you need to pass a ref to the Tooltip component (that might be why, since it's being overwritten)?
Here's an old conversation on the subject facebook/react#8873 (comment)
@oagboola and I approached it this way to reduce the wrapping elements needed for the Tooltip and add the Popper ref to the child element (assuming that the child would not have a ref of its own). All good either way!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just looking at:
https://reactjs.org/docs/react-api.html#cloneelement
"Clone and return a new React element using element as the starting point. The resulting element will have the original element’s props with the new props merged in shallowly. New children will replace existing children. key and ref from the original element will be preserved."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it's that preserved behaviour I think is what is special - like if theres are no overrides then it will keep it the original ref. I'll CodeSandbox this for fun!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a CodeSandbox showing that the children's ref is overridden by the clone elements ref! Still a very interesting result!
https://codesandbox.io/s/reactcloneelement-ref-override-gyyhm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh I see, it "preserves it" as in "it won't drop the existing one", but you're allowed to override it...playing devils advocate, if the caller did want to use the ref, i.e. the parentRef
in your example, this approach drops it?
I'll put back though, given it's not broken as I originally though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also makes me think maybe we should have a few snapshot tests to show all of the aria-*
stuff getting wired together. Between this change, and others, I'm not really sure when aria attributes are/are not getting hooked up, and that isn't covered by storybook.
* | ||
* If a ReactNode, it's treated as a "disabled reason" that's shown in a tooltip. | ||
*/ | ||
disabled?: boolean | ReactNode; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯
## [2.37.0](v2.36.0...v2.37.0) (2021-08-04) ### Features * Add Button.disabled can be a tooltip ([#202](#202)) ([84b4641](84b4641))
🎉 This PR is included in version 2.37.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
This is a) a blatant copy/paste from one of @DeanGilewicz 's WIP PRs + b) a long-standing need to be able to
s/BP Button/Beam Button
in BP, because we have quite a few places that use current BP Button'sdisabledReason
prop.