diff --git a/packages/patternfly-4/react-core/src/components/Switch/Switch.d.ts b/packages/patternfly-4/react-core/src/components/Switch/Switch.d.ts new file mode 100644 index 00000000000..f3433ec2388 --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/Switch.d.ts @@ -0,0 +1,16 @@ +import { HTMLProps, FormEvent, ReactNode } from 'react'; +import { Omit } from '../../typeUtils'; + +export interface SwitchProps extends Omit, 'type' | 'onChange' | 'disabled' | 'label'> { + isDisabled?: boolean; + isChecked?: boolean; + onChange?(checked: boolean, event: FormEvent): void; + id?: string; + 'aria-label': string; + label?: string; + className?: string; +} + +declare const Switch: React.SFC; + +export default Switch; diff --git a/packages/patternfly-4/react-core/src/components/Switch/Switch.docs.js b/packages/patternfly-4/react-core/src/components/Switch/Switch.docs.js new file mode 100644 index 00000000000..da9d5b1ca33 --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/Switch.docs.js @@ -0,0 +1,18 @@ +import { Switch } from '@patternfly/react-core'; +import SimpleSwitch from './examples/SimpleSwitch'; +import NoLabelSwitch from './examples/NoLabelSwitch'; +import DisabledSwitch from './examples/DisabledSwitch'; +import UncontrolledSwitch from './examples/UncontrolledSwitch'; + +export default { + title: 'Switch', + components: { + Switch + }, + examples: [ + { component: SimpleSwitch, title: 'Simple Switch' }, + { component: NoLabelSwitch, title: 'Switch with no labels' }, + { component: DisabledSwitch, title: 'Disabled Switch' }, + { component: UncontrolledSwitch, title: 'Uncontrolled Switch' } + ] +}; diff --git a/packages/patternfly-4/react-core/src/components/Switch/Switch.js b/packages/patternfly-4/react-core/src/components/Switch/Switch.js new file mode 100644 index 00000000000..3dc2652cafe --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/Switch.js @@ -0,0 +1,68 @@ +import React from 'react'; +import styles from '@patternfly/patternfly-next/components/Switch/switch.css'; +import { css } from '@patternfly/react-styles'; +import PropTypes from 'prop-types'; +import { getUniqueId } from '../../internal/util'; + +const propTypes = { + /** id for the label. */ + id: PropTypes.string, + /** Additional classes added to the Switch */ + className: PropTypes.string, + /** Text value for the label */ + label: PropTypes.string, + /** Flag to show if the Switch is checked. */ + isChecked: PropTypes.bool, + /** Flag to show if the Switch is disabled. */ + isDisabled: PropTypes.bool, + /** A callback for when the Switch selection changes. (isChecked, event) => {} */ + onChange: PropTypes.func, + /** Adds accessible text to the Switch. */ + 'aria-label': props => { + if (!props.id && !props['aria-label']) { + return new Error('Switch requires either an id or aria-label to be specified'); + } + } +}; + +const defaultProps = { + id: '', + className: '', + label: '', + isChecked: true, + isDisabled: false, + onChange: () => undefined, + 'aria-label': '' +}; + +class Switch extends React.Component { + id = this.props.id || getUniqueId(); + + render() { + const { id, className, label, isChecked, isDisabled, onChange, ...props } = this.props; + return ( + + ); + } +} + +Switch.propTypes = propTypes; +Switch.defaultProps = defaultProps; + +export default Switch; diff --git a/packages/patternfly-4/react-core/src/components/Switch/Switch.test.js b/packages/patternfly-4/react-core/src/components/Switch/Switch.test.js new file mode 100644 index 00000000000..c168d64360a --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/Switch.test.js @@ -0,0 +1,59 @@ +import React from 'react'; +import { shallow } from 'enzyme'; +import Switch from './Switch'; + +const props = { + onChange: jest.fn(), + checked: false +}; + +test('switch label for attribute equals input id attribute', () => { + const view = shallow(); + expect(view.find('input').prop('id')).toBe('foo'); + expect(view.find('label').prop('htmlFor')).toBe('foo'); +}); + +test('switch label id is auto generated', () => { + const view = shallow(); + expect(view.find('input').prop('id')).toBeDefined(); +}); + +test('switch is checked', () => { + const view = shallow(); + expect(view).toMatchSnapshot(); +}); + +test('switch is not checked', () => { + const view = shallow(); + expect(view).toMatchSnapshot(); +}); + +test('no label switch is checked', () => { + const view = shallow(); + expect(view).toMatchSnapshot(); +}); + +test('no label switch is not checked', () => { + const view = shallow(); + expect(view).toMatchSnapshot(); +}); + +test('switch is checked and disabled', () => { + const view = shallow(); + expect(view).toMatchSnapshot(); +}); + +test('switch is not checked and disabled', () => { + const view = shallow(); + expect(view).toMatchSnapshot(); +}); + +test('switch passes value and event to onChange handler', () => { + const newValue = true; + const event = { + currentTarget: { checked: newValue } + }; + const view = shallow(); + view.find('input').simulate('change', event); + expect(props.onChange).toBeCalledWith(newValue, event); +}); diff --git a/packages/patternfly-4/react-core/src/components/Switch/__snapshots__/Switch.test.js.snap b/packages/patternfly-4/react-core/src/components/Switch/__snapshots__/Switch.test.js.snap new file mode 100644 index 00000000000..7a1df65c3e0 --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/__snapshots__/Switch.test.js.snap @@ -0,0 +1,223 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`no label switch is checked 1`] = ` +.pf-c-switch__input { + display: block; +} +.pf-c-switch__toggle { + display: block; +} +.pf-c-switch { + display: inline-block; + position: relative; + line-height: 1.5; + vertical-align: middle; + cursor: pointer; +} + + +`; + +exports[`no label switch is not checked 1`] = ` +.pf-c-switch__input { + display: block; +} +.pf-c-switch__toggle { + display: block; +} +.pf-c-switch { + display: inline-block; + position: relative; + line-height: 1.5; + vertical-align: middle; + cursor: pointer; +} + + +`; + +exports[`switch is checked 1`] = ` +.pf-c-switch__input { + display: block; +} +.pf-c-switch__toggle { + display: block; +} +.pf-c-switch__label { + display: block; +} +.pf-c-switch { + display: inline-block; + position: relative; + line-height: 1.5; + vertical-align: middle; + cursor: pointer; +} + + +`; + +exports[`switch is checked and disabled 1`] = ` +.pf-c-switch__input { + display: block; +} +.pf-c-switch__toggle { + display: block; +} +.pf-c-switch { + display: inline-block; + position: relative; + line-height: 1.5; + vertical-align: middle; + cursor: pointer; +} + + +`; + +exports[`switch is not checked 1`] = ` +.pf-c-switch__input { + display: block; +} +.pf-c-switch__toggle { + display: block; +} +.pf-c-switch__label { + display: block; +} +.pf-c-switch { + display: inline-block; + position: relative; + line-height: 1.5; + vertical-align: middle; + cursor: pointer; +} + + +`; + +exports[`switch is not checked and disabled 1`] = ` +.pf-c-switch__input { + display: block; +} +.pf-c-switch__toggle { + display: block; +} +.pf-c-switch { + display: inline-block; + position: relative; + line-height: 1.5; + vertical-align: middle; + cursor: pointer; +} + + +`; diff --git a/packages/patternfly-4/react-core/src/components/Switch/examples/DisabledSwitch.js b/packages/patternfly-4/react-core/src/components/Switch/examples/DisabledSwitch.js new file mode 100644 index 00000000000..75d529ce62b --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/examples/DisabledSwitch.js @@ -0,0 +1,42 @@ +import React from 'react'; +import { Switch } from '@patternfly/react-core'; + +class DisabledSwitch extends React.Component { + render() { + return ( + + +
+ +
+ +
+ +
+ ); + } +} + +export default DisabledSwitch; diff --git a/packages/patternfly-4/react-core/src/components/Switch/examples/NoLabelSwitch.js b/packages/patternfly-4/react-core/src/components/Switch/examples/NoLabelSwitch.js new file mode 100644 index 00000000000..af697612344 --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/examples/NoLabelSwitch.js @@ -0,0 +1,26 @@ +import React from 'react'; +import { Switch } from '@patternfly/react-core'; + +class NoLabelSwitch extends React.Component { + state = { + isChecked: true + }; + + handleChange = isChecked => { + this.setState({ isChecked }); + }; + + render() { + const { isChecked } = this.state; + return ( + + ); + } +} + +export default NoLabelSwitch; diff --git a/packages/patternfly-4/react-core/src/components/Switch/examples/SimpleSwitch.js b/packages/patternfly-4/react-core/src/components/Switch/examples/SimpleSwitch.js new file mode 100644 index 00000000000..8572b941a8b --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/examples/SimpleSwitch.js @@ -0,0 +1,27 @@ +import React from 'react'; +import { Switch } from '@patternfly/react-core'; + +class SimpleSwitch extends React.Component { + state = { + isChecked: true + }; + + handleChange = isChecked => { + this.setState({ isChecked }); + }; + + render() { + const { isChecked } = this.state; + return ( + + ); + } +} + +export default SimpleSwitch; diff --git a/packages/patternfly-4/react-core/src/components/Switch/examples/UncontrolledSwitch.js b/packages/patternfly-4/react-core/src/components/Switch/examples/UncontrolledSwitch.js new file mode 100644 index 00000000000..68bcb223e3e --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/examples/UncontrolledSwitch.js @@ -0,0 +1,38 @@ +import React from 'react'; +import { Switch } from '@patternfly/react-core'; + +class UncontrolledSwitch extends React.Component { + render() { + return ( + + +
+ +
+ +
+ +
+ ); + } +} + +export default UncontrolledSwitch; diff --git a/packages/patternfly-4/react-core/src/components/Switch/index.d.ts b/packages/patternfly-4/react-core/src/components/Switch/index.d.ts new file mode 100644 index 00000000000..433b789e089 --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/index.d.ts @@ -0,0 +1 @@ +export { default as Switch, SwitchProps } from './Switch'; diff --git a/packages/patternfly-4/react-core/src/components/Switch/index.js b/packages/patternfly-4/react-core/src/components/Switch/index.js new file mode 100644 index 00000000000..4f1eea554da --- /dev/null +++ b/packages/patternfly-4/react-core/src/components/Switch/index.js @@ -0,0 +1 @@ +export { default as Switch } from './Switch'; diff --git a/packages/patternfly-4/react-core/src/components/index.d.ts b/packages/patternfly-4/react-core/src/components/index.d.ts index 500d7612959..8bac8a25c68 100644 --- a/packages/patternfly-4/react-core/src/components/index.d.ts +++ b/packages/patternfly-4/react-core/src/components/index.d.ts @@ -18,6 +18,7 @@ export * from './Nav'; export * from './Progress'; export * from './Radio'; export * from './Select'; +export * from './Switch'; export * from './TextArea'; export * from './TextInput'; export * from './Title'; diff --git a/packages/patternfly-4/react-core/src/components/index.js b/packages/patternfly-4/react-core/src/components/index.js index 881621ef0ff..1856683851b 100644 --- a/packages/patternfly-4/react-core/src/components/index.js +++ b/packages/patternfly-4/react-core/src/components/index.js @@ -19,6 +19,7 @@ export * from './Nav'; export * from './Progress'; export * from './Radio'; export * from './Select'; +export * from './Switch'; export * from './TextArea'; export * from './TextInput'; export * from './Title';