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

View File

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

View File

@ -1,20 +1,21 @@
import {Modal as M, ModalBackground, ModalContent, ModalClose} from 'bloomer'
import React from 'react'
export interface ModalProps {
isActive?: boolean
}
export class Modal extends React.PureComponent<ModalProps> {
render() {
return (
<M isActive={this.props.isActive}>
<ModalBackground />
<ModalContent>
{this.props.children}
</ModalContent>
<ModalClose />
</M>
)
}
}
// import {Modal as M, ModalBackground, ModalContent, ModalClose} from 'bloomer'
// import React from 'react'
//
// export interface ModalProps {
// isActive?: boolean
// }
//
// export class Modal extends React.PureComponent<ModalProps> {
// render() {
// return (
// <M isActive={this.props.isActive}>
// <ModalBackground />
// <ModalContent>
// {this.props.children}
// </ModalContent>
// <ModalClose />
// </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 './Heading'
export * from './Help'
export * from './Input'
export * from './Link'
export * from './Modal'
export * from './Panel'
export * from './Redirect'
export * from './ReturnHere'
export * from './TimeAgo'
export * from './Well'
export * from './withHistory'
export * from './WithRouterProps'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,5 +5,5 @@ export function getColor(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: {
dark: string
light: string
lighter: string
}
border: {
width: 1
radius: 3
width: string
radius: string
}
}
}
@ -31,10 +32,11 @@ export const theme: DefaultTheme = {
grey: {
dark: 'darkgrey',
light: 'lightgrey',
lighter: 'whitesmoke',
},
border: {
width: 1,
radius: 3,
width: '1px',
radius: '3px',
},
}