Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Radiobutton #911

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/assets/stylesheets/base/_colors.scss
Expand Up @@ -26,3 +26,5 @@ $transparent: rgba(255, 255, 255, 0.1);
$notification-backdrop: rgba(0, 0, 0, 0.7);
$tag-color: rgba(109, 8, 57, 0.8);
$slider-focus-color: #54042a;
$grey: grey;
$light-grey:#D3D3D3;
32 changes: 32 additions & 0 deletions client/app/components/RadioButton/RadioButton.scss
@@ -0,0 +1,32 @@
@import "../../styles/_legacy.scss";
@import "../../styles/_base.scss";

.radioButtonWrapper {
display: flex;
flex-direction: row;
margin-bottom: 10px;
align-items: center;
}

.radioButtonLabel {
color: grey;
opacity: 50%;
font-family: $font-family;
font-size: $font18;
margin-left: 10px;
}

.radioButton {
border-radius: 100%;
width: 30px;
height: 30px;
border: 4px solid $grey;
}

.radioButtonUnchecked {
background-color: $light-grey;
}

.radioButtonChecked {
background-color: $grey;
}
78 changes: 78 additions & 0 deletions client/app/components/RadioButton/RadioButtonGroup.jsx
@@ -0,0 +1,78 @@
// @flow
import React from 'react';

export interface Props {
children: any;
}

export interface State {
options: string[];
children: any;
}

export class RadioButtonGroup extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
const { children } = this.props;
const options = [];
children.forEach((child: any) => {
const { id } = child.props;
options.push(id);
});
this.state = { options, children };
}

componentDidMount() {
this.setupChildren();
}

uncheckAllAndCheck = (id: string) => {
const { children } = this.state;
const newChildren = children.map((child: any) =>
React.cloneElement(child, {
updateAll: this.updateAll,
key: child.props.id,
checked: child.props.id === id,
}),
);
this.setState({ children: newChildren });
};

updateAll = (id: string) => {
this.uncheckAllAndCheck(id);
};

setupChildren = () => {
const { children } = this.state;
let newChildren;
if (Array.isArray(children)) {
const numChecked = children.filter((child: any) => child.props.checked)
.length;
if (numChecked > 1) {
newChildren = children.map((child: any) =>
React.cloneElement(child, {
updateAll: this.updateAll,
key: child.props.id,
checked: false,
}),
);
} else {
newChildren = children.map((child: any) =>
React.cloneElement(child, {
updateAll: this.updateAll,
key: child.props.id,
}),
);
}
} else {
newChildren = React.cloneElement((children: any), {
updateAll: this.updateAll,
});
}
this.setState({ children: newChildren });
};
render() {
const { children } = this.state;
return <div className="RadioButtonGroup">{children}</div>;
}
}
41 changes: 41 additions & 0 deletions client/app/components/RadioButton/__tests__/RadioButton.spec.jsx
@@ -0,0 +1,41 @@
// @flow
import { mount } from 'enzyme/build';
import React from 'react';
import { RadioButton } from '..';

const label = 'Hello';
const id = 'some-id';

describe('RadioButton', () => {
describe('when props.checked is false', () => {
it('renders radio button correctly', () => {
const wrapper = mount(<RadioButton label={label} id={id} />);
expect(wrapper.state('checked')).toEqual(false);
expect(wrapper.find('.radioButtonLabel').text()).toEqual(label);
});

it('toggles correctly', () => {
const wrapper = mount(<RadioButton label={label} id={id} />);
const radioButton = wrapper.find('.radioButton');
radioButton.simulate('click');
expect(wrapper.state('checked')).toEqual(true);
radioButton.simulate('click');
expect(wrapper.state('checked')).toEqual(true);
});
});

describe('when props.checked is true', () => {
it('renders RadioButton correctly', () => {
const wrapper = mount(<RadioButton label={label} id={id} checked />);
expect(wrapper.state('checked')).toEqual(true);
expect(wrapper.find('.radioButtonLabel').text()).toEqual(label);
});

it('toggles correctly', () => {
const wrapper = mount(<RadioButton label={label} id={id} checked />);
const radioButton = wrapper.find('.radioButton');
radioButton.simulate('click');
expect(wrapper.state('checked')).toEqual(true);
});
});
});
56 changes: 56 additions & 0 deletions client/app/components/RadioButton/index.jsx
@@ -0,0 +1,56 @@
// @flow
import React from 'react';
import css from './RadioButton.scss';

export interface Props {
id: string;
label: string;
checked?: boolean;
updateAll?: (id: string) => void;
}

export interface State {
checked: boolean;
}

export class RadioButton extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { checked: !!this.props.checked };
}

shouldComponentUpdate(nextProps: Props) {
// TODO: not working as expected
this.setState({ checked: nextProps.checked });
return true;
}

toggle = (id: string) => {
const { updateAll } = this.props;
this.setState({ checked: true });
if (updateAll) {
updateAll(id);
}
};

render() {
const { id, label } = this.props;
const { checked } = this.state;
const radioButtonClassNames = `radioButton ${css.radioButton} ${
checked ? css.radioButtonChecked : css.radioButtonUnchecked
}`;
const labelClassNames = `radioButtonLabel ${css.radioButtonLabel}`;
return (
<div className={css.radioButtonWrapper} id={id}>
<div
role="presentation"
className={radioButtonClassNames}
onClick={() => this.toggle(id)}
/>
<label htmlFor={id} className={labelClassNames}>
{label}
</label>
</div>
);
}
}
16 changes: 16 additions & 0 deletions client/app/stories/RadioButton.stories.jsx
@@ -0,0 +1,16 @@
import React from 'react';
import { storiesOf } from '@storybook/react';

import { RadioButton } from '../components/RadioButton/index';
import { RadioButtonGroup } from '../components/RadioButton/RadioButtonGroup';

storiesOf('RadioButton', module).add('RadioButton', () => (
<div>
<h2>One RadioButtonGroup</h2>
<RadioButtonGroup>
<RadioButton id="radioButton-one" label="Option 1" checked />
<RadioButton id="radioButton-two" label="Option 2" checked />
<RadioButton id="radioButton-three" label="Option 3" />
</RadioButtonGroup>
</div>
));