Skip to content

Commit

Permalink
feat(radio-button): wip add radio button on junipero v3
Browse files Browse the repository at this point in the history
  • Loading branch information
maximedasilva committed Oct 19, 2022
1 parent 347fc95 commit b059d7e
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 0 deletions.
110 changes: 110 additions & 0 deletions packages/react/lib/RadioField/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { forwardRef, useReducer, useRef } from 'react';
import PropTypes from 'prop-types';
import { classNames, mockState } from '@junipero/core';

const RadioField = forwardRef(({
className,
disabled = false,
id,
name,
options = [],
value,
parseValue = val => val.value ?? val,
parseTitle = val => val?.title ?? val?.toString?.(),
parseDescription = val => val?.description || '',
onChange = () => {},
...rest
}, ref) => {
const inputRefs = useRef([]);
const innerRefs = useRef([]);
const wrapperRef = useRef();
const [state, dispatch] = useReducer(mockState, {
focused: null,
dirty: false,
value,
valid: false,
});

const isChecked = option =>
state.value === option || state.value === parseValue(option);

const onChange_ = option => {
dispatch({ value: option, valid: true, dirty: true });
onChange({ value: parseValue(option), valid: true });
};

const onKeyDown = (option, e) => {
if (
state.value !== option &&
state.value !== parseValue(option) &&
(e.key === 'Enter' || e.key === ' ')
) {
onChange_(option);
}

return true;
};

return (
<div
{ ...rest }
id={id}
className={classNames(
'junipero',
'radio-field',
{ disabled, dirty: state.dirty },
className,
)}
ref={wrapperRef}
>
{ options.map((option, index) => (
<label
key={index}
ref={el => { innerRefs.current[index] = el; }}
className={classNames(
{
checked: isChecked(option),
disabled: disabled || option.disabled,
},
)}
onKeyDown={onKeyDown.bind(null, option)}
tabIndex={disabled ? -1 : index + 1}
>
<input
id={option.id || option.value}
name={name}
ref={el => { inputRefs.current[index] = el; }}
type="radio"
value={parseValue(option)}
checked={isChecked(option)}
onChange={onChange_.bind(null, option)}
tabIndex={-1}
/>
<div className="inner" />
<div className="label">
<div className="title">{ parseTitle(option, true) }</div>

{ option.description && (
<div className="description">{ parseDescription(option) }</div>
) }
</div>
</label>
))}
</div>
);
});

RadioField.propTypes = {
disabled: PropTypes.bool,
className: PropTypes.string,
onChange: PropTypes.func,
options: PropTypes.array,
parseValue: PropTypes.func,
parseDescription: PropTypes.func,
parseTitle: PropTypes.func,
value: PropTypes.any,
id: PropTypes.string,
name: PropTypes.string,
};

export default RadioField;
25 changes: 25 additions & 0 deletions packages/react/lib/RadioField/index.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { action } from '@storybook/addon-actions';

import RadioField from '.';

export default { title: 'react/RadioField' };
const basicOptions = [
{ title: 'Apple', value: 'Apple' },
{ title: 'Pear', value: 'Pear' },
{ title: 'Orange', value: 'Orange' },
];

const basicOptionsOneDisabled = [
{ title: 'Apple', value: 'Apple' },
{ title: 'Pear', value: 'Pear' },
{ title: 'Orange', value: 'Orange', disabled: true },
];

const withDescriptions = [
{ title: 'Apple', value: 'Apple', description: 'This is a description' },
{ title: 'Pear', value: 'Pear', description: 'This is a description' },
{ title: 'Orange', value: 'Orange', description: 'This is a description' },
];
export const basic = () => (
<RadioField options={basicOptions} onChange={action('change')} />
);
Empty file.
1 change: 1 addition & 0 deletions packages/theme/lib/RadioField.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@use "colors" as colors

0 comments on commit b059d7e

Please sign in to comment.