Skip to content

Commit

Permalink
fix(Button): allow using disabled inside Button.Group
Browse files Browse the repository at this point in the history
  • Loading branch information
s0ber committed Sep 3, 2020
1 parent b373a37 commit 81d6c83
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 50 deletions.
13 changes: 7 additions & 6 deletions packages/picasso/src/Button/Button.tsx
Expand Up @@ -84,7 +84,7 @@ export interface StaticProps {
}

const getVariantType = (variant: VariantType) => {
const [type] = variant!.split('-')
const [type] = variant!.split('-') // eslint-disable-line @typescript-eslint/no-non-null-assertion

return type
}
Expand All @@ -93,6 +93,7 @@ const useStyles = makeStyles<Theme, Props>(styles, { name: 'PicassoButton' })

const defaultOnClick = () => {}

// eslint-disable-next-line complexity
export const Button = forwardRef<HTMLButtonElement, Props>(function Button(
props,
ref
Expand Down Expand Up @@ -152,11 +153,11 @@ export const Button = forwardRef<HTMLButtonElement, Props>(function Button(
}
}

const variantType = getVariantType(variant!)
const variantType = getVariantType(variant!) // eslint-disable-line @typescript-eslint/no-non-null-assertion
const variantClassName = disabled
? classes[`${variantType}Disabled`]
: classes[kebabToCamelCase(variant!)]
const sizeClassName = classes[size!]
: classes[kebabToCamelCase(variant!)] // eslint-disable-line @typescript-eslint/no-non-null-assertion
const sizeClassName = classes[size!] // eslint-disable-line @typescript-eslint/no-non-null-assertion

const rootClassName = cx(
{
Expand All @@ -180,14 +181,14 @@ export const Button = forwardRef<HTMLButtonElement, Props>(function Button(
root: rootClassName,
disabled: classes.disabled
}}
onClick={loading ? defaultOnClick : onClick}
onClick={loading || disabled ? defaultOnClick : onClick}
className={className}
style={style}
disabled={disabled}
title={title}
value={value}
type={type}
component={disabled && as === 'button' ? 'span' : as!}
component={disabled && as === 'button' ? 'span' : as!} // eslint-disable-line @typescript-eslint/no-non-null-assertion
>
<Container
as='span'
Expand Down
21 changes: 0 additions & 21 deletions packages/picasso/src/Button/story/DisabledWithEvents.example.tsx

This file was deleted.

31 changes: 31 additions & 0 deletions packages/picasso/src/Button/story/DisabledWithTooltip.example.tsx
@@ -0,0 +1,31 @@
import React from 'react'
import { Container, Button, Tooltip } from '@toptal/picasso'
import { useNotifications } from '@toptal/picasso/utils'

const Example = () => {
const { showSuccess } = useNotifications()
const handleClick = () => showSuccess('I was clicked')

return (
<div>
<Tooltip content='Action is disabled'>
<Button disabled onClick={handleClick}>
Disabled With Tooltip
</Button>
</Tooltip>

<Container top='small'>
<Button.Group>
<Button onClick={handleClick}>Enabled</Button>
<Tooltip content='Action is disabled'>
<Button disabled onClick={handleClick}>
Disabled With Tooltip
</Button>
</Tooltip>
</Button.Group>
</Container>
</div>
)
}

export default Example
8 changes: 4 additions & 4 deletions packages/picasso/src/Button/story/index.jsx
Expand Up @@ -192,10 +192,10 @@ page
title: 'Disabled',
description: 'The button shows that currently unable to be interacted with'
})
.addExample('Button/story/DisabledWithEvents.example.tsx', {
title: 'Disabled with mouse events',
description: 'The button is disabled, but event handlers can be added'
})
.addExample(
'Button/story/DisabledWithTooltip.example.tsx',
'Disabled with tooltip'
)
.addExample('Button/story/Sizes.example.jsx', 'Sizes')
.addExample('Button/story/FullWidth.example.jsx', 'Full width')
.addExample('Button/story/IconButtons.example.jsx', 'Button with Icon')
Expand Down
24 changes: 19 additions & 5 deletions packages/picasso/src/ButtonGroup/ButtonGroup.tsx
@@ -1,9 +1,15 @@
import React, { ReactNode, HTMLAttributes, forwardRef } from 'react'
import { withStyles } from '@material-ui/core/styles'
import cx from 'classnames'
import { StandardProps, withClasses } from '@toptal/picasso-shared'
import {
StandardProps,
withClasses,
Classes,
OverridableComponent
} from '@toptal/picasso-shared'

import Button from '../Button'
import Tooltip from '../Tooltip'
import styles from './styles'

export interface Props extends StandardProps, HTMLAttributes<HTMLDivElement> {
Expand Down Expand Up @@ -35,12 +41,20 @@ ButtonGroup.displayName = 'ButtonGroup'

export default withStyles(styles)(
withClasses(classes => [
[
Button,
{
{
componentType: Button,
classes: {
root: classes.button,
active: classes.active
} as Classes
},
{
componentType: Tooltip as OverridableComponent,
classes: {} as Classes,
childrenClasses: {
root: classes.button,
active: classes.active
}
]
}
])(ButtonGroup)
)
17 changes: 15 additions & 2 deletions packages/picasso/src/Tooltip/Tooltip.tsx
Expand Up @@ -9,7 +9,12 @@ import React, {
import { makeStyles, Theme } from '@material-ui/core/styles'
import MUITooltip from '@material-ui/core/Tooltip'
import cx from 'classnames'
import { usePicassoRoot, BaseProps } from '@toptal/picasso-shared'
import {
usePicassoRoot,
BaseProps,
withClasses,
Classes
} from '@toptal/picasso-shared'

import styles from './styles'

Expand Down Expand Up @@ -53,6 +58,7 @@ export interface Props extends BaseProps, HTMLAttributes<HTMLDivElement> {
delay?: DelayType
/** Show a compact tooltip */
compact?: boolean
childrenClasses?: Classes
}

const useStyles = makeStyles<Theme, Props>(styles, { name: 'PicassoTooltip' })
Expand All @@ -64,6 +70,7 @@ export const Tooltip: FunctionComponent<Props> = props => {
placement,
interactive,
className,
childrenClasses, // eslint-disable-line @typescript-eslint/no-unused-vars
style,
arrow,
open,
Expand Down Expand Up @@ -150,4 +157,10 @@ Tooltip.defaultProps = {
disablePortal: false
}

export default Tooltip
// Forward classes to children if were provided,
// e.g. from ButtonGroup to Button that is wrapped by the Tooltip
export default withClasses((_classes, childrenClasses) => [
{
classes: childrenClasses
}
])(Tooltip) as typeof Tooltip
2 changes: 1 addition & 1 deletion packages/shared/src/styles/withClasses/test.tsx
Expand Up @@ -12,7 +12,7 @@ const TestComponent = (props: { children: ReactElement; classes: Classes }) => {
}

const DecoratedComponent = withClasses(classes => [
[Button, { root: classes.test }]
{ componentType: Button, classes: { root: classes.test } }
])(TestComponent)

const renderComponent = () => {
Expand Down
44 changes: 33 additions & 11 deletions packages/shared/src/styles/withClasses/withClasses.tsx
Expand Up @@ -2,20 +2,25 @@ import React, { ComponentType, ReactNode } from 'react'

import { Classes } from '../types'

type ConfigItem = [ComponentType, Classes]
type Config = (classes: Classes) => ConfigItem[]
type ConfigItem = {
componentType?: ComponentType
classes?: Classes
childrenClasses?: Classes
}
type Config = (classes: Classes, childrenClasses?: Classes) => ConfigItem[]

export interface WithClassesProps {
classes: Classes
children: ReactNode
childrenClasses?: Classes
}

export default (config: Config) => {
const withClasses = <T extends WithClassesProps>(
Component: ComponentType<T>
) => {
const Wrapper = (props: T) => {
const { children, classes } = props
const { children, classes, childrenClasses } = props

const modifiedChildren = React.Children.map(children, childNode => {
if (!React.isValidElement(childNode)) {
Expand All @@ -24,19 +29,36 @@ export default (config: Config) => {

let childResult = childNode

config(classes).forEach(([componentType, configClasses]) => {
if (childNode.type === componentType) {
childResult = React.cloneElement(childNode, {
classes: configClasses
})
config(classes, childrenClasses).forEach(
({
componentType,
classes: configClasses,
childrenClasses: configChildrenClasses
}) => {
if (!componentType || childNode.type === componentType) {
childResult = configChildrenClasses
? React.cloneElement(childNode, {
classes: configClasses,
childrenClasses: configChildrenClasses
})
: configClasses
? React.cloneElement(childNode, { classes: configClasses })
: childNode
}
}
})
)

return childResult
})

// eslint-disable-next-line react/jsx-props-no-spreading
return <Component {...props}>{modifiedChildren}</Component>
return (
// eslint-disable-next-line react/jsx-props-no-spreading
<Component {...props}>
{modifiedChildren.length === 1
? modifiedChildren[0]
: modifiedChildren}
</Component>
)
}

Wrapper.displayName = Component.displayName || Component.name
Expand Down

0 comments on commit 81d6c83

Please sign in to comment.