diff --git a/cypress/integration/Radio.spec.ts b/cypress/integration/Radio.spec.ts
new file mode 100644
index 0000000000..27f34eb221
--- /dev/null
+++ b/cypress/integration/Radio.spec.ts
@@ -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');
+ });
+ });
+});
diff --git a/cypress/integration/RadioGroup.spec.ts b/cypress/integration/RadioGroup.spec.ts
new file mode 100644
index 0000000000..a3da5a7d95
--- /dev/null
+++ b/cypress/integration/RadioGroup.spec.ts
@@ -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');
+ });
+ });
+ });
+ });
+});
diff --git a/modules/form-field/react/stories/stories_Radio.tsx b/modules/form-field/react/stories/stories_Radio.tsx
index 4ff40b2b3d..a33eb8e8bf 100644
--- a/modules/form-field/react/stories/stories_Radio.tsx
+++ b/modules/form-field/react/stories/stories_Radio.tsx
@@ -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';
@@ -72,7 +77,7 @@ storiesOf('Components|Inputs/Radio/React/Top Label/Radio', module)
-
+
));
@@ -140,7 +145,89 @@ storiesOf('Components|Inputs/Radio/React/Left Label/Radio', module)
-
+
+ ))
+ .add('Disabled', () => (
+
+
+
+
+
+ ));
+
+storiesOf('Components|Inputs/Radio/React/Visual', module)
+ .addParameters({component: Radio})
+ .addDecorator(withReadme(README))
+ .add('States', () => (
+
+
Radio
+
+ {
+ if (props.disabled && !['', 'hover'].includes(props.className)) {
+ return false;
+ }
+ return true;
+ }
+ )}
+ >
+ {props => (
+ {}} // eslint-disable-line no-empty-function
+ label="Radio"
+ />
+ )}
+
+
+ Radio Group
+
+
+ {props => (
+
+
+
+
+
+
+
+
+ )}
+
+
+
));
diff --git a/modules/radio/react/spec/Radio.spec.tsx b/modules/radio/react/spec/Radio.spec.tsx
index 5037cf60bb..fc09cf35c0 100644
--- a/modules/radio/react/spec/Radio.spec.tsx
+++ b/modules/radio/react/spec/Radio.spec.tsx
@@ -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();
- expect(component.find('input').props().id).toBe('myRadio');
- component.unmount();
+ describe('when rendered', () => {
+ it('should render an input with type=radio', () => {
+ const {getByRole} = render();
+ expect(getByRole('radio')).toHaveProperty('type', 'radio');
+ });
+
+ it('should be unchecked by default', () => {
+ const {getByRole} = render();
+ expect(getByRole('radio')).toHaveProperty('checked', false);
+ });
});
- test('render an radio input with name', () => {
- const component = mount();
- 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();
+ expect(getByRole('radio')).toHaveAttribute('id', id);
+ });
});
- test('render an radio input with value', () => {
- const component = mount();
- 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();
+ expect(getByDisplayValue(value)).toBeDefined();
+ });
});
- test('render an checked radio input', () => {
- const component = mount();
- 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();
+ expect(getByRole('radio')).toHaveProperty('checked', true);
+ });
});
- test('render an disabled radio input', () => {
- const component = mount();
- 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();
+ expect(getByRole('radio')).toHaveProperty('disabled', true);
+ });
});
- test('should call a callback function', () => {
- const component = mount();
- 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(
+
+ );
- 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();
- 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();
+
+ 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(
-
- );
-
- 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();
+
+ 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(
-
- );
- 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();
+ expect(getByRole('radio')).toHaveAttribute('data-propspread', attr);
+ });
});
- test('Radio without a defined id should pass axe DOM accessibility guidelines', async () => {
- const html = ReactDOMServer.renderToString();
- 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();
+
+ render();
+
+ 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(
-
-
-
- );
- expect(await axe(html)).toHaveNoViolations();
+ describe('when clicked', () => {
+ it('should call a callback function', () => {
+ const {getByRole} = render();
+ fireEvent.click(getByRole('radio'));
+ expect(cb).toHaveBeenCalledTimes(1);
+ });
});
});
diff --git a/modules/radio/react/spec/RadioGroup.spec.tsx b/modules/radio/react/spec/RadioGroup.spec.tsx
index 39e6db5e7c..657676205b 100644
--- a/modules/radio/react/spec/RadioGroup.spec.tsx
+++ b/modules/radio/react/spec/RadioGroup.spec.tsx
@@ -1,151 +1,72 @@
import * as React from 'react';
-import {mount} from 'enzyme';
-import Radio from '../lib/Radio';
-import RadioGroup from '../lib/RadioGroup';
+import {render, fireEvent} from '@testing-library/react';
+import {Radio, RadioGroup} from '../index';
-describe('Radio', () => {
+describe('Radio Group', () => {
const cb = jest.fn();
afterEach(() => {
cb.mockReset();
});
- test('renders two radios as expected', () => {
- const component = mount(
-
-
-
-
- );
-
- const inputs = component.find('input');
-
- expect(inputs.length).toEqual(2);
- expect(inputs.get(0).props.name).toEqual('contact');
- expect(inputs.get(1).props.name).toEqual('contact');
+ describe('when rendered rendered with a name', () => {
+ test('should render a radio group with a name', () => {
+ const {getByTestId} = render(
+
+
+
+
+ );
+ expect(getByTestId('radiogroup')).toHaveAttribute('name');
+ });
});
-
- test('renders two radios with name set at radio level as expected', () => {
- const component = mount(
-
-
-
-
- );
-
- const inputs = component.find('input');
-
- expect(inputs.length).toEqual(2);
- expect(inputs.get(0).props.name).toEqual('contact');
- expect(inputs.get(1).props.name).toEqual('contact');
+ describe('when rendered with a value', () => {
+ test('should render a selected radio that matches that value of a string', async () => {
+ const {getByLabelText} = render(
+
+
+
+
+ );
+
+ const phoneRadio = await getByLabelText('Phone');
+ expect(phoneRadio).toHaveProperty('checked', true);
+ });
+ test('should render a selected radio that matches that value of a index', async () => {
+ const {getByLabelText} = render(
+
+
+
+
+ );
+
+ const phoneRadio = await getByLabelText('Phone');
+ expect(phoneRadio).toHaveProperty('checked', true);
+ });
});
-
- test('renders two radios preselected with index as expected', () => {
- const component = mount(
-
-
-
-
- );
-
- const inputs = component.find('input');
-
- expect(inputs.length).toEqual(2);
- expect(inputs.get(0).props.name).toEqual('contact');
- expect(inputs.get(1).props.name).toEqual('contact');
+ describe('when rendered with extra, arbitrary props', () => {
+ it('should spread extra props onto the radio group', () => {
+ const attr = 'test';
+ const {getByTestId} = render(
+
+
+
+
+ );
+ expect(getByTestId('radiogroup')).toHaveAttribute('data-propspread', attr);
+ });
});
-
- test('renders two radios preselected with value as expected', () => {
- const component = mount(
-
-
-
-
- );
-
- const inputs = component.find('input');
-
- expect(inputs.length).toEqual(2);
- expect(inputs.get(0).props.name).toEqual('contact');
- expect(inputs.get(1).props.name).toEqual('contact');
- });
-
- test('can switch to a different radio', () => {
- const component = mount(
-
-
-
-
- );
-
- component
- .find('input')
- .at(1)
- .simulate('change');
-
- expect(cb.mock.calls.length).toBe(1);
- expect(cb.mock.calls[0][0]).toBe('phone');
- });
-
- test('can switch to a different radio without value', () => {
- const component = mount(
-
-
-
-
- );
-
- component
- .find('input')
- .at(1)
- .simulate('change');
-
- expect(cb.mock.calls.length).toBe(1);
- expect(cb.mock.calls[0][0]).toBe(1);
- });
-
- test('renders two radios and one disabled as expected', () => {
- const component = mount(
-
-
-
-
- {/* ensure random elements don't break anything */}
-
- );
-
- const inputs = component.find('input');
-
- expect(inputs.length).toEqual(3);
- expect(inputs.get(1).props.disabled).toBe(true);
- });
-
- test('simulate onchange on a radio', () => {
- const component = mount(
-
-
-
-
- {/* ensure random elements don't break anything */}
-
- );
-
- component
- .find('input')
- .at(2)
- .simulate('change');
-
- expect(cb.mock.calls.length).toBe(1);
- });
-
- test('RadioGroup should spread extra props', () => {
- const component = mount(
-
-
-
-
- );
- const container = component.at(0).getDOMNode();
- expect(container.getAttribute('data-propspread')).toBe('test');
- component.unmount();
+ describe('when clicked', () => {
+ it('should call a callback function', () => {
+ const {getByLabelText} = render(
+
+
+
+
+ );
+
+ fireEvent.click(getByLabelText('Phone'));
+
+ expect(cb).toHaveBeenCalledWith('phone');
+ });
});
});