Skip to content

Commit

Permalink
Merge cad4b83 into 65ef0a5
Browse files Browse the repository at this point in the history
  • Loading branch information
mannycarrera4 committed Feb 6, 2020
2 parents 65ef0a5 + cad4b83 commit 5824333
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 221 deletions.
45 changes: 45 additions & 0 deletions cypress/integration/Radio.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import * as h from '../helpers';

const getRadio = () => {
return cy.get(`[type="radio"]`);
};

describe('Radio', () => {
before(() => {
h.stories.visit();
});

context(`given the Default story is rendered`, () => {
beforeEach(() => {
h.stories.load('Components|Inputs/Radio/React/Left Label/Radio', 'Default');
});

it('should pass accessibility checks', () => {
cy.checkA11y();
});

context('when clicked', () => {
beforeEach(() => {
cy.findByLabelText('E-mail').click();
});

it('should be checked', () => {
getRadio().should('be.checked');
});
});
});

context(`given the 'Disabled' story is rendered`, () => {
beforeEach(() => {
h.stories.load('Components|Inputs/Radio/React/Left Label/Radio', 'Disabled');
});

it('should pass accessibility checks', () => {
cy.checkA11y();
});

it('should be disabled', () => {
getRadio().should('be.disabled');
});
});
});
29 changes: 29 additions & 0 deletions cypress/integration/RadioGroup.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as h from '../helpers';

describe('Radio Group', () => {
before(() => {
h.stories.visit();
});
['Default', 'Alert', 'Error with Grow'].forEach(story => {
context(`given the '${story}' story is rendered`, () => {
beforeEach(() => {
h.stories.load('Components|Inputs/Radio/React/Top Label/Radio Group', story);
});

it('should pass accessibility checks', () => {
cy.checkA11y();
});

context('when clicking one radio and then selecting another radio', () => {
beforeEach(() => {
cy.findByLabelText('E-mail').click();
cy.findByLabelText('Mail').click();
});

it('should one have one radio selected', () => {
cy.findByLabelText('Mail').should('be.checked');
});
});
});
});
});
93 changes: 90 additions & 3 deletions modules/form-field/react/stories/stories_Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
import * as React from 'react';
import {storiesOf} from '@storybook/react';
import withReadme from 'storybook-readme/with-readme';
import {ControlledComponentWrapper} from '../../../../utils/storybook';
import {StaticStates} from '@workday/canvas-kit-labs-react-core/lib/StaticStates';
import {
ControlledComponentWrapper,
ComponentStatesTable,
permutateProps,
} from '../../../../utils/storybook';

import {Radio, RadioGroup} from '../../../radio/react';
import FormField from '../index';
Expand Down Expand Up @@ -72,7 +77,7 @@ storiesOf('Components|Inputs/Radio/React/Top Label/Radio', module)
<ControlledComponentWrapper
controlledProp={ControlledComponentWrapper.ControlledProp.Checked}
>
<Radio id="1" value="email" label="E-mail" />
<Radio value="email" label="E-mail" />
</ControlledComponentWrapper>
</FormField>
));
Expand Down Expand Up @@ -140,7 +145,89 @@ storiesOf('Components|Inputs/Radio/React/Left Label/Radio', module)
<ControlledComponentWrapper
controlledProp={ControlledComponentWrapper.ControlledProp.Checked}
>
<Radio id="1" value="email" label="E-mail" />
<Radio value="email" label="E-mail" />
</ControlledComponentWrapper>
</FormField>
))
.add('Disabled', () => (
<FormField label="Label" labelPosition={FormField.LabelPosition.Left}>
<ControlledComponentWrapper
controlledProp={ControlledComponentWrapper.ControlledProp.Checked}
>
<Radio disabled={true} value="email" label="E-mail" />
</ControlledComponentWrapper>
</FormField>
));

