Skip to content

Commit

Permalink
fix(react): update radio button group state initialization (#9104)
Browse files Browse the repository at this point in the history
* fix(react): update radio button group state initialization

* test(react): add test for RadioButtonGroup

* feat(jest-config): add jest-dom as default matcher

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
joshblack and kodiakhq[bot] authored Jul 7, 2021
1 parent 8476be8 commit 0e2b53b
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 126 deletions.
2 changes: 2 additions & 0 deletions config/jest-config-carbon/setup/setupAfterEnv.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const util = require('util');
const toHaveNoAxeViolations = require('../matchers/toHaveNoAxeViolations');
const toHaveNoACViolations = require('../matchers/toHaveNoACViolations');

require('@testing-library/jest-dom');

// We can extend `expect` using custom matchers as defined by:
// https://jest-bot.github.io/jest/docs/expect.html#expectextendmatchers
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import React from 'react';
import { default as Accordion, AccordionItem } from '../';
import { cleanup, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';

describe('Accordion', () => {
afterEach(cleanup);
Expand Down
1 change: 0 additions & 1 deletion packages/react/src/components/Button/Button-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { shallow, mount } from 'enzyme';
import { settings } from 'carbon-components';
import { cleanup, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';

const { prefix } = settings;

Expand Down
1 change: 0 additions & 1 deletion packages/react/src/components/Checkbox/Checkbox-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { mount } from 'enzyme';
import { settings } from 'carbon-components';
import { cleanup, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';

const { prefix } = settings;

Expand Down
1 change: 0 additions & 1 deletion packages/react/src/components/Link/Link-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import Link from '../Link';
import { shallow } from 'enzyme';
import { settings } from 'carbon-components';
import { cleanup, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';
import { describe, document } from 'window-or-global';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
} from '../next/Notification';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import { settings } from 'carbon-components';
import { it } from 'window-or-global';

Expand Down
301 changes: 184 additions & 117 deletions packages/react/src/components/RadioButtonGroup/RadioButtonGroup-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,155 +5,222 @@
* LICENSE file in the root directory of this source tree.
*/

import { render, screen } from '@testing-library/react';
import React from 'react';
import { shallow, mount } from 'enzyme';
import RadioButtonGroup from '../RadioButtonGroup';
import RadioButton from '../RadioButton';
import { settings } from 'carbon-components';

const { prefix } = settings;

describe('RadioButtonGroup', () => {
describe('renders as expected', () => {
const wrapper = mount(
<RadioButtonGroup
defaultSelected="female"
name="gender"
legendText="Radio legend">
<RadioButton labelText="Male" value="male" />
<RadioButton labelText="Female" value="female" />
it('should render `legendText` in a <label>', () => {
render(
<RadioButtonGroup defaultSelected="test-1" name="test" legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

describe('wrapping fieldset', () => {
const fieldset = wrapper.find('fieldset');
const legend = wrapper.find('legend');

it('renders a fieldset', () => {
expect(fieldset.length).toEqual(1);
});

it('renders a legend if legendText is provided', () => {
expect(legend.length).toEqual(1);
});

it('sets classes that are passed via className prop', () => {
wrapper.setProps({ className: 'extra-class' });
expect(wrapper.find('fieldset').hasClass('extra-class')).toBe(true);
});

it('sets disabled attribute if disabled prop is set', () => {
wrapper.setProps({ disabled: true });
expect(wrapper.first().props().disabled).toEqual(true);
});
const legend = screen.getByText('test', {
selector: 'legend',
});
expect(legend).toBeDefined();
});

describe('children', () => {
const radioButton = () => wrapper.find(RadioButton);

it('renders expected number of children', () => {
expect(radioButton().length).toEqual(2);
});

it('should set checked property based on defaultSelected prop', () => {
expect(radioButton().last().props().checked).toEqual(true);
});

it('should set checked property based on valueSelected prop', () => {
wrapper.setProps({ valueSelected: 'male' });
expect(radioButton().first().props().checked).toEqual(true);
wrapper.setProps({ valueSelected: 'female' });
expect(radioButton().last().props().checked).toEqual(true);
});
it('should render `legendText` in a <fieldset>', () => {
render(
<RadioButtonGroup defaultSelected="test-1" name="test" legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

it('should set expected props on children', () => {
const firstChild = radioButton().first();
expect(firstChild.props().name).toEqual('gender');
expect(firstChild.props().value).toEqual('male');
});
});
const fieldset = screen
.getByText('test', {
selector: 'legend',
})
.closest('fieldset');
expect(fieldset).toBeDefined();
});

describe('onChange event', () => {
const onChange = jest.fn();
const wrapper = mount(
<RadioButtonGroup onChange={onChange} name="gender">
<RadioButton labelText="Male" value="male" />
<RadioButton labelText="Female" value="female" />
it('should render <RadioButton> as children', () => {
render(
<RadioButtonGroup defaultSelected="test-1" name="test" legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

const firstRadio = wrapper.find(RadioButton).first();
const args = ['male', 'gender', { test: 'test event' }];
const fieldset = screen
.getByText('test', {
selector: 'legend',
})
.closest('fieldset');
expect(fieldset).toContainElement(screen.getByLabelText('test-1'));
expect(fieldset).toContainElement(screen.getByLabelText('test-2'));
});

describe('Component API', () => {
it('should support a custom className on the <fieldset>', () => {
render(
<RadioButtonGroup
className="custom-class"
defaultSelected="test-1"
name="test"
legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

const fieldset = screen
.getByText('test', {
selector: 'legend',
})
.closest('fieldset');
expect(fieldset).toHaveClass('custom-class');
});

it('first child should not have checked set initially', () => {
expect(firstRadio.props().checked).toEqual(false);
it('should support passing in disabled to disable the <fieldset>', () => {
render(
<RadioButtonGroup
defaultSelected="test-1"
disabled
name="test"
legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);
const fieldset = screen
.getByText('test', {
selector: 'legend',
})
.closest('fieldset');
expect(fieldset).toBeDisabled();
});

it('invoking onChange sets checked on correct child', () => {
firstRadio.props().onChange(...args);
wrapper.update();
expect(wrapper.find(RadioButton).first().props().checked).toEqual(true);
it('should support `defaultSelected` as a way to select a radio button', () => {
render(
<RadioButtonGroup
defaultSelected="test-1"
name="test"
legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

expect(screen.getByLabelText('test-1')).toEqual(
screen.getByRole('radio', {
checked: true,
})
);
});

it('should invoke onChange with correct arguments', () => {
expect(onChange).toHaveBeenCalledWith(...args);
it('should support `valueSelected` as a way to select a radio button', () => {
const { rerender } = render(
<RadioButtonGroup valueSelected="test-1" name="test" legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

expect(screen.getByLabelText('test-1')).toEqual(
screen.getByRole('radio', {
checked: true,
})
);

rerender(
<RadioButtonGroup valueSelected="test-2" name="test" legendText="test">
<RadioButton labelText="test-1" value="test-1" />
<RadioButton labelText="test-2" value="test-2" />
</RadioButtonGroup>
);

expect(screen.getByLabelText('test-2')).toEqual(
screen.getByRole('radio', {
checked: true,
})
);
});

it('calling onChange with same args should not call onChange prop', () => {
onChange.mockClear();
firstRadio.props().onChange(...args);
expect(onChange).not.toHaveBeenCalled();
it('should support a 0 value for `valueSelected` (#9041)', () => {
render(
<RadioButtonGroup valueSelected={0} name="test" legendText="test">
<RadioButton labelText="test-1" value={1} />
<RadioButton labelText="test-0" value={0} />
</RadioButtonGroup>
);

expect(screen.getByLabelText('test-0')).toEqual(
screen.getByRole('radio', {
checked: true,
})
);
});
});

describe('Getting derived state from props', () => {
const wrapper = shallow(
<RadioButtonGroup
valueSelected="male"
defaultSelected="female"
name="gender">
<RadioButton labelText="Male" value="male" />
<RadioButton labelText="Female" value="female" />
</RadioButtonGroup>
);
describe('onChange event', () => {
const onChange = jest.fn();
const wrapper = mount(
<RadioButtonGroup onChange={onChange} name="gender">
<RadioButton labelText="Male" value="male" />
<RadioButton labelText="Female" value="female" />
</RadioButtonGroup>
);

it('should initialize the current selection from props', () => {
expect(wrapper.state().selected).toEqual('male');
});
const firstRadio = wrapper.find(RadioButton).first();
const args = ['male', 'gender', { test: 'test event' }];

it('should change the current selection upon change in props', () => {
wrapper.setProps({ valueSelected: 'male' });
wrapper.setState({ selected: 'male' });
wrapper.setProps({ valueSelected: undefined });
expect(wrapper.state().selected).toEqual('female');
});
it('first child should not have checked set initially', () => {
expect(firstRadio.props().checked).toEqual(false);
});

it('invoking onChange sets checked on correct child', () => {
firstRadio.props().onChange(...args);
wrapper.update();
expect(wrapper.find(RadioButton).first().props().checked).toEqual(true);
});

it('should invoke onChange with correct arguments', () => {
expect(onChange).toHaveBeenCalledWith(...args);
});

it('should avoid change the current selection upon setting props, unless there the value actually changes', () => {
wrapper.setProps({ valueSelected: 'female' });
wrapper.setState({ selected: 'male' });
wrapper.setProps({ valueSelected: 'female' });
expect(wrapper.state().selected).toEqual('male');
it('calling onChange with same args should not call onChange prop', () => {
onChange.mockClear();
firstRadio.props().onChange(...args);
expect(onChange).not.toHaveBeenCalled();
});
});
});

describe('Custom class name should stay with original class name', () => {
const wrapper = shallow(
<RadioButtonGroup
className="my-radio-group"
valueSelected="male"
defaultSelected="female"
name="gender">
<RadioButton labelText="Male" value="male" />
<RadioButton labelText="Female" value="female" />
</RadioButtonGroup>
);
describe('Getting derived state from props', () => {
const wrapper = shallow(
<RadioButtonGroup
valueSelected="male"
defaultSelected="female"
name="gender">
<RadioButton labelText="Male" value="male" />
<RadioButton labelText="Female" value="female" />
</RadioButtonGroup>
);

it('should initialize the current selection from props', () => {
expect(wrapper.state().selected).toEqual('male');
});

it('should found the provided class along with the base class', () => {
expect(wrapper.exists('.my-radio-group')).toBe(true);
expect(
wrapper.exists(`.${prefix}--radio-button-group.my-radio-group`)
).toBe(true);
it('should change the current selection upon change in props', () => {
wrapper.setProps({ valueSelected: 'male' });
wrapper.setState({ selected: 'male' });
wrapper.setProps({ valueSelected: undefined });
expect(wrapper.state().selected).toEqual('female');
});

it('should avoid change the current selection upon setting props, unless there the value actually changes', () => {
wrapper.setProps({ valueSelected: 'female' });
wrapper.setState({ selected: 'male' });
wrapper.setProps({ valueSelected: 'female' });
expect(wrapper.state().selected).toEqual('male');
});
});
});
});
Loading

0 comments on commit 0e2b53b

Please sign in to comment.