aaronjameslang/antd-promise-button

View on GitHub
src/PromiseButton.stories.tsx

Summary

Maintainability
D
2 days
Test Coverage
import { action } from '@storybook/addon-actions'
import {
  boolean,
  color,
  number,
  select,
  text,
} from '@storybook/addon-knobs/react'
import { storiesOf } from '@storybook/react'
import 'antd/lib/button/style/css'
import Form from 'antd/lib/form'
import 'antd/lib/form/style/css'
import Icon from 'antd/lib/icon'
import 'antd/lib/icon/style/css'
import Input from 'antd/lib/input'
import 'antd/lib/input/style/css'
import React from 'react'
import { Button } from '.'

const delay = <T extends any> (x?: T, y?: Error) =>
  new Promise((res, rej) => setTimeout(
    () => {
      if (y) rej(y)
      else res(x)
    },
    Math.random() * 1000 + 1000,
  ))

// tslint:disable:max-classes-per-file

storiesOf('Promise Button/BC Native Button', module)
  .add('Basic', () => (
    <Button
      onClick={() => action('Clicked')()}
    >Click Me!</Button>
  ), {
    notes: 'If your onClick handler doesn\'t return a promise, the button will behave like an ordinary button',
  })
  .add('Submit/Reset', () => (
    <form style={{
      display: 'flex',
      flexDirection: 'column',
    }}>
      <label htmlFor='name'>Name (4 to 8 characters):</label>
      <input
        maxLength={8}
        minLength={4}
        name='name'
        required
      />
      <Button htmlType='submit' type='primary'
      >Submit</Button>
      <Button htmlType='reset'>Reset</Button>
    </form>
  ))
storiesOf('Promise Button/BC Anchor Button', module)
  .add('Basic', () => (
    <Button
     href='//example.com'
    >Go To Example</Button>
  ))
  .add('New Tab', () => (
    <Button
      href='//example.com'
      target='_blank'
    >Go To Example</Button>
  ))
  .add('On Click', () => (
    <Button
      href='//example.com'
      onClick={() => action('Clicked')()}
      target='_blank'
    >Go To Example</Button>
  ))
  .add('Large Primary', () => (
    <Button
      href='//example.com'
      size='large'
      type='primary'
    >Go To Example</Button>
  ))
