Skip to content

Commit

Permalink
Merge branch 'main' into style/listbox-tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
sstrubberg authored Oct 28, 2021
2 parents fb1069c + 56d08c0 commit 70a92bc
Show file tree
Hide file tree
Showing 10 changed files with 701 additions and 39 deletions.
13 changes: 4 additions & 9 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -7054,10 +7054,11 @@ Map {
"TimePickerSelect" => Object {
"defaultProps": Object {
"disabled": false,
"iconDescription": "open list of options",
"inline": true,
},
"propTypes": Object {
"aria-label": Object {
"type": "string",
},
"children": Object {
"type": "node",
},
Expand All @@ -7071,17 +7072,11 @@ Map {
"type": "bool",
},
"hideLabel": [Function],
"iconDescription": Object {
"isRequired": true,
"type": "string",
},
"iconDescription": [Function],
"id": Object {
"isRequired": true,
"type": "string",
},
"inline": Object {
"type": "bool",
},
"labelText": Object {
"isRequired": true,
"type": "node",
Expand Down
11 changes: 9 additions & 2 deletions packages/react/src/components/RadioButton/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
* LICENSE file in the root directory of this source tree.
*/

import * as FeatureFlags from '@carbon/feature-flags';
import { default as RadioButtonNext } from './next/RadioButton';
import { default as RadioButtonClassic } from './RadioButton';

const RadioButton = FeatureFlags.enabled('enable-v11-release')
? RadioButtonNext
: RadioButtonClassic;

export default RadioButton;
export * from './RadioButton.Skeleton';
export * from './RadioButton';
export default from './RadioButton';
161 changes: 161 additions & 0 deletions packages/react/src/components/RadioButton/next/RadioButton-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/**
* Copyright IBM Corp. 2016, 2018
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import RadioButton from '../RadioButton';
import RadioButtonSkeleton from '../../RadioButton/RadioButton.Skeleton';
import { mount, shallow } from 'enzyme';
import { settings } from 'carbon-components';

const { prefix } = settings;

const render = (props) =>
mount(
<RadioButton
{...props}
className="extra-class"
name="test-name"
value="test-value"
labelText="testlabel"
/>
);

describe('RadioButton', () => {
describe('renders as expected', () => {
const wrapper = render({
checked: true,
});

const input = wrapper.find('input');
const label = wrapper.find('label');
const div = wrapper.find('div');

describe('input', () => {
it('is of type radio', () => {
expect(input.props().type).toEqual('radio');
});

it('has the expected class', () => {
expect(input.hasClass(`${prefix}--radio-button`)).toEqual(true);
});

it('has a unique id set by default', () => {
expect(input.props().id).toBeDefined();
});

it('should have checked set when checked is passed', () => {
wrapper.setProps({ checked: true });
expect(input.props().checked).toEqual(true);
});

it('should set the name prop as expected', () => {
expect(input.props().name).toEqual('test-name');
});
});

describe('label', () => {
it('should set htmlFor', () => {
expect(label.props().htmlFor).toEqual(input.props().id);
});

it('should set the correct class', () => {
expect(label.props().className).toEqual(
`${prefix}--radio-button__label`
);
});

it('should render a span with the correct class', () => {
const span = label.find('span');
expect(
span.at(0).hasClass(`${prefix}--radio-button__appearance`)
).toEqual(true);
});

it('should render a span for the label text', () => {
const span = label.find('span');
expect(span.at(1).hasClass('')).toEqual(true);
expect(span.at(1).text()).toEqual('testlabel');
});

it('should render a span with hidden class name to hide label text', () => {
wrapper.setProps({
hideLabel: true,
});
const label = wrapper.find('span');
const span = label.find('span');
expect(span.at(1).hasClass(`${prefix}--visually-hidden`)).toEqual(true);
expect(span.at(1).text()).toEqual('testlabel');
});

it('should render label text', () => {
wrapper.setProps({ labelText: 'test label text' });
expect(label.text()).toMatch(/test label text/);
});
});

describe('wrapper', () => {
it('should have the correct class', () => {
expect(div.hasClass(`${prefix}--radio-button-wrapper`)).toEqual(true);
});

it('should have extra classes applied', () => {
expect(div.hasClass('extra-class')).toEqual(true);
});
});
});

it('should set defaultChecked as expected', () => {
const wrapper = render({
defaultChecked: true,
});

const input = () => wrapper.find('input');
expect(input().props().defaultChecked).toEqual(true);
wrapper.setProps({ defaultChecked: false });
expect(input().props().defaultChecked).toEqual(false);
});

it('should set id if one is passed in', () => {
const wrapper = render({
id: 'unique-id',
});

const input = wrapper.find('input');
expect(input.props().id).toEqual('unique-id');
});

describe('events', () => {
it('should invoke onChange with expected arguments', () => {
const onChange = jest.fn();
const wrapper = render({ onChange });
const input = wrapper.find('input');
const inputElement = input.instance();

inputElement.checked = true;
wrapper.find('input').simulate('change');

const call = onChange.mock.calls[0];

expect(call[0]).toEqual('test-value');
expect(call[1]).toEqual('test-name');
expect(call[2].target).toBe(inputElement);
});
});
});

describe('RadioButtonSkeleton', () => {
describe('Renders as expected', () => {
const wrapper = shallow(<RadioButtonSkeleton />);

const label = wrapper.find('span');

it('Has the expected classes', () => {
expect(label.hasClass(`${prefix}--skeleton`)).toEqual(true);
expect(label.hasClass(`${prefix}--radio-button__label`)).toEqual(true);
});
});
});
127 changes: 127 additions & 0 deletions packages/react/src/components/RadioButton/next/RadioButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import PropTypes from 'prop-types';
import React from 'react';
import classNames from 'classnames';
import { usePrefix } from '../../../internal/usePrefix';
import { useId } from '../../../internal/useId';

import { Text } from '../../Text';

const RadioButton = React.forwardRef(function RadioButton(
{
className,
hideLabel,
id,
labelPosition = 'right',
labelText = '',
name,
onChange = () => {},
value = '',
...rest
},
ref
) {
const prefix = usePrefix();
const uid = useId('radio-button');
const uniqueId = id || uid;

function handleOnChange(event) {
onChange(value, name, event);
}

const innerLabelClasses = classNames({
[`${prefix}--visually-hidden`]: hideLabel,
});

const wrapperClasses = classNames(
className,
`${prefix}--radio-button-wrapper`,
{
[`${prefix}--radio-button-wrapper--label-${labelPosition}`]:
labelPosition !== 'right',
}
);

return (
<div className={wrapperClasses}>
<input
{...rest}
type="radio"
className={`${prefix}--radio-button`}
onChange={handleOnChange}
id={uniqueId}
ref={ref}
/>
<label htmlFor={uniqueId} className={`${prefix}--radio-button__label`}>
<span className={`${prefix}--radio-button__appearance`} />
{labelText && <Text className={innerLabelClasses}>{labelText}</Text>}
</label>
</div>
);
});

RadioButton.propTypes = {
/**
* Specify whether the <RadioButton> is currently checked
*/
checked: PropTypes.bool,

/**
* Provide an optional className to be applied to the containing node
*/
className: PropTypes.string,

/**
* Specify whether the <RadioButton> should be checked by default
*/
defaultChecked: PropTypes.bool,

/**
* Specify whether the control is disabled
*/
disabled: PropTypes.bool,

/**
* Specify whether the label should be hidden, or not
*/
hideLabel: PropTypes.bool,

/**
* Provide a unique id for the underlying `<input>` node
*/
id: PropTypes.string,

/**
* Provide where label text should be placed
* NOTE: `top`/`bottom` are deprecated
*/
labelPosition: PropTypes.oneOf(['right', 'left']),

/**
* Provide label text to be read by screen readers when interacting with the
* control
*/
labelText: PropTypes.node.isRequired,

/**
* Provide a name for the underlying `<input>` node
*/
name: PropTypes.string,

/**
* Provide an optional `onChange` hook that is called each time the value of
* the underlying `<input>` changes
*/
onChange: PropTypes.func,

/**
* Provide a handler that is invoked when a user clicks on the control
*/
onClick: PropTypes.func,

/**
* Specify the value of the <RadioButton>
*/
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
};

export default RadioButton;
10 changes: 9 additions & 1 deletion packages/react/src/components/RadioButtonGroup/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/

export default from './RadioButtonGroup';
import * as FeatureFlags from '@carbon/feature-flags';
import { default as RadioButtonGroupNext } from './next/RadioButtonGroup';
import { default as RadioButtonGroupClassic } from './RadioButtonGroup';

const RadioButtonGroup = FeatureFlags.enabled('enable-v11-release')
? RadioButtonGroupNext
: RadioButtonGroupClassic;

export default RadioButtonGroup;
Loading

0 comments on commit 70a92bc

Please sign in to comment.