6 changed files with 343 additions and 0 deletions
@ -0,0 +1,88 @@ |
|||
import React from 'react' |
|||
import PropTypes from 'prop-types' |
|||
import { Radio as InformedRadio } from 'informed' |
|||
import styled from 'styled-components' |
|||
import { Label, Text } from 'components/UI' |
|||
import { Box } from 'rebass' |
|||
|
|||
const Wrapper = styled(Box)` |
|||
/* The container */ |
|||
.container { |
|||
display: block; |
|||
position: relative; |
|||
padding-left: 30px; |
|||
cursor: pointer; |
|||
user-select: none; |
|||
} |
|||
|
|||
/* Hide the browser's default radio button */ |
|||
.container input { |
|||
position: absolute; |
|||
opacity: 0; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
/* Create a custom radio button */ |
|||
.selection { |
|||
position: absolute; |
|||
top: 1px; |
|||
left: 0; |
|||
height: 16px; |
|||
width: 16px; |
|||
border: 1px solid ${props => props.theme.colors.gray}; |
|||
border-radius: 50%; |
|||
} |
|||
|
|||
/* On mouse-over, add an orange border color */ |
|||
.container:hover input ~ .selection { |
|||
border: 1px solid ${props => props.theme.colors.lightningOrange}; |
|||
} |
|||
|
|||
/* When the radio button is checked, make the border orange */ |
|||
.container input:checked ~ .selection { |
|||
border: 1px solid ${props => props.theme.colors.lightningOrange}; |
|||
} |
|||
|
|||
/* Create the indicator (the dot/circle - hidden when not checked) */ |
|||
.selection:after { |
|||
content: ''; |
|||
position: absolute; |
|||
display: none; |
|||
} |
|||
|
|||
/* Show the indicator (dot/circle) when checked */ |
|||
.container input:checked ~ .selection:after { |
|||
display: block; |
|||
} |
|||
|
|||
/* Style the indicator (dot/circle) */ |
|||
.container .selection:after { |
|||
top: 3px; |
|||
left: 3px; |
|||
width: 8px; |
|||
height: 8px; |
|||
border-radius: 50%; |
|||
background: ${props => props.theme.colors.lightningOrange}; |
|||
} |
|||
` |
|||
const Radio = ({ value, label, description, fontWeight, onChange, onBlur, ...rest }) => ( |
|||
<Wrapper> |
|||
<Label htmlFor={value} className="container" fontWeight="light" mb={3} {...rest}> |
|||
<Text>{label}</Text> |
|||
<InformedRadio value={value} id={value} onChange={onChange} onBlur={onBlur} /> |
|||
<span className="selection" /> |
|||
{description && ( |
|||
<Text mt={2} color="gray"> |
|||
{description} |
|||
</Text> |
|||
)} |
|||
</Label> |
|||
</Wrapper> |
|||
) |
|||
Radio.propTypes = { |
|||
value: PropTypes.string.isRequired, |
|||
label: PropTypes.node, |
|||
description: PropTypes.node |
|||
} |
|||
|
|||
export default Radio |
@ -0,0 +1,3 @@ |
|||
import { RadioGroup } from 'informed' |
|||
|
|||
export default RadioGroup |
@ -0,0 +1,24 @@ |
|||
import React from 'react' |
|||
import renderer from 'react-test-renderer' |
|||
import { dark } from 'themes' |
|||
import { ThemeProvider } from 'styled-components' |
|||
import { Form, Radio, RadioGroup } from 'components/UI' |
|||
|
|||
describe('component.UI.Radio', () => { |
|||
it('should render correctly', () => { |
|||
const tree = renderer |
|||
.create( |
|||
<ThemeProvider theme={dark}> |
|||
<Form> |
|||
<RadioGroup field="radio"> |
|||
<Radio value="item1" label="Item 1" description="Radio buttons" /> |
|||
<Radio value="item2" label="Item 2" description="can have an optional title" /> |
|||
<Radio value="item3" label="Item 3" description="and description" /> |
|||
</RadioGroup> |
|||
</Form> |
|||
</ThemeProvider> |
|||
) |
|||
.toJSON() |
|||
expect(tree).toMatchSnapshot() |
|||
}) |
|||
}) |
@ -0,0 +1,207 @@ |
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP |
|||
|
|||
exports[`component.UI.Radio should render correctly 1`] = ` |
|||
.c2 { |
|||
font-size: 13px; |
|||
color: #ffffff; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
.c3 { |
|||
margin-top: 8px; |
|||
font-size: 13px; |
|||
color: #959595; |
|||
line-height: 1.4; |
|||
} |
|||
|
|||
.c1 { |
|||
margin-bottom: 16px; |
|||
width: 100%; |
|||
font-size: 13px; |
|||
color: #ffffff; |
|||
color: #ffffff; |
|||
font-weight: 300; |
|||
display: block; |
|||
} |
|||
|
|||
.c0 .container { |
|||
display: block; |
|||
position: relative; |
|||
padding-left: 30px; |
|||
cursor: pointer; |
|||
-webkit-user-select: none; |
|||
-moz-user-select: none; |
|||
-ms-user-select: none; |
|||
user-select: none; |
|||
} |
|||
|
|||
.c0 .container input { |
|||
position: absolute; |
|||
opacity: 0; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.c0 .selection { |
|||
position: absolute; |
|||
top: 1px; |
|||
left: 0; |
|||
height: 16px; |
|||
width: 16px; |
|||
border: 1px solid #959595; |
|||
border-radius: 50%; |
|||
} |
|||
|
|||
.c0 .container:hover input ~ .selection { |
|||
border: 1px solid #fd9800; |
|||
} |
|||
|
|||
.c0 .container input:checked ~ .selection { |
|||
border: 1px solid #fd9800; |
|||
} |
|||
|
|||
.c0 .selection:after { |
|||
content: ''; |
|||
position: absolute; |
|||
display: none; |
|||
} |
|||
|
|||
.c0 .container input:checked ~ .selection:after { |
|||
display: block; |
|||
} |
|||
|
|||
.c0 .container .selection:after { |
|||
top: 3px; |
|||
left: 3px; |
|||
width: 8px; |
|||
height: 8px; |
|||
border-radius: 50%; |
|||
background: #fd9800; |
|||
} |
|||
|
|||
<form |
|||
className="" |
|||
onReset={[Function]} |
|||
onSubmit={[Function]} |
|||
> |
|||
<div |
|||
className="c0" |
|||
> |
|||
<label |
|||
className="container c1" |
|||
color="primaryText" |
|||
display="block" |
|||
fontSize="m" |
|||
fontWeight="light" |
|||
htmlFor="item1" |
|||
opacity={null} |
|||
width={1} |
|||
> |
|||
<div |
|||
className="c2" |
|||
color="primaryText" |
|||
fontSize="m" |
|||
> |
|||
Item 1 |
|||
</div> |
|||
<input |
|||
checked={false} |
|||
id="item1" |
|||
onBlur={[Function]} |
|||
onChange={[Function]} |
|||
type="radio" |
|||
value="item1" |
|||
/> |
|||
<span |
|||
className="selection" |
|||
/> |
|||
<div |
|||
className="c3" |
|||
color="gray" |
|||
fontSize="m" |
|||
> |
|||
Radio buttons |
|||
</div> |
|||
</label> |
|||
</div> |
|||
<div |
|||
className="c0" |
|||
> |
|||
<label |
|||
className="container c1" |
|||
color="primaryText" |
|||
display="block" |
|||
fontSize="m" |
|||
fontWeight="light" |
|||
htmlFor="item2" |
|||
opacity={null} |
|||
width={1} |
|||
> |
|||
<div |
|||
className="c2" |
|||
color="primaryText" |
|||
fontSize="m" |
|||
> |
|||
Item 2 |
|||
</div> |
|||
<input |
|||
checked={false} |
|||
id="item2" |
|||
onBlur={[Function]} |
|||
onChange={[Function]} |
|||
type="radio" |
|||
value="item2" |
|||
/> |
|||
<span |
|||
className="selection" |
|||
/> |
|||
<div |
|||
className="c3" |
|||
color="gray" |
|||
fontSize="m" |
|||
> |
|||
can have an optional title |
|||
</div> |
|||
</label> |
|||
</div> |
|||
<div |
|||
className="c0" |
|||
> |
|||
<label |
|||
className="container c1" |
|||
color="primaryText" |
|||
display="block" |
|||
fontSize="m" |
|||
fontWeight="light" |
|||
htmlFor="item3" |
|||
opacity={null} |
|||
width={1} |
|||
> |
|||
<div |
|||
className="c2" |
|||
color="primaryText" |
|||
fontSize="m" |
|||
> |
|||
Item 3 |
|||
</div> |
|||
<input |
|||
checked={false} |
|||
id="item3" |
|||
onBlur={[Function]} |
|||
onChange={[Function]} |
|||
type="radio" |
|||
value="item3" |
|||
/> |
|||
<span |
|||
className="selection" |
|||
/> |
|||
<div |
|||
className="c3" |
|||
color="gray" |
|||
fontSize="m" |
|||
> |
|||
and description |
|||
</div> |
|||
</label> |
|||
</div> |
|||
</form> |
|||
`; |
Loading…
Reference in new issue