storiesOf('Components|Inputs/Radio/React/Visual', module)
.addParameters({component: Radio})
.addDecorator(withReadme(README))
.add('States', () => (
<div>
<h3>Radio</h3>
<StaticStates>
<ComponentStatesTable
rowProps={permutateProps({
checked: [{value: true, label: 'Checked'}, {value: false, label: 'Unchecked'}],
})}
columnProps={permutateProps(
{
className: [
{label: 'Default', value: ''},
{label: 'Hover', value: 'hover'},
{label: 'Focus', value: 'focus'},
{label: 'Focus Hover', value: 'focus hover'},
{label: 'Active', value: 'active'},
{label: 'Active Hover', value: 'active hover'},
],
disabled: [{label: '', value: false}, {label: 'Disabled', value: true}],
},
props => {
if (props.disabled && !['', 'hover'].includes(props.className)) {
return false;
}
return true;
}
)}
>
{props => (
<Radio
{...props}
onChange={() => {}} // eslint-disable-line no-empty-function
label="Radio"
/>
)}
</ComponentStatesTable>
</StaticStates>
<h3>Radio Group</h3>
<StaticStates>
<ComponentStatesTable
rowProps={permutateProps({
error: [
{value: undefined, label: 'No Error'},
{value: FormField.ErrorType.Alert, label: 'Alert'},
{value: FormField.ErrorType.Error, label: 'Error'},
],
})}
columnProps={[{label: 'Default', props: {}}]}
>
{props => (
<FormField
useFieldset={true}
hintText={props.error ? hintText : undefined}
hintId={hintId}
labelPosition={FormField.LabelPosition.Left}
{...props}
>
<ControlledComponentWrapper>
<RadioGroup name="contact">
<Radio id="1" value="email" label="E-mail" />
<Radio id="2" value="fax" label="Fax (disabled)" disabled={true} />
</RadioGroup>
</ControlledComponentWrapper>
</FormField>
)}
</ComponentStatesTable>
</StaticStates>
</div>
));
157 changes: 78 additions & 79 deletions modules/radio/react/spec/Radio.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,108 +1,107 @@
import * as React from 'react';
import {mount} from 'enzyme';
import {render, fireEvent} from '@testing-library/react';
import Radio from '../lib/Radio';
import ReactDOMServer from 'react-dom/server';
import {axe} from 'jest-axe';
import FormField from '../../../form-field/react';

