Skip to content
Open
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
1 change: 1 addition & 0 deletions ORGANIZATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Currently, there is no clear, definitive organization to our RoverUI component l
- ListControlDefaultItem
- MultiLineInput
- PasswordInput
- Radio
- Select
- SmallCheckbox(?)
- TextArea
Expand Down
14 changes: 14 additions & 0 deletions example/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const App = () => {
const [tooltipOpen, setTooltipOpen] = useState(false);
const [inputValue, setInputValue] = useState('');
const [inputCheckboxValue, setInputCheckboxValue] = useState(false);
const [inputRadioValue, setInputRadioValue] = useState(false);
const [inputTimeValue, setInputTimeValue] = useState('');
const [isModalOpen, setIsModalOpen] = useState(false);

Expand Down Expand Up @@ -410,6 +411,19 @@ const App = () => {
{JSON.stringify(inputCheckboxValue)}
</Section>

<Section title="Input.Radio">
<h1>Input.Radio</h1>
<Input
onChange={(e) =>
console.log(e.target.checked) ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't gonna hurt anything, but you don't need to leave a console log in here. If I did it for checkbox, it was a mistake.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

going to remove this from checkbox as well

setInputRadioValue(e.target.checked)
}
checked={inputRadioValue}
type="radio"
/>{' '}
{JSON.stringify(inputRadioValue)}
</Section>

<Section title="InputTime">
<InputTime
value={inputTimeValue}
Expand Down
9 changes: 9 additions & 0 deletions src/components/Input/Radio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# \<Radio\>

### Radio component is a thin wrapper around an HTML input[type="radio"] element

The basic `<Radio />` delegates all normal input behavior to the HTML element, and adds custom styles.

It also provides 1 custom prop:

- _fauxDisabled_: Applies the same style as disabled, but, unlike the real thing, doesn't stop propagation of events. Useful for adding tooltips or other helpful behavior when a user tries to interact with a disabled field. Because it doesn't stop click or change events, the consumer is responsible for making faux-disabled fields read-only.
82 changes: 82 additions & 0 deletions src/components/Input/Radio/Radio.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
.Radio {
position: relative;
display: inline-block;
height: var(--rvr-radio-size);
width: var(--rvr-radio-size);
}

.svg {
transition: var(--rvr-transition-duration-fast) var(--rvr-linear) box-shadow;
display: block;
height: 100%;
width: 100%;
}

.input {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
opacity: 0;
z-index: 1;
display: block;
}
.outline {
fill: var(--rvr-color-font);
transition: var(--rvr-transition-duration-fast) var(--rvr-linear) fill;
border-width: medium;
}

.inner {
fill: none;
}
.Radio.checked .outline {
fill: var(--rvr-color-primary);
}

input:checked + .svg .outline {
fill: var(--rvr-color-primary);
}

input:hover + .svg .outline {
fill: var(--rvr-color-primary-hover);
}

input:focus + .svg {
outline: 0 transparent none;
box-shadow:
0px 0px 0px 2px var(--rvr-white),
0px 0px 1px 3px var(--rvr-color-primary-hover),
0px 0px 8px 2px var(--rvr-color-primary-hover);
}

input:disabled + .svg .outline {
fill: var(--rvr-color-disabled);
}

input:checked + .svg .inner {
fill: var(--rvr-color-primary);
}

.Radio.checked .check,
.indeterminate + .svg .dash {
display: initial;
fill: var(--rvr-white);
}

.Radio.disabled .svg {
box-shadow: none;
}

input:disabled + .svg .outline,
.Radio.disabled .svg .outline {
fill: var(--rvr-color-disabled);
box-shadow: none;
}

input:disabled + .svg .inner,
.Radio.disabled .svg .inner {
fill: var(--rvr-color-disabled);
box-shadow: none;
}
46 changes: 46 additions & 0 deletions src/components/Input/Radio/Radio.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from 'react';

import classNames from 'classnames';

import type { InputProps } from '../Input';

import RadioSvg from './RadioSvg';
import styles from './Radio.module.css';

interface RadioWithRefProps extends InputProps {
fauxDisabled?: boolean;
forwardedRef?: React.Ref<HTMLInputElement>;
}

const RadioWithRef: React.FC<RadioWithRefProps> = ({
checked = false,
className = '',
fauxDisabled = false,
forwardedRef: ref,
...passedProps
}) => {
return (
<div
className={classNames(styles.Radio, className, {
[styles.checked]: checked,
[styles.disabled]: fauxDisabled,
})}
>
<input
checked={checked}
className={styles.input}
ref={ref || undefined}
tabIndex={fauxDisabled ? -1 : undefined}
type="radio"
{...passedProps}
/>
<RadioSvg aria-hidden className={styles.svg} />
</div>
);
};

const Radio = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => (
<RadioWithRef {...props} forwardedRef={ref || undefined} />
));

export default Radio;
27 changes: 27 additions & 0 deletions src/components/Input/Radio/RadioSvg.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';

import styles from './Radio.module.css';

const Svg = ({ title = 'Radio', ...passedProps }) => (
<svg
width="20px"
height="20px"
viewBox="0 0 20 20"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
{...passedProps}
>
<title>{title}</title>
<g id="Radio" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
<circle className={styles.outer} id="outer" cx="10" cy="10" r="10" />
<path
className={styles.outline}
d="M10,20 C4.4771525,20 0,15.5228475 0,10 C0,4.4771525 4.4771525,0 10,0 C15.5228475,0 20,4.4771525 20,10 C20,15.5228475 15.5228475,20 10,20 Z M10,18 C14.418278,18 18,14.418278 18,10 C18,5.581722 14.418278,2 10,2 C5.581722,2 2,5.581722 2,10 C2,14.418278 5.581722,18 10,18 Z"
id="outline"
/>
<circle className={styles.inner} id="inner" cx="10" cy="10" r="5" />
</g>
</svg>
);

export default Svg;
1 change: 1 addition & 0 deletions src/components/Input/Radio/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Radio';
187 changes: 187 additions & 0 deletions src/components/Input/Radio/story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import React, { useState } from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { boolean, text } from '@storybook/addon-knobs';

import Radio from '.';
import Readme from './README.md';

import { InteractiveInput, Wrap } from '../../../stories/storybook-helpers';

storiesOf('Planets/Input/Radio', module)
.addParameters({
readme: {
sidebar: Readme,
},
})
.add(
'Overview',
() => (
<Wrap>
{/*
Eslint doesn't recognise that the `InteractiveInput` wrappers are
putting IDs on input elements, but they are.
*/}
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
<label className="text-xl inline-block mb-2">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might be able to remove the eslint-disable by adding htmlFor="SampleRadio", since you're not using the InteractiveInput wrapper anymore.

{`<Radio />`}
<p className="mt-6">
<small>
<em>
Radio is a bare input. You&apos;ll have to provide your own
label. &ldquo;Control&rdquo; components with integrated labels
and validation messages are on the roadmap.
</em>
</small>
</p>
<div>
<Radio
checked={boolean('checked', false)}
className={text('className (HTML)', 'w-full')}
fauxDisabled={boolean('fauxDisabled', false)}
id="SampleRadio"
onChange={action('onChange (HTML)')}
/>
</div>
</label>
</Wrap>
),
{
info: {
inline: true,
source: true,
},
}
)
.add(
'Examples',
() => (
<Wrap>
<h2>Radio</h2>
<p className="mt-6">
<small>
<em>
Radio is a bare input - the labels are provided for documentation
purposes only. &ldquo;Control&rdquo; components with integrated
labels and validation messages are on the roadmap.
</em>
</small>
</p>
<p className="mt-6">
<small>
This component is identical to {'`<Input type="radio" />`'}.
</small>
</p>
<p className="mt-6">
<small>
These examples use an `InteractiveInput` wrapper with an
`InputRenderer` prop. In actual implementations, just use a `Radio`
component, `onChange`, and `checked` props instead.
</small>
</p>
<div>
{[
[],
['checked'],
['disabled'],
['fauxDisabled'],
['checked', 'disabled'],
].map((attributes) => {
const attributesId = ['atts', ...attributes].join('-');
const attributesLabel = attributes.join(' and ') || 'default';

const attributesProps = attributes.reduce(
(acc, attr) => ({ ...acc, [attr]: true }),
{}
);

return (
<div className="my-6" key={attributesId}>
<label htmlFor={attributesId}>
{attributesLabel}{' '}
<InteractiveInput
InputRenderer={Radio}
id={attributesId}
onChange={action(`onChange ${attributesLabel}`)}
{...attributesProps}
/>
</label>
</div>
);
})}
</div>
</Wrap>
),
{
info: {
inline: false,
source: true,
},
}
)
.add(
'Radio Group Example',
() => {
const [brother, setBrother] = useState('');
return (
<Wrap>
<form>
<h2>Example</h2>
<ul style={{ listStyleType: 'none' }}>
<li>
<label htmlFor="huey">
<Radio
checked={brother === 'huey'}
name="brother"
id="huey"
value="huey"
onChange={(e) => {
action('onChange (HTML)')(e);
setBrother('huey');
}}
/>{' '}
Huey
</label>
</li>
<li>
<label htmlFor="dewey">
<Radio
checked={brother === 'dewey'}
name="brother"
id="dewey"
value="dewey"
onChange={(e) => {
action('onChange (HTML)')(e);
setBrother('dewey');
}}
/>{' '}
Dewey
</label>
</li>
<li>
<label htmlFor="louie">
<Radio
checked={brother === 'louie'}
name="brother"
id="louie"
value="louie"
onChange={(e) => {
action('onChange (HTML)')(e);
setBrother('louie');
}}
/>{' '}
Louie
</label>
</li>
</ul>
</form>
</Wrap>
);
},
{
info: {
inline: false,
source: true,
},
}
);
1 change: 1 addition & 0 deletions src/components/Input/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default } from './Input';
export { default as Checkbox } from './Checkbox';
export { default as Radio } from './Radio';
export type { InputProps } from './Input';
Loading