Skip to content
This repository was archived by the owner on Jul 3, 2023. 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
100 changes: 100 additions & 0 deletions src/client/components/button/stories/button.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { action } from '@storybook/addon-actions'
import { storiesOf } from '@storybook/react'
import * as React from 'react'

import { Button } from '../view'

import IconAfter from './icon-after.svg'
import IconBefore from './icon-before.svg'

const onClick = action('onClick')

const HSpacer = () => <span style={{ display: 'inline-block', width: '4px' }}/>
const VSpacer = () => <div style={{ height: '4px' }}/>

storiesOf('components.button', module)
.add('renders basic', () => {
return <div>
<Button onClick={onClick}>Normal</Button>
<HSpacer/>
<Button type='primary' onClick={onClick}>Primary</Button>
</div>
})
.add('renders fullwidth', () => {
return <div>
<Button fullwidth onClick={onClick}>Normal</Button>
<VSpacer/>
<Button type='primary' fullwidth onClick={onClick}>Primary</Button>
</div>
})
.add('renders aligned left', () => {
return <div>
<Button fullwidth textAlign='left' onClick={onClick}>Normal</Button>
<VSpacer/>
<Button type='primary' textAlign='left' fullwidth onClick={onClick}>Primary</Button>
</div>
})
.add('renders aligned right', () => {
return <div>
<Button fullwidth textAlign='right' onClick={onClick}>Normal</Button>
<VSpacer/>
<Button type='primary' textAlign='right' fullwidth onClick={onClick}>Primary</Button>
</div>
})
.add('renders icon before', () => {
return <div>
<Button iconBefore={<IconBefore/>} onClick={onClick}>Button</Button>
<VSpacer/>
<Button iconBefore={<IconBefore/>} fullwidth onClick={onClick}>Fullwidth</Button>
<VSpacer/>
<Button
iconBefore={<IconBefore/>}
fullwidth
textAlign='left'
onClick={onClick}
>Fullwidth, aligned left</Button>
<VSpacer/>
<Button
iconBefore={<IconBefore/>}
fullwidth
textAlign='right'
onClick={onClick}
>Fullwidth, aligned right</Button>
</div>
})
.add('renders icon after', () => {
return <div>
<Button iconAfter={<IconAfter/>} onClick={onClick}>Button</Button>
<VSpacer/>
<Button iconAfter={<IconAfter/>} fullwidth onClick={onClick}>Button, fullwidth</Button>
<VSpacer/>
<Button
iconAfter={<IconAfter/>}
fullwidth
textAlign='left'
onClick={onClick}
>Fullwidth, aligned left</Button>
<VSpacer/>
<Button
iconAfter={<IconAfter/>}
iconAfterAlignedRight
fullwidth
textAlign='left'
onClick={onClick}
>Fullwidth, aligned left, icon aligned right</Button>
<VSpacer/>
<Button
iconAfter={<IconAfter/>}
fullwidth
textAlign='right'
onClick={onClick}
>Fullwidth, aligned right</Button>
</div>
})
.add('renders disabled', () => {
return <div>
<Button disabled>Normal</Button>
<HSpacer/>
<Button type='primary' disabled>Primary</Button>
</div>
})
3 changes: 3 additions & 0 deletions src/client/components/button/stories/icon-after.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/client/components/button/stories/icon-before.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
98 changes: 98 additions & 0 deletions src/client/components/button/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
.button {
display: inline-flex;
height: 32px;
min-width: 64px;
padding: 0 12px;
align-items: center;
font-family: inherit;
border-radius: 4px;
outline: none;
font-size: 14px;
justify-content: center;
align-items: center;
border-width: 1px;
border-style: solid;
}

.button:hover:not([disabled]) {
cursor: pointer;
}

.button[disabled] {
opacity: 0.65
}

.alignLeft {
justify-content: flex-start;
}

.alignRight {
justify-content: flex-end;
}

.fullwidth {
width: 100%;
}

.iconBefore {
margin-right: 8px;
}

.iconAfter {
padding-left: 4px;
}

.iconAfterAlignedRight {
margin-left: auto;
}

.iconBefore svg,
.iconAfter svg {
height: 20px;
width: 20px;
fill: currentColor;
}

/* Type: normal ================================== */

.buttonNormal {
background-color: #eee;
border-color: #CCC;
}

.buttonNormal:hover:not([disabled]) {
border-color: #AAA;
}

.buttonNormal:active:not([disabled]) {
background-color: #DDD;
}

.buttonNormal .iconBefore {
color: #1197d3;
}

.buttonNormal .iconAfter {
color: #777;
}

/* Type: primary ================================= */

.buttonPrimary {
background-color: #1197d3;
border-color: #0f86bd;
color: #fff;
}