storiesOf('Promise Button', module)
  .add('Simple', () => (
    <div>
      <Button
        onClick={() => delay()}
        type='primary'
      >Click Me!</Button>
      <Button
        onClick={() => delay()}
      >No, Click Me!</Button>
    </div>
  ), {
    notes: 'The only requirement is that your onClick returns a promise. We\'ll do the rest!',
  })
  .add('Login Form', () => {
    const actionButtonClicked = action('button clicked')
    let password = 'changeme'
    const login = (event: React.MouseEvent<HTMLButtonElement>) => new Promise((res, rej) => {
      actionButtonClicked(event)
      setTimeout(() => {
        if (password === 'changeme') {
          action('login approved')(password)
          res()
        } else {
          action('login rejected')(password)
          rej()
        }
      }, 1000)
    })
    return (
      <div style={{
        alignItems: 'center',
        display: 'flex',
        flexDirection: 'column',
        maxWidth: '32em',
      }}>
        <Form.Item label='Username' >
          <Input
            defaultValue='alice'
            type='text'
          />
        </Form.Item>
        <Form.Item label='Password' >
          <Input
            defaultValue={password}
            type='password'
            onChange={event => {
              action('password changed')(event)
              password = (event.target as { value: string}).value
            }}
          />
        </Form.Item>
        <Button
          style={{ marginTop: '1em', minWidth: '10em' }}
          type='primary'
          onClick={login}
          labels={{
            [Button.FULFILLED]: 'Logged In',
            [Button.INITIALISED]: 'Log In',
            [Button.PENDING]: 'Logging In',
            [Button.REJECTED]: 'Log In Failed',
          }}
        />
        <Button
          style={{ marginTop: '1em', minWidth: '10em' }}
          // type='default'
          onClick={login}
          labels={{
            [Button.FULFILLED]: 'Signed Up',
            [Button.INITIALISED]: 'Sign Up',
            [Button.PENDING]: 'Signing Up',
            [Button.REJECTED]: 'Sign Up Failed',
          }}
        />
      </div>
    )
  }, {
    notes: { markdown: `
Promise buttons can be used to make many interfaces feel more responsive.

When the user clicks the button, a loading spinner is shown and the button is disabled.
This feedback assures the user that their action is being processed,
and prevents accidental double-logins and other bugs.

The expected password is "changeme".

If the password is correct, the button turns green and shows a successful message.
This makes the page feel more responsive while the user waits for the
page redirection or other slow process to complete.

If the password is incorrect, the button will turn red and show an error message.
Try changing the password.
`},
  })
  .add('Variety', () => {
    let clicks = 0
    const onClick = () =>
      new Promise((res, rej) => {
        clicks += 1
        setTimeout(() => clicks % 2 ? res() : rej(), 1000)
      })
    class Row extends React.Component<any> {
      public render () {
        return <div style={{
          alignItems: 'center',
          display: 'flex',
          flexDirection: 'row',
          justifyContent: 'space-around',
          margin: '1em',
          width: '100%',
          ...this.props.style,
        }} {...this.props}/>
      }
    }
    class Col extends React.Component {
      public render () {
        return <div style={{
          alignItems: 'center',
          display: 'flex',
          flexDirection: 'column',
          height: '100%',
          justifyContent: 'space-around',
        }} {...this.props}/>
      }
    }
    return (
      <Col>
        <Row>
        <Button type='primary' size='large' onClick={onClick}>Primary</Button>
        <Button size='large' onClick={onClick}>Normal</Button>
        <Button type='dashed' size='large' onClick={onClick}>Dashed</Button>
        <Button type='danger' size='large' onClick={onClick}>Danger</Button>
        </Row><Row>
        <Button type='primary' onClick={onClick}>Primary</Button>
        <Button onClick={onClick}>Default</Button>
        <Button type='dashed' onClick={onClick}>Dashed</Button>
        <Button type='danger' onClick={onClick}>Danger</Button>
      </Row><Row>
        <Button type='primary' size='small' onClick={onClick}>Primary</Button>
        <Button size='small' onClick={onClick}>Normal</Button>
        <Button type='dashed' size='small' onClick={onClick}>Dashed</Button>
        <Button type='danger' size='small' onClick={onClick}>Danger</Button>
      </Row><Row>
        <Button type='primary' disabled onClick={onClick}>Primary</Button>
        <Button disabled onClick={onClick}>Default</Button>
        <Button type='dashed' disabled onClick={onClick}>Dashed</Button>
        <Button type='danger' disabled onClick={onClick}>Danger</Button>
      </Row><Row>
        <Button type='primary' shape='circle' icon='search' onClick={onClick}/>
        <Button type='primary' icon='search' onClick={onClick}>Search</Button>
        <Button shape='circle' icon='search' onClick={onClick}/>
        <Button icon='search' onClick={onClick}>Search</Button>
        <Button type='dashed' shape='circle' icon='search' onClick={onClick}/>
        <Button type='dashed' icon='search' onClick={onClick}>Search</Button>
      </Row><Row>
        <Button type='primary' shape='circle' icon='download' onClick={onClick}/>
        <Button type='primary' shape='round' icon='download' onClick={onClick}>Download</Button>
        <Button type='primary' icon='download' onClick={onClick}>Download</Button>
      </Row><Row>
        <Button.Group>
        <Button type='primary' onClick={onClick}>
        <Icon type='left' />Backward
        </Button>
        <Button type='primary' onClick={onClick}>
        Forward<Icon type='right' />
        </Button>
        </Button.Group>
      </Row><Row style={{ background: 'rgb(190, 200, 200)' }}>
          <Button type='primary' ghost onClick={onClick}>Primary</Button>
          <Button ghost onClick={onClick}>Default</Button>
          <Button type='dashed' ghost onClick={onClick}>Dashed</Button>
          <Button type='danger' ghost onClick={onClick}>danger</Button>
      </Row>
      </Col>
    )
  }, {
    notes: { markdown: `
Promise buttons can be configured in all the normal ways you'd expect
from [antd](https://ant.design/components/button/).
`},
  })
  .add('Custom', () => {
    const succeed = boolean('Succeed', true, 'Promise')
    const type = select('Type', ['primary', 'default', 'dashed', 'danger'], 'primary', 'Original')
    const timeout = number('Reset Timeout', 2000, {}, 'Promise')
    const colors = {
      [Button.FULFILLED]: color('Color Fulfilled', '#52c41a', 'Promise'),
      [Button.REJECTED]: color('Color Rejected', '#f5222d', 'Promise'),
    }
    const labels = {
      [Button.FULFILLED]: text('Label Fulfilled', 'Submitted', 'Labels'),
      [Button.INITIALISED]: text('Label Initialised', 'Submit', 'Labels'),
      [Button.PENDING]: text('Label Pending', 'Submitting', 'Labels'),
      [Button.REJECTED]: text('Label Rejected', 'Not Submitted', 'Labels'),
    }
    return (
      <Button
        colors={colors}
        labels={labels}
        timeout={timeout}
        type={type}
        onClick={() => new Promise((res, rej) => {
          setTimeout(() => { succeed ? res() : rej() }, 1000)
        })}
      />

    )
  })