Remove bloomer from packages/client

This commit is contained in:
Jerko Steiner 2019-11-03 12:34:26 -04:00
parent 2d4cb2d275
commit a3578e35ba
18 changed files with 196 additions and 151 deletions

View File

@ -2,11 +2,12 @@ import styled from 'styled-components'
import { getColor, ColorSchemeProps, getBorder } from '../theme' import { getColor, ColorSchemeProps, getBorder } from '../theme'
export const Button = styled.button<ColorSchemeProps>` export const Button = styled.button<ColorSchemeProps>`
font-size: 1rem;
padding: 0.5rem; padding: 0.5rem;
background: transparent; background: transparent;
color: ${getColor}; color: ${getColor};
border: ${getBorder}; border: ${getBorder};
border-radius: ${props => props.theme.border.radius}px; border-radius: ${props => props.theme.border.radius};
cursor: pointer; cursor: pointer;
&:hover { &:hover {
@ -17,4 +18,8 @@ export const Button = styled.button<ColorSchemeProps>`
&:hover:active { &:hover:active {
box-shadow: 0 0 3px ${getColor}; box-shadow: 0 0 3px ${getColor};
} }
& > * {
vertical-align: middle;
}
` `

View File

@ -0,0 +1,7 @@
import styled from 'styled-components'
export const Heading = styled.h1`
text-transform: uppercase;
font-weight: 300;
font-size: 1.6rem;
`

View File

@ -0,0 +1,6 @@
import styled from 'styled-components'
import { ColorSchemeProps, getColor } from '../theme'
export const Help = styled.div<ColorSchemeProps>`
color: ${getColor};
`

View File

@ -1,9 +1,11 @@
import React from 'react' import React from 'react'
import {IconType} from 'react-icons' import {IconType} from 'react-icons'
import styled from 'styled-components' import styled from 'styled-components'
import { Help } from './Help'
export interface InputProps { export interface InputProps {
name: string name: string
error?: string
type: 'text' | 'password' | 'hidden' | 'submit' | 'email' type: 'text' | 'password' | 'hidden' | 'submit' | 'email'
value?: string value?: string
onChange?: (name: this['name'], value: string) => void onChange?: (name: this['name'], value: string) => void
@ -14,20 +16,20 @@ export interface InputProps {
required?: boolean required?: boolean
} }
const Field = styled.div` export const Field = styled.div`
margin-bottom: 1em; margin-bottom: 1em;
` `
const Label = styled('label')` export const Label = styled('label')`
text-transform: uppercase; text-transform: uppercase;
font-size: 11px; font-size: 0.8rem;
` `
const Control = styled.div` export const Control = styled.div`
position: relative; position: relative;
` `
const Icon = styled.span` export const Icon = styled.span`
position: absolute; position: absolute;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -43,7 +45,11 @@ const Icon = styled.span`
color: ${props => props.theme.grey.light}; color: ${props => props.theme.grey.light};
` `
const TextInput = styled.input<{hasIconLeft?: boolean}>` export const TextInput = styled.input<{hasIconLeft?: boolean}>`
font-size: 1rem;
border-radius: ${props => props.theme.border.radius};
border: ${props => props.theme.border.width}
solid ${props => props.theme.grey.light};
padding: 0.5rem 0.75rem; padding: 0.5rem 0.75rem;
padding-left: ${props => props.hasIconLeft ? '2.5rem' : '0.75rem'} padding-left: ${props => props.hasIconLeft ? '2.5rem' : '0.75rem'}
width: 100%; width: 100%;
@ -75,6 +81,9 @@ export class Input extends React.PureComponent<InputProps> {
required={this.props.required} required={this.props.required}
/> />
{this.props.Icon && <Icon><this.props.Icon /></Icon>} {this.props.Icon && <Icon><this.props.Icon /></Icon>}
{this.props.error && (
<Help colorScheme='danger'>{this.props.error}</Help>
)}
</Control> </Control>
</Field> </Field>
) )

View File

@ -1,47 +1,47 @@
import React from 'react' // import React from 'react'
import T from 'react-dom/test-utils' // import T from 'react-dom/test-utils'
import {Modal} from './Modal' // import {Modal} from './Modal'
import {TestUtils} from '../test-utils' // import {TestUtils} from '../test-utils'
//
describe('Modal', () => { // describe('Modal', () => {
//
class TestToggle extends React.PureComponent<{}, {visible: boolean}> { // class TestToggle extends React.PureComponent<{}, {visible: boolean}> {
constructor(props: {}) { // constructor(props: {}) {
super(props) // super(props)
this.state = { // this.state = {
visible: false, // visible: false,
} // }
} // }
toggle = () => { // toggle = () => {
this.setState({ // this.setState({
visible: !this.state.visible, // visible: !this.state.visible,
}) // })
} // }
render() { // render() {
return ( // return (
<div> // <div>
<button onClick={this.toggle}>Toggle</button> // <button onClick={this.toggle}>Toggle</button>
<Modal isActive={this.state.visible}> // <Modal isActive={this.state.visible}>
hi! // hi!
</Modal> // </Modal>
</div> // </div>
) // )
} // }
} // }
//
describe('isActive', () => { // describe('isActive', () => {
const t = new TestUtils() // const t = new TestUtils()
//
it('toggles is-active via isActive property', () => { // it('toggles is-active via isActive property', () => {
const {node} = t.render(<TestToggle />) // const {node} = t.render(<TestToggle />)
expect(node.innerHTML).toContain('hi!') // expect(node.innerHTML).toContain('hi!')
const modal = node.querySelector('.modal') as HTMLElement // const modal = node.querySelector('.modal') as HTMLElement
expect(modal.className).toEqual('modal') // expect(modal.className).toEqual('modal')
T.Simulate.click(node.querySelector('button') as HTMLElement) // T.Simulate.click(node.querySelector('button') as HTMLElement)
expect(modal.className).toEqual('modal is-active') // expect(modal.className).toEqual('modal is-active')
T.Simulate.click(node.querySelector('button') as HTMLElement) // T.Simulate.click(node.querySelector('button') as HTMLElement)
expect(modal.className).toEqual('modal') // expect(modal.className).toEqual('modal')
}) // })
}) // })
//
}) // })

View File

@ -1,20 +1,21 @@
import {Modal as M, ModalBackground, ModalContent, ModalClose} from 'bloomer' // import {Modal as M, ModalBackground, ModalContent, ModalClose} from 'bloomer'
import React from 'react' // import React from 'react'
//
export interface ModalProps { // export interface ModalProps {
isActive?: boolean // isActive?: boolean
} // }
//
export class Modal extends React.PureComponent<ModalProps> { // export class Modal extends React.PureComponent<ModalProps> {
render() { // render() {
return ( // return (
<M isActive={this.props.isActive}> // <M isActive={this.props.isActive}>
<ModalBackground /> // <ModalBackground />
<ModalContent> // <ModalContent>
{this.props.children} // {this.props.children}
</ModalContent> // </ModalContent>
<ModalClose /> // <ModalClose />
</M> // </M>
) // )
} // }
} // }
export {}

View File

@ -0,0 +1,15 @@
import styled from 'styled-components'
export const Panel = styled.div`
border: ${props => props.theme.border.width}
solid ${props => props.theme.grey.lighter};
`
export const PanelHeading = styled.div`
background-color: ${props => props.theme.grey.lighter};
padding: 1rem;
`
export const PanelBlock = styled.div`
padding: 1rem;
`

View File

@ -0,0 +1,6 @@
import styled from 'styled-components'
export const Well = styled.div`
background-color: ${props => props.theme.grey.lighter};
border-radius: ${props => props.theme.border.radius};
`

View File

@ -1,9 +1,13 @@
export * from './Button' export * from './Button'
export * from './Heading'
export * from './Help'
export * from './Input' export * from './Input'
export * from './Link' export * from './Link'
export * from './Modal' export * from './Modal'
export * from './Panel'
export * from './Redirect' export * from './Redirect'
export * from './ReturnHere' export * from './ReturnHere'
export * from './TimeAgo' export * from './TimeAgo'
export * from './Well'
export * from './withHistory' export * from './withHistory'
export * from './WithRouterProps' export * from './WithRouterProps'

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import {Control, Field, Heading, Icon, Input} from 'bloomer' import {Control, Field, Heading, Icon, TextInput} from '../components'
import {CRUDChangeParams} from './CRUDActions' import {CRUDChangeParams} from './CRUDActions'
export type CRUDFieldType = 'text' | 'password' | 'number' | 'email' | 'tel' export type CRUDFieldType = 'text' | 'password' | 'number' | 'email' | 'tel'
@ -53,15 +53,15 @@ export class CRUDField<T> extends React.PureComponent<CRUDFieldProps<T>> {
return ( return (
<Field> <Field>
<Heading>{label}</Heading> <Heading>{label}</Heading>
<Control hasIcons={!!this.props.Icon}> <Control>
<Input <TextInput
name={name} name={name}
onChange={this.handleChange} onChange={this.handleChange}
placeholder={placeholder} placeholder={placeholder}
value={value} value={value}
/> />
{!!this.props.Icon && ( {!!this.props.Icon && (
<Icon isSize='small' isAlign='left'> <Icon>
<this.props.Icon /> <this.props.Icon />
</Icon> </Icon>
)} )}

View File

@ -1,5 +1,5 @@
import React from 'react' import React from 'react'
import {Button, Panel, PanelHeading, PanelBlock} from 'bloomer' import {Button, Panel, PanelHeading, PanelBlock} from '../components'
import {FaPlus, FaEdit, FaTimes} from 'react-icons/fa' import {FaPlus, FaEdit, FaTimes} from 'react-icons/fa'
import {Link} from '../components' import {Link} from '../components'
@ -66,7 +66,7 @@ export class CRUDItemRow<T> extends React.PureComponent<CRUDItemRowProps<T>> {
&nbsp; &nbsp;
{!!editLink && ( {!!editLink && (
<Link to={editLink}> <Link to={editLink}>
<Button isInverted isColor='link' aria-label='Edit'> <Button colorScheme='primary' aria-label='Edit'>
<FaEdit /> <FaEdit />
</Button> </Button>
</Link> </Link>
@ -76,8 +76,7 @@ export class CRUDItemRow<T> extends React.PureComponent<CRUDItemRowProps<T>> {
<Button <Button
aria-label='Remove' aria-label='Remove'
onClick={this.handleRemove} onClick={this.handleRemove}
isColor='danger' colorScheme='danger'
isInverted
> >
<FaTimes /> <FaTimes />
</Button> </Button>

View File

@ -1,7 +1,23 @@
import { Breadcrumb, BreadcrumbItem } from 'bloomer'
import React from 'react' import React from 'react'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { CrumbLink } from './CrumbLink' import { CrumbLink } from './CrumbLink'
import styled from 'styled-components'
const Breadcrumb = styled.ul`
list-style: none;
padding: 0;
`
const BreadcrumbItem = styled.li`
display: inline-block;
padding: 1rem 0.5rem 1rem 0;
& + &:before {
padding-right: 0.5rem;
color: ${props => props.theme.grey.light};
content: '»';
}
`
export interface CrumbProps { export interface CrumbProps {
links: CrumbLink[] links: CrumbLink[]

View File

@ -1,4 +1,4 @@
import { Button, Control, Heading, Help, Input } from 'bloomer' import { Button, Heading, Help, Input } from '../components'
import React from 'react' import React from 'react'
import { FaCheck, FaEdit, FaPlusSquare } from 'react-icons/fa' import { FaCheck, FaEdit, FaPlusSquare } from 'react-icons/fa'
import { TeamActions, Team } from '@rondo.dev/common' import { TeamActions, Team } from '@rondo.dev/common'
@ -34,9 +34,8 @@ extends React.PureComponent<TeamEditorProps, TeamEditorState> {
getName(team?: Team) { getName(team?: Team) {
return team ? team.name : '' return team ? team.name : ''
} }
handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { handleChange = (name: string, value: string) => {
const name = event.target.value this.setState({name: value})
this.setState({name})
} }
handleSubmit = async (event: React.FormEvent) => { handleSubmit = async (event: React.FormEvent) => {
event.preventDefault() event.preventDefault()
@ -54,8 +53,6 @@ extends React.PureComponent<TeamEditorProps, TeamEditorState> {
} }
} }
render() { render() {
const {error} = this.state
return ( return (
<form <form
autoComplete='off' autoComplete='off'
@ -64,27 +61,19 @@ extends React.PureComponent<TeamEditorProps, TeamEditorState> {
<Heading> <Heading>
{this.props.type === 'update' ? 'Edit team' : 'Add team'} {this.props.type === 'update' ? 'Edit team' : 'Add team'}
</Heading> </Heading>
<Control hasIcons='left'> <Input
<Input error={this.state.error}
placeholder='Team Name' name='name'
type='text' label='Team Name'
value={this.state.name} placeholder='Team Name'
onChange={this.handleChange} type='text'
/> value={this.state.name}
<span className='icon is-left'> onChange={this.handleChange}
{this.props.type === 'update' ? <FaEdit /> : <FaPlusSquare />} Icon={this.props.type === 'update' ? FaEdit : FaPlusSquare}
</span> />
{error && ( <div className='text-right'>
<Help isColor='danger'>{error}</Help> <Button type='submit'>
)} <FaCheck /> Save
</Control>
<div className='text-right mt-1'>
<Button
isColor='dark'
className='button'
type='submit'
>
<FaCheck className='mr-1' /> Save
</Button> </Button>
</div> </div>
</form> </form>

View File

@ -1,5 +1,5 @@
import { ReadonlyRecord, Team, TeamActions } from '@rondo.dev/common' import { ReadonlyRecord, Team, TeamActions } from '@rondo.dev/common'
import { Button, Panel, PanelBlock, PanelHeading } from 'bloomer' import { Button, Panel, PanelBlock, PanelHeading } from '../components'
import React from 'react' import React from 'react'
import { FaEdit, FaPlus, FaTimes } from 'react-icons/fa' import { FaEdit, FaPlus, FaTimes } from 'react-icons/fa'
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
@ -34,16 +34,15 @@ export class TeamRow extends React.PureComponent<TeamProps> {
{!!ListButtons && <ListButtons team={team} />} {!!ListButtons && <ListButtons team={team} />}
&nbsp; &nbsp;
<Link to={`/teams/${team.id}/users`}> <Link to={`/teams/${team.id}/users`}>
<Button isInverted isColor='link' aria-label='Edit Team'> <Button colorScheme='primary' aria-label='Edit Team'>
<FaEdit /> <FaEdit />
</Button> </Button>
</Link> </Link>
&nbsp; &nbsp;
<Button <Button
aria-label='Remove' aria-label='Remove'
colorScheme='danger'
onClick={this.handleRemove} onClick={this.handleRemove}
isColor='danger'
isInverted
> >
<FaTimes /> <FaTimes />
</Button> </Button>

View File

@ -1,9 +1,9 @@
import { UserInTeam, ReadonlyRecord, TeamActions, UserActions, Team } from '@rondo.dev/common' import { ReadonlyRecord, Team, TeamActions, UserActions, UserInTeam } from '@rondo.dev/common'
import { Panel, PanelBlock, PanelHeading } from 'bloomer'
import { History, Location } from 'history' import { History, Location } from 'history'
import React from 'react' import React from 'react'
import { match as Match } from 'react-router' import { match as Match } from 'react-router'
import { Route, Switch } from 'react-router-dom' import { Route, Switch } from 'react-router-dom'
import { Panel, PanelBlock, PanelHeading } from '../components'
import { TeamEditor } from './TeamEditor' import { TeamEditor } from './TeamEditor'
import { TeamList } from './TeamList' import { TeamList } from './TeamList'
import { TeamUserList } from './TeamUserList' import { TeamUserList } from './TeamUserList'
@ -67,7 +67,7 @@ export class TeamManager extends React.PureComponent<TeamManagerProps> {
<> <>
<Panel> <Panel>
<PanelHeading>Edit Team: {team && team.name}</PanelHeading> <PanelHeading>Edit Team: {team && team.name}</PanelHeading>
<PanelBlock isDisplay='block'> <PanelBlock>
{team && <TeamEditor {team && <TeamEditor
key={team.id} key={team.id}
type='update' type='update'

View File

@ -1,5 +1,5 @@
import { User, UserInTeam, ReadonlyRecord, TeamActions, UserActions, Team } from '@rondo.dev/common' import { User, UserInTeam, ReadonlyRecord, TeamActions, UserActions, Team } from '@rondo.dev/common'
import { Button, Control, Heading, Help, Input, Panel, PanelBlock, PanelHeading } from 'bloomer' import { Button, Input, Panel, PanelBlock, PanelHeading, Heading } from '../components'
import React from 'react' import React from 'react'
import { FaCheck, FaTimes, FaUser } from 'react-icons/fa' import { FaCheck, FaTimes, FaUser } from 'react-icons/fa'
@ -52,8 +52,7 @@ export class TeamUser extends React.PureComponent<TeamUserProps> {
<div className='ml-auto'> <div className='ml-auto'>
<Button <Button
aria-label='Remove' aria-label='Remove'
isColor='danger' colorScheme='danger'
isInverted
className='team-user-remove' className='team-user-remove'
onClick={this.handleRemoveUser} onClick={this.handleRemoveUser}
> >
@ -74,8 +73,7 @@ export class AddUser extends React.PureComponent<AddUserProps, AddUserState> {
user: undefined, user: undefined,
} }
} }
handleChangeEmail = (event: React.ChangeEvent<HTMLInputElement>) => { handleChangeEmail = (name: string, email: string) => {
const email = event.target.value
this.setState({email}) this.setState({email})
} }
handleAddUser = async (event: React.FormEvent) => { handleAddUser = async (event: React.FormEvent) => {
@ -97,33 +95,22 @@ export class AddUser extends React.PureComponent<AddUserProps, AddUserState> {
this.setState({error: '', email: '', user: undefined}) this.setState({error: '', email: '', user: undefined})
} }
render() { render() {
const {error} = this.state
return ( return (
<form autoComplete='off' onSubmit={this.handleAddUser}> <form autoComplete='off' onSubmit={this.handleAddUser}>
<Heading>Add User</Heading> <Heading>Add User</Heading>
<Control hasIcons='left'> <Input
<Input label='Search by email'
isColor={error ? 'danger' : ''} error={this.state.error}
onChange={this.handleChangeEmail} name='email'
placeholder='Email' onChange={this.handleChangeEmail}
type='email' placeholder='Email'
value={this.state.email} type='email'
/> value={this.state.email}
<span className='icon is-left'> Icon={FaUser}
<FaUser /> />
</span>
{error && (
<Help isColor='danger'>{error}</Help>
)}
</Control>
<div className='mt-1 text-right'> <div className='mt-1 text-right'>
<Button <Button type='submit'>
isColor='dark' <FaCheck /> Add
type='submit'
>
<FaCheck className='mr-1' />
Add
</Button> </Button>
</div> </div>
</form> </form>
@ -166,7 +153,7 @@ export class TeamUserList extends React.PureComponent<TeamUsersProps> {
) )
})} })}
<PanelBlock isDisplay='block'> <PanelBlock>
<AddUser <AddUser
onAddUser={this.props.onAddUser} onAddUser={this.props.onAddUser}
onSearchUser={this.props.findUserByEmail} onSearchUser={this.props.findUserByEmail}

View File

@ -5,5 +5,5 @@ export function getColor(props: ColorSchemeProps) {
} }
export function getBorder(props: ColorSchemeProps) { export function getBorder(props: ColorSchemeProps) {
return `${props.theme.border.width}px solid ${getColor(props)};` return `${props.theme.border.width} solid ${getColor(props)};`
} }

View File

@ -12,10 +12,11 @@ declare module 'styled-components' {
grey: { grey: {
dark: string dark: string
light: string light: string
lighter: string
} }
border: { border: {
width: 1 width: string
radius: 3 radius: string
} }
} }
} }
@ -31,10 +32,11 @@ export const theme: DefaultTheme = {
grey: { grey: {
dark: 'darkgrey', dark: 'darkgrey',
light: 'lightgrey', light: 'lightgrey',
lighter: 'whitesmoke',
}, },
border: { border: {
width: 1, width: '1px',
radius: 3, radius: '3px',
}, },
} }