describe('Radio Input', () => {
describe('Radio', () => {
const cb = jest.fn();
afterEach(() => {
cb.mockReset();
});

test('render an radio input with id', () => {
const component = mount(<Radio id="myRadio" onChange={cb} />);
expect(component.find('input').props().id).toBe('myRadio');
component.unmount();
describe('when rendered', () => {
it('should render an input with type=radio', () => {
const {getByRole} = render(<Radio onChange={cb} />);
expect(getByRole('radio')).toHaveProperty('type', 'radio');
});

it('should be unchecked by default', () => {
const {getByRole} = render(<Radio onChange={cb} />);
expect(getByRole('radio')).toHaveProperty('checked', false);
});
});

test('render an radio input with name', () => {
const component = mount(<Radio name="myRadio" onChange={cb} />);
expect(component.find('input').props().name).toBe('myRadio');
component.unmount();
describe('when rendered with an id', () => {
it('should render a radio input with id', () => {
const id = 'myRadio';
const {getByRole} = render(<Radio id={id} onChange={cb} />);
expect(getByRole('radio')).toHaveAttribute('id', id);
});
});

test('render an radio input with value', () => {
const component = mount(<Radio value="myRadio" onChange={cb} />);
expect(component.find('input').props().value).toBe('myRadio');
component.unmount();
describe('when rendered with a value', () => {
it('should render a radio input with value', () => {
const value = 'myRadio';
const {getByDisplayValue} = render(<Radio value={value} onChange={cb} />);
expect(getByDisplayValue(value)).toBeDefined();
});
});

test('render an checked radio input', () => {
const component = mount(<Radio checked={true} onChange={cb} />);
expect(component.find('input').props().checked).toBe(true);
component.unmount();
describe('when rendered with checked=true', () => {
it('should render a checked radio input', () => {
const {getByRole} = render(<Radio checked={true} onChange={cb} />);
expect(getByRole('radio')).toHaveProperty('checked', true);
});
});

test('render an disabled radio input', () => {
const component = mount(<Radio disabled={true} onChange={cb} />);
expect(component.find('input').props().disabled).toBe(true);
component.unmount();
describe('when rendered with disabled attribute', () => {
it('should render a disabled radio input', () => {
const {getByRole} = render(<Radio disabled={true} onChange={cb} />);
expect(getByRole('radio')).toHaveProperty('disabled', true);
});
});

test('should call a callback function', () => {
const component = mount(<Radio onChange={cb} />);
const input = component.find('input');
input.simulate('change');
describe('when rendered without an id', () => {
it('should create a unique id for each instance', async () => {
const {getByLabelText} = render(
<form>
<Radio checked={true} onChange={cb} disabled={false} label="label1" />;
<Radio onChange={cb} disabled={false} label="label2" />;
</form>
);

expect(cb.mock.calls.length).toBe(1);
component.unmount();
});
const id1 = getByLabelText('label1').getAttribute('id');
const id2 = getByLabelText('label2').getAttribute('id');

test('Radio input should spread extra props', () => {
const component = mount(<Radio data-propspread="test" onChange={jest.fn()} />);
const input = component
.find('input') // TODO: Standardize on prop spread location (see #150)
.getDOMNode();
expect(input.getAttribute('data-propspread')).toBe('test');
component.unmount();
});
expect(id1).not.toEqual(id2);
});

it('should keep the same unique id if re-rendered', () => {
const {getByRole, rerender} = render(<Radio checked={false} onChange={cb} />);

const uniqueId = getByRole('radio').getAttribute('id');
expect(getByRole('radio')).toHaveProperty('id', uniqueId);

test('Radio creates a unique id for each instance', async () => {
const fragment = mount(
<form>
<Radio checked={true} onChange={jest.fn()} disabled={false} />;
<Radio onChange={jest.fn()} disabled={false} />;
</form>
);

const id1 = fragment
.find('input')
.at(0)
.getDOMNode()
.getAttribute('id');

const id2 = fragment
.find('input')
.at(1)
.getDOMNode()
.getAttribute('id');

expect(id1).not.toEqual(id2);
fragment.unmount();
rerender(<Radio checked={true} onChange={cb} />);

expect(getByRole('radio')).toHaveProperty('checked');
expect(getByRole('radio')).toHaveProperty('id', uniqueId);
});
});
});

describe('Radio Accessibility', () => {
test('Radio should pass axe DOM accessibility guidelines', async () => {
const html = ReactDOMServer.renderToString(
<Radio id={'123'} label={'Label'} onChange={jest.fn()} />
);
expect(await axe(html)).toHaveNoViolations();
describe('when rendered with extra, arbitrary props', () => {
it('should spread extra props onto the radio', () => {
const attr = 'test';
const {getByRole} = render(<Radio data-propspread={attr} onChange={cb} />);
expect(getByRole('radio')).toHaveAttribute('data-propspread', attr);
});
});

test('Radio without a defined id should pass axe DOM accessibility guidelines', async () => {
const html = ReactDOMServer.renderToString(<Radio label={'Label'} onChange={jest.fn()} />);
expect(await axe(html)).toHaveNoViolations();
describe('when rendered with an input ref', () => {
it('should set the ref to the checkbox input element', () => {
const ref = React.createRef<HTMLInputElement>();

render(<Radio inputRef={ref} onChange={cb} />);

expect(ref.current).not.toBeNull();
expect(ref.current).toHaveAttribute('type', 'radio');
});
});

test('Radio using FormField should pass axe DOM accessibility guidelines', async () => {
const html = ReactDOMServer.renderToString(
<FormField label="My Field" inputId="my-radio-field">
<Radio disabled={false} checked={true} id="my-radio-field" onChange={jest.fn()} />
</FormField>
);
expect(await axe(html)).toHaveNoViolations();
describe('when clicked', () => {
it('should call a callback function', () => {
const {getByRole} = render(<Radio onChange={cb} />);
fireEvent.click(getByRole('radio'));
expect(cb).toHaveBeenCalledTimes(1);
});
});
});
Loading

0 comments on commit 5824333

Please sign in to comment.