.buttonPrimary:hover:not([disabled]) {
border-color: #0f86bd;
}

.buttonPrimary:active:not([disabled]) {
background-color: #108cc6;
}

.buttonPrimary .iconBefore,
.buttonPrimary .iconAfter {
color: white;
}
40 changes: 40 additions & 0 deletions src/client/components/button/view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as classNames from 'classnames'
import * as React from 'react'
import { ReactNode } from 'react'

import * as styles from './styles.css'

export type ButtonProps = {
type?: 'normal' | 'primary'
fullwidth?: boolean
textAlign?: 'left' | 'center' | 'right'
disabled?: boolean
iconBefore?: ReactNode
iconAfter?: ReactNode
iconAfterAlignedRight?: boolean
children?: any
onClick?(): void
}

export class Button extends React.PureComponent<ButtonProps> {
render() {
const {
type = 'normal', textAlign = 'center', fullwidth, disabled,
iconBefore, iconAfter, iconAfterAlignedRight, children, onClick,
} = this.props
return <button onClick={onClick} disabled={disabled} className={classNames(styles.button, {
[styles.buttonPrimary]: type === 'primary',
[styles.buttonNormal]: type === 'normal',
[styles.fullwidth]: fullwidth,
[styles.iconAfterAlignedRight]: iconAfterAlignedRight,
[styles.alignLeft]: textAlign === 'left',
[styles.alignRight]: textAlign === 'right',
})}>
{iconBefore && <span className={styles.iconBefore}>{iconBefore}</span>}
{children}
{iconAfter && <span className={classNames(styles.iconAfter, {
[styles.iconAfterAlignedRight]: iconAfterAlignedRight,
})}>{iconAfter}</span>}
</button>
}
}
35 changes: 0 additions & 35 deletions src/client/components/select/style.css
Original file line number Diff line number Diff line change
@@ -1,38 +1,3 @@
.button {
background-color: transparent;
border: none;
cursor: pointer;
display: flex;
height: 32px;
background-color: #eee;
border: 1px solid #CCC;
padding: 0 8px;
align-items: center;
font-family: inherit;
border-radius: 4px;
line-height: 1;
width: 100%;
outline: none;
}

.button:hover {
border: 1px solid #1197d3;
}

.buttonIcon svg {
fill: #1197d3;
height: 18px;
width: 18px;
margin-right: 12px;
}

.dropdownIcon {
fill: #777;
margin-left: auto;
width: 20px;
height: 20px;
}

.empty {
color: #888;
padding: 1.5em;
Expand Down
38 changes: 20 additions & 18 deletions src/client/components/select/view.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import * as classNames from 'classnames'
import { action } from 'mobx'
import { observable } from 'mobx'
import { observer } from 'mobx-react'
import * as React from 'react'
import { ReactNode } from 'react'
import OutsideClickHandler from 'react-outside-click-handler'

import { Button } from '../button/view'
import { Dropdown } from '../dropdown/view'

import DropdownIcon from './dropdown.svg'
Expand Down Expand Up @@ -51,11 +51,13 @@ export class Select extends React.Component<SelectProps> {
const { className, icon, placeholder, empty, options, selectedOption } = this.props

const button = (
<button className={style.button}>
{ icon && <span className={style.buttonIcon}>{ icon }</span> }
{ selectedOption ? selectedOption.label : placeholder }
<DropdownIcon className={style.dropdownIcon}/>
</button>
<Button
iconBefore={icon}
iconAfter={<DropdownIcon/>}
textAlign='left'
iconAfterAlignedRight
fullwidth
>{selectedOption ? selectedOption.label : placeholder}</Button>
)

return <OutsideClickHandler onOutsideClick={this.close}>
Expand All @@ -67,18 +69,18 @@ export class Select extends React.Component<SelectProps> {
onToggleClick={this.onToggleClick}
>
<div className={style.dropdown}>
{ options.length === 0 && <div className={style.empty}>{ empty || 'No options' }</div> }
{ options.length > 0 && <div className={style.options}>{
options.map(option => {
const isSelected = Boolean(selectedOption && selectedOption.id === option.id)
return <SelectOption
key={option.id}
option={option}
isSelected={isSelected}
onSelect={this.onSelect}
/>
})
}</div>
{options.length === 0 && <div className={style.empty}>{empty || 'No options'}</div>}
{options.length > 0 && <div className={style.options}>{
options.map(option => {
const isSelected = Boolean(selectedOption && selectedOption.id === option.id)
return <SelectOption
key={option.id}
option={option}
isSelected={isSelected}
onSelect={this.onSelect}
/>
})
}</div>
}
</div>
</Dropdown>
Expand Down