Skip to content
This repository has been archived by the owner on Mar 31, 2021. It is now read-only.

Commit

Permalink
Add Radio component
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Oct 26, 2018
1 parent bd26cdc commit ceccb56
Show file tree
Hide file tree
Showing 12 changed files with 1,723 additions and 62 deletions.
4 changes: 2 additions & 2 deletions src/Checkbox/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import React from 'react';

import Text from '../Text';
import _Checkbox, { CheckboxIcon, HiddenInput } from './styled';
import _Checkbox, { CheckboxIcon, HiddenCheckbox } from './styled';

type Props = {
/** An accessible label for the checkbox */
Expand Down Expand Up @@ -59,7 +59,7 @@ const Checkbox = ({
aria-required={isRequired}
{...props}
>
<HiddenInput
<HiddenCheckbox
autoFocus={autoFocus}
checked={checked}
defaultValue={defaultValue}
Expand Down
80 changes: 20 additions & 60 deletions src/Checkbox/styled.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import styled, { css } from 'reakit/styled';
import { palette, theme } from 'styled-tools';
import { tint } from 'polished';
import Label from 'reakit/Label';
import Input from 'reakit/Input';

import { Box } from '../primitives';
import HiddenInput from '../_utils/HiddenInput';

export const CheckboxIcon = styled(Box)`
border: 1px solid #bdbdbd;
Expand All @@ -28,66 +28,26 @@ export const CheckboxIcon = styled(Box)`
${theme('Checkbox.icon.base')};
}
`;
export const HiddenInput = styled(Input)`
clip: rect(0, 0, 0, 0);
height: 1px;
margin: -1px;
overflow: hidden;
position: absolute;
width: 1px;

&[disabled] + ${CheckboxIcon} {
background-color: ${palette('whiteDarker')};
box-shadow: unset;
& {
${theme('Checkbox.icon.disabled')};
}
}
&:focus + ${CheckboxIcon} {
border-color: ${props => tint(0.3, palette('primary')(props))};
box-shadow: ${props => tint(0.3, palette('primary')(props))} 0px 0px 0px 1px !important;
& {
${theme('Checkbox.icon.focus')};
}
}
&:checked + ${CheckboxIcon} {
border-color: ${props => tint(0.3, palette('primary')(props))};
& {
${props =>
props.state &&
css`
box-shadow: ${props => tint(0.3, palette('primary')(props))} 0px 0px 0px 1px !important;
`};
}
&::before {
background-clip: padding-box;
border: 0.1rem solid ${props => tint(0.3, palette('primary')(props))};
border-left-width: 0;
border-top-width: 0;
content: '';
height: 10px;
left: 50%;
margin-left: -3px;
margin-top: -6px;
position: absolute;
top: 50%;
transform: rotate(45deg);
width: 6px;
& {
${theme('Checkbox.icon.tick')};
}
}
& {
${theme('Checkbox.icon.checked')};
}
}
`;
export const HiddenCheckbox = HiddenInput({
Icon: CheckboxIcon,
tickCss: css`
background-clip: padding-box;
border: 0.1rem solid ${props => tint(0.3, palette('primary')(props))};
border-left-width: 0;
border-top-width: 0;
content: '';
height: 10px;
left: 50%;
margin-left: -3px;
margin-top: -6px;
position: absolute;
top: 50%;
transform: rotate(45deg);
width: 6px;
`,
themePrefix: 'Checkbox'
});

export default styled(Label)`
display: flex;
Expand Down
100 changes: 100 additions & 0 deletions src/Radio/Radio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// @flow
import React from 'react';

import Text from '../Text';
import _Radio, { RadioIcon, HiddenRadio } from './styled';

type Props = {
/** An accessible label for the radio */
a11yLabel?: string,
/** Automatically focus on the radio */
autoFocus?: boolean,
checked?: boolean | string,
className?: string,
/** Default value of the radio */
defaultValue?: string,
/** Disables the radio */
disabled?: boolean,
/** ID for the radio */
id?: string,
/** Makes the radio required and sets aria-invalid to true */
isRequired?: boolean,
/** radio label */
label: string,
name?: string,
/** State of the radio. Can be any color in the palette. */
state?: string,
/** Initial value of the radio */
value?: string,
/** Function to invoke when focus is lost */
onBlur?: Function,
/** Function to invoke when radio has changed */
onChange?: Function,
/** Function to invoke when radio is focused */
onFocus?: Function
};

const Radio = ({
a11yLabel,
autoFocus,
checked,
className,
defaultValue,
disabled,
id,
isRequired,
label,
onBlur,
onChange,
onFocus,
name,
state,
value,
...props
}: Props) => (
<_Radio
aria-describedby="label"
aria-invalid={state === 'danger'}
aria-label={a11yLabel}
aria-required={isRequired}
{...props}
>
<HiddenRadio
autoFocus={autoFocus}
checked={checked}
defaultValue={defaultValue}
disabled={disabled}
id={id}
onBlur={onBlur}
onChange={onChange}
onFocus={onFocus}
name={name}
state={state}
type="radio"
value={value}
/>
<RadioIcon state={state} />
<Text id="label" marginLeft="xxsmall">
{label}
</Text>
</_Radio>
);

Radio.defaultProps = {
a11yLabel: null,
autoFocus: false,
checked: undefined,
className: null,
defaultValue: undefined,
disabled: false,
id: null,
isRequired: false,
onBlur: null,
onChange: null,
onFocus: null,
name: null,
state: null,
value: undefined
};

export default Radio;
62 changes: 62 additions & 0 deletions src/Radio/Radio.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
name: Radio
route: /form/radio
menu: Form
---

import { Playground, PropsTable } from 'docz';
import { Box } from '../primitives/index';
import Radio from './index';

# Radio

## Import

`import { Radio } from 'fannypack'`

## Basic Usage

<Playground>
<Radio name="weather" label="Sunny" />
<Radio name="weather" label="Windy" />
</Playground>

## Disabled

Make the Radio disabled with the `disabled` prop.

<Playground>
<Radio disabled name="weather" label="Sunny" />
</Playground>

## States

A Radio can use different states (as per palette) such as `danger`, `success` and `warning`.

<Playground>
<Radio state="danger" name="weather" label="Sunny" />
<Radio state="success" name="weather" label="Rainy" />
<Radio state="warning" name="weather" label="Windy" />
<Radio state="primary" name="weather" label="Overcast" />
</Playground>

## Props

<PropsTable of={Radio} />

## Theming

### Schema

```jsx
{
base: string | Object,
icon: {
base: string | Object,
checked: string | Object
disabled: string | Object,
focus: string | Object
tick: string | Object
}
}
```
23 changes: 23 additions & 0 deletions src/Radio/__tests__/Radio.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import render from '../../_utils/tests/render';
import Radio from '../Radio';
import 'jest-styled-components';

it('renders correctly for a basic radio', () => {
const { container } = render(<Radio />);
expect(container.firstChild).toMatchSnapshot();
});

it('renders correctly for a disabled radio', () => {
const { container } = render(<Radio disabled />);
expect(container.firstChild).toMatchSnapshot();
});

describe('states', () => {
['danger', 'success', 'warning', 'primary'].forEach(state => {
it(`renders correctly for an radio with state ${state}`, () => {
const { container } = render(<Radio state={state} />);
expect(container.firstChild).toMatchSnapshot();
});
});
});
Loading

0 comments on commit ceccb56

Please sign in to comment.