Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Features
- Add Icon `xSpacing` prop @Bugaa92 ([#22](https://github.com/stardust-ui/react/pull/22))
- Add Button `icon` prop and Text `truncated` prop @Bugaa92 ([#13](https://github.com/stardust-ui/react/pull/13))
- Add Button `disabled` prop @Bugaa92 ([#14](https://github.com/stardust-ui/react/pull/14))

<!--------------------------------[ v0.2.3 ]------------------------------- -->
## [v0.2.3](https://github.com/stardust-ui/react/tree/v0.2.3) (2018-07-24)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react'
import { Button } from '@stardust-ui/react'

const ButtonExampleDisabled = () => (
<div>
<Button disabled content="Default" />
<Button disabled type="primary" content="Primary" />
<Button disabled type="secondary" content="Secondary" />
<Button disabled type="primary" icon="book" content="Click me" iconPosition="before" />
<Button disabled circular icon="coffee" />
<br />
<br />
<Button disabled fluid content="Fluid" />
</div>
)

export default ButtonExampleDisabled
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react'
import { Button, Icon, Text } from '@stardust-ui/react'

const ButtonExampleDisabled = () => (
<div>
<Button disabled>Default</Button>
<Button disabled type="primary">
Primary
</Button>
<Button disabled type="secondary">
Secondary
</Button>
<Button disabled type="primary" icon iconPosition="before">
<Icon name="book" color="white" xSpacing="after" />
<Text content="Click me" />
</Button>
<Button disabled circular>
<Icon name="coffee" xSpacing="none" />
</Button>
<br />
<br />
<Button disabled fluid>
Fluid
</Button>
</div>
)

export default ButtonExampleDisabled
15 changes: 15 additions & 0 deletions docs/src/examples/components/Button/States/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react'
import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample'
import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection'

const States = () => (
<ExampleSection title="States">
<ComponentExample
title="Disabled"
description="A button can show it is currently unable to be interacted with."
examplePath="components/Button/States/ButtonExampleDisabled"
/>
</ExampleSection>
)

export default States
2 changes: 2 additions & 0 deletions docs/src/examples/components/Button/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import React from 'react'
import Types from './Types'
import Variations from './Variations'
import States from './States'

const ButtonExamples = () => (
<div>
<Types />
<States />
<Variations />
</div>
)
Expand Down
38 changes: 35 additions & 3 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import PropTypes from 'prop-types'
import React, { ReactNode, CSSProperties } from 'react'
import React, { ReactNode, CSSProperties, SyntheticEvent } from 'react'

import { UIComponent, childrenExist, customPropTypes, IRenderResultConfig } from '../../lib'
import buttonRules from './buttonRules'
Expand All @@ -16,9 +16,11 @@ export interface IButtonProps {
circular?: boolean
className?: string
content?: ReactNode
disabled?: boolean
fluid?: boolean
icon?: boolean | string
iconPosition?: IconPosition
onClick?: (e: SyntheticEvent, props: IButtonProps) => void
style?: CSSProperties
type?: ButtonType
}
Expand Down Expand Up @@ -50,6 +52,9 @@ class Button extends UIComponent<IButtonProps, any> {
/** Additional classes. */
className: PropTypes.string,

/** A button can show it is currently unable to be interacted with. */
disabled: PropTypes.bool,

/** Shorthand for primary content. */
content: customPropTypes.contentShorthand,

Expand All @@ -62,6 +67,13 @@ class Button extends UIComponent<IButtonProps, any> {
/** An icon button can format an Icon to appear before or after the button */
iconPosition: PropTypes.oneOf(['before', 'after']),

/**
* Called after user's click.
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props.
*/
onClick: PropTypes.func,

/** A button can be formatted to show different levels of emphasis. */
type: PropTypes.oneOf(['primary', 'secondary']),
}
Expand All @@ -72,9 +84,11 @@ class Button extends UIComponent<IButtonProps, any> {
'circular',
'className',
'content',
'disabled',
'fluid',
'icon',
'iconPosition',
'onClick',
'type',
]

Expand All @@ -87,7 +101,7 @@ class Button extends UIComponent<IButtonProps, any> {
classes,
rest,
}: IRenderResultConfig<IButtonProps>): ReactNode {
const { children, content, icon, iconPosition, type } = this.props
const { children, content, disabled, icon, iconPosition, type } = this.props
const primary = type === 'primary'

const getContent = (): ReactNode => {
Expand All @@ -113,11 +127,29 @@ class Button extends UIComponent<IButtonProps, any> {
}

return (
<ElementType {...rest} className={classes.root}>
<ElementType
className={classes.root}
disabled={disabled}
onClick={this.handleClick}
{...rest}
>
{getContent()}
</ElementType>
)
}

private handleClick = (e: SyntheticEvent) => {
const { onClick, disabled } = this.props

if (disabled) {
e.preventDefault()
return
}

if (onClick) {
onClick(e, this.props)
}
}
}

export default Button
29 changes: 21 additions & 8 deletions src/components/Button/buttonRules.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { pxToRem } from '../../lib'
import { truncateStyle } from '../../styles/customCSS'
import { disabledStyle, truncateStyle } from '../../styles/customCSS'
import { IButtonVariables } from './buttonVariables'
import { IButtonProps } from './Button'

export default {
root: ({ props, variables }: { props: IButtonProps; variables: IButtonVariables }) => {
const { circular, fluid, icon, iconPosition, type } = props
const { circular, disabled, fluid, icon, iconPosition, type } = props
const primary = type === 'primary'
const secondary = type === 'secondary'

Expand All @@ -27,7 +27,7 @@ export default {
typeSecondaryBorderColor,
} = variables

return {
const rules = {
height,
minWidth,
maxWidth,
Expand All @@ -37,12 +37,8 @@ export default {
padding: `0 ${pxToRem(paddingLeftRightValue)}`,
margin: `0 ${pxToRem(8)} 0 0`,
verticalAlign: 'middle',
cursor: 'pointer',
borderWidth: `${secondary ? (circular ? 1 : 2) : 0}px`,
borderRadius: pxToRem(2),
':hover': {
backgroundColor: backgroundColorHover,
},
borderWidth: 0,

...truncateStyle,

Expand All @@ -67,6 +63,23 @@ export default {
width: '100%',
maxWidth: '100%',
}),
}

if (disabled) {
return {
...rules,
...disabledStyle,
}
}

return {
...rules,

borderWidth: `${secondary ? (circular ? 1 : 2) : 0}px`,
cursor: 'pointer',
':hover': {
backgroundColor: backgroundColorHover,
},

...(primary && {
color: typePrimaryColor,
Expand Down
2 changes: 1 addition & 1 deletion src/components/Button/buttonVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export default (siteVars: any): IButtonVariables => {
maxWidth: pxToRem(280),
backgroundColor: siteVars.gray08,
backgroundColorHover: siteVars.gray06,
paddingLeftRightValue: 20,
circularRadius: pxToRem(999),
paddingLeftRightValue: 20,
typePrimaryColor: siteVars.white,
typePrimaryBackgroundColor: siteVars.brand,
typePrimaryBackgroundColorHover: siteVars.brand04,
Expand Down
12 changes: 11 additions & 1 deletion test/specs/components/Button/Button-test.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'

import { isConformant } from 'test/specs/commonTests'
import { getTestingRenderedComponent } from 'test/utils'
import { getTestingRenderedComponent, mountWithProvider } from 'test/utils'

import Button from 'src/components/Button/Button'

Expand Down Expand Up @@ -47,4 +47,14 @@ describe('Button', () => {
expect(btnCircular).toEqual(true)
})
})

describe('onClick', () => {
it('does not call onClick when the button is disabled', () => {
const onClick = jest.fn()
const button = mountWithProvider(<Button disabled onClick={onClick} />).find('Button')
button.simulate('click')

expect(onClick).not.toHaveBeenCalled()
})
})
})