[UI Framework] Reactify basic form components (#14374)

* add basic form React components and their tests
* make own subdirectory for each form element
This commit is contained in:
Árpád Poprádi 2017-10-31 23:36:58 +01:00 committed by CJ Cenizal
parent a348ea74e5
commit b38f45b9b1
55 changed files with 1671 additions and 175 deletions

View file

@ -1,21 +0,0 @@
<input type="checkbox" class="kuiCheckBox">
<hr class="guideBreak">
<input type="checkbox" class="kuiCheckBox" checked>
<hr class="guideBreak">
<input type="checkbox" class="kuiCheckBox" checked disabled>
<hr class="guideBreak">
<label class="kuiCheckBoxLabel kuiVerticalRhythm">
<input
class="kuiCheckBox"
type="checkbox"
>
<span class="kuiCheckBoxLabel__text">
With clickable label
</span>
</label>

View file

@ -0,0 +1,51 @@
import React, {
Component,
} from 'react';
import {
KuiCheckBox,
KuiCheckBoxLabel
} from '../../../../components';
class KuiCheckBoxExample extends Component {
state = {
value1: false,
value2: true,
value3: true,
value4: false,
};
handleChange = (event, key) => {
this.setState({ [key]: event.target.checked });
}
render() {
return (
<div>
<KuiCheckBox
isChecked={this.state.value1}
onChange={event => this.handleChange(event, 'value1')}
/>
<hr className="guideBreak"/>
<KuiCheckBox
isChecked={this.state.value2}
onChange={event => this.handleChange(event, 'value2')}
/>
<hr className="guideBreak"/>
<KuiCheckBox
isChecked={this.state.value3}
onChange={event => this.handleChange(event, 'value3')}
isDisabled
/>
<hr className="guideBreak"/>
<KuiCheckBoxLabel
text="With clickable label"
isChecked={this.state.value4}
onChange={event => this.handleChange(event, 'value4')}
/>
</div>
);
}
}
export default KuiCheckBoxExample;

View file

@ -1,4 +1,5 @@
import React from 'react';
import { renderToHtml } from '../../services';
import {
GuideCode,
@ -9,21 +10,44 @@ import {
GuideText,
} from '../../components';
const textInputHtml = require('./text_input.html');
const labelHtml = require('./label.html');
const assistedInputHtml = require('./assisted_input.html');
const searchInputHtml = require('./search_input.html');
const staticInputHtml = require('./static_input.html');
const textAreaHtml = require('./text_area.html');
const textAreaNonResizableHtml = require('./text_area_non_resizable.html');
const checkBoxHtml = require('./check_box.html');
const selectHtml = require('./select.html');
const Label = require('./label');
const labelSource = require('!!raw!./label');
const labelHtml = renderToHtml(Label);
const TextInput = require('./text_input');
const textInputSource = require('!!raw!./text_input');
const textInputHtml = renderToHtml(TextInput, { id: '1' });
const TextArea = require('./text_area');
const textAreaSource = require('!!raw!./text_area');
const textAreaHtml = renderToHtml(TextArea);
const TextAreaNonResizable = require('./text_area_non_resizable');
const textAreaNonResizableSource = require('!!raw!./text_area_non_resizable');
const textAreaNonResizableHtml = renderToHtml(TextAreaNonResizable);
const Select = require('./select');
const selectSource = require('!!raw!./select');
const selectHtml = renderToHtml(Select);
const CheckBox = require('./check_box');
const checkBoxSource = require('!!raw!./check_box');
const checkBoxHtml = renderToHtml(CheckBox);
export default props => (
<GuidePage title={props.route.name}>
<GuideSection
title="Label"
source={[{
type: GuideSectionTypes.JS,
code: labelSource,
}, {
type: GuideSectionTypes.HTML,
code: labelHtml,
}]}
@ -31,9 +55,9 @@ export default props => (
<GuideText>
Never forget to label every input element. You can either
use a <code>label</code> element with a <code>for</code> attribute
referencing the <code>id</code> of the input field, wrap the <code>input</code> field
within the <code>label</code> element or use <code>aria-label</code> or <code>aria-labelledby</code>.
use a <GuideCode>label</GuideCode> element with a <GuideCode>for</GuideCode> attribute
referencing the <GuideCode>id</GuideCode> of the input field, wrap the <GuideCode>input</GuideCode> field
within the <GuideCode>label</GuideCode> element or use <GuideCode>aria-label</GuideCode> or <GuideCode>aria-labelledby</GuideCode>.
</GuideText>
<GuideText>
@ -41,26 +65,29 @@ export default props => (
this page correctly.
</GuideText>
<GuideDemo
html={labelHtml}
/>
<GuideDemo>
<Label/>
</GuideDemo>
</GuideSection>
<GuideSection
title="TextInput"
source={[{
type: GuideSectionTypes.JS,
code: textInputSource,
}, {
type: GuideSectionTypes.HTML,
code: textInputHtml,
}]}
>
<GuideDemo
html={textInputHtml}
/>
<GuideDemo>
<TextInput/>
</GuideDemo>
<GuideDemo
html={textInputHtml}
isDarkTheme
/>
<GuideDemo isDarkTheme={true}>
<TextInput/>
</GuideDemo>
</GuideSection>
<GuideSection
@ -123,64 +150,73 @@ export default props => (
<GuideSection
title="TextArea"
source={[{
type: GuideSectionTypes.JS,
code: textAreaSource,
}, {
type: GuideSectionTypes.HTML,
code: textAreaHtml,
}]}
>
<GuideDemo
html={textAreaHtml}
/>
<GuideDemo>
<TextArea/>
</GuideDemo>
<GuideDemo
html={textAreaHtml}
isDarkTheme
/>
<GuideDemo isDarkTheme={true}>
<TextArea/>
</GuideDemo>
</GuideSection>
<GuideSection
title="TextArea, non-resizable"
source={[{
type: GuideSectionTypes.JS,
code: textAreaNonResizableSource,
}, {
type: GuideSectionTypes.HTML,
code: textAreaNonResizableHtml,
}]}
>
<GuideDemo
html={textAreaNonResizableHtml}
/>
<GuideDemo>
<TextAreaNonResizable/>
</GuideDemo>
</GuideSection>
<GuideSection
title="CheckBox"
source={[{
type: GuideSectionTypes.JS,
code: checkBoxSource,
}, {
type: GuideSectionTypes.HTML,
code: checkBoxHtml,
}]}
>
<GuideDemo
html={checkBoxHtml}
/>
<GuideDemo>
<CheckBox/>
</GuideDemo>
<GuideDemo
html={checkBoxHtml}
isDarkTheme
/>
<GuideDemo isDarkTheme={true}>
<CheckBox/>
</GuideDemo>
</GuideSection>
<GuideSection
title="Select"
source={[{
type: GuideSectionTypes.JS,
code: selectSource,
}, {
type: GuideSectionTypes.HTML,
code: selectHtml,
}]}
>
<GuideDemo
html={selectHtml}
/>
<GuideDemo>
<Select/>
</GuideDemo>
<GuideDemo
html={selectHtml}
isDarkTheme
/>
<GuideDemo isDarkTheme={true}>
<Select/>
</GuideDemo>
</GuideSection>
</GuidePage>
);

View file

@ -1,3 +0,0 @@
<label class="kuiLabel">
Label
</label>

View file

@ -0,0 +1,10 @@
import React from 'react';
import {
KuiLabel,
} from '../../../../components';
const KuiLabelExample = () => {
return <KuiLabel>Label</KuiLabel>;
};
export default KuiLabelExample;

View file

@ -1,29 +0,0 @@
<select class="kuiSelect">
<option>Apple</option>
<option>Bread</option>
<option>Cheese</option>
</select>
<hr class="guideBreak">
<select class="kuiSelect" disabled>
<option>Disabled</option>
</select>
<hr class="guideBreak">
<select class="kuiSelect kuiSelect-isInvalid">
<option>Invalid</option>
</select>
<hr class="guideBreak">
<select class="kuiSelect kuiSelect--small">
<option>Small</option>
</select>
<hr class="guideBreak">
<select class="kuiSelect kuiSelect--large">
<option>Large</option>
</select>

View file

@ -0,0 +1,69 @@
import React, {
Component,
} from 'react';
import {
KuiSelect,
} from '../../../../components';
class KuiSelectExample extends Component {
state = {
value1: '',
value2: '',
value3: '',
value4: '',
value5: '',
};
handleChange = (event, key) => {
this.setState({ [key]: event.target.value });
}
render() {
return (
<div>
<KuiSelect
value={this.state.value1}
onChange={event => this.handleChange(event, 'value1')}
>
<option value="apple" >Apple</option>
<option value="bread" >Bread</option>
<option value="cheese" >Cheese</option>
</KuiSelect>
<hr className="guideBreak"/>
<KuiSelect
value={this.state.value2}
onChange={event => this.handleChange(event, 'value2')}
isDisabled
>
<option>Disabled</option>
</KuiSelect>
<hr className="guideBreak"/>
<KuiSelect
value={this.state.value3}
onChange={event => this.handleChange(event, 'value3')}
isInvalid
>
<option>Invalid</option>
</KuiSelect>
<hr className="guideBreak"/>
<KuiSelect
value={this.state.value4}
onChange={event => this.handleChange(event, 'value4')}
size="small"
>
<option>Small</option>
</KuiSelect>
<hr className="guideBreak"/>
<KuiSelect
value={this.state.value5}
onChange={event => this.handleChange(event, 'value5')}
size="large"
>
<option>Large</option>
</KuiSelect>
</div>
);
}
}
export default KuiSelectExample;

View file

@ -1,47 +0,0 @@
<textarea
type="text"
class="kuiTextArea"
placeholder="Placeholder text"
></textarea>
<hr class="guideBreak">
<textarea
type="text"
class="kuiTextArea"
>
Entered text
</textarea>
<hr class="guideBreak">
<textarea
type="text"
class="kuiTextArea kuiTextArea-isInvalid"
></textarea>
<hr class="guideBreak">
<textarea
type="text"
class="kuiTextArea"
disabled
>
Disabled
</textarea>
<hr class="guideBreak">
<textarea
type="text"
class="kuiTextArea kuiTextArea--small"
placeholder="Small"
></textarea>
<hr class="guideBreak">
<textarea
type="text"
class="kuiTextArea kuiTextArea--large"
placeholder="Large"
></textarea>

View file

@ -0,0 +1,66 @@
import React, {
Component,
} from 'react';
import {
KuiTextArea,
} from '../../../../components';
class KuiTextAreaExample extends Component {
state = {
value1: '',
value2: 'Entered text',
value3: '',
value4: 'Disabled',
value5: '',
value6: '',
};
handleChange = (event, key) => {
this.setState({ [key]: event.target.value });
}
render() {
return (
<div>
<KuiTextArea
placeholder="Placeholder text"
value={this.state.value1}
onChange={event => this.handleChange(event, 'value1')}
/>
<hr className="guideBreak"/>
<KuiTextArea
value={this.state.value2}
onChange={event => this.handleChange(event, 'value2')}
/>
<hr className="guideBreak"/>
<KuiTextArea
isInvalid
value={this.state.value3}
onChange={event => this.handleChange(event, 'value3')}
/>
<hr className="guideBreak"/>
<KuiTextArea
isDisabled
value={this.state.value4}
onChange={event => this.handleChange(event, 'value4')}
/>
<hr className="guideBreak"/>
<KuiTextArea
placeholder="Small"
value={this.state.value5}
size="small"
onChange={event => this.handleChange(event, 'value5')}
/>
<hr className="guideBreak"/>
<KuiTextArea
placeholder="Large"
value={this.state.value6}
size="large"
onChange={event => this.handleChange(event, 'value6')}
/>
</div>
);
}
}
export default KuiTextAreaExample;

View file

@ -1,3 +0,0 @@
<textarea type="text" class="kuiTextArea kuiTextArea--nonResizable">
Non-resizable
</textarea>

View file

@ -0,0 +1,28 @@
import React, {
Component,
} from 'react';
import {
KuiTextArea,
} from '../../../../components';
class KuiTextAreaNonResizableExample extends Component {
state = {
value1: 'Non-resizable',
};
handleChange = (event, key) => {
this.setState({ [key]: event.target.value });
}
render() {
return (
<KuiTextArea
value={this.state.value1}
onChange={event => this.handleChange(event, 'value1')}
isNonResizable
/>
);
}
}
export default KuiTextAreaNonResizableExample;

View file

@ -1,21 +0,0 @@
<input type="text" class="kuiTextInput" placeholder="Placeholder text">
<hr class="guideBreak">
<input type="text" class="kuiTextInput" value="Entered text" autofocus>
<hr class="guideBreak">
<input type="text" class="kuiTextInput kuiTextInput-isInvalid">
<hr class="guideBreak">
<input type="text" class="kuiTextInput" value="Disabled" disabled>
<hr class="guideBreak">
<input type="text" class="kuiTextInput kuiTextInput--small" placeholder="Small">
<hr class="guideBreak">
<input type="text" class="kuiTextInput kuiTextInput--large" placeholder="Large">

View file

@ -0,0 +1,67 @@
import React, {
Component,
} from 'react';
import {
KuiTextInput,
} from '../../../../components';
class KuiTextInputExample extends Component {
state = {
value1: '',
value2: 'Entered text',
value3: '',
value4: 'Disabled',
value5: '',
value6: '',
};
handleChange = (event, key) => {
this.setState({ [key]: event.target.value });
}
render() {
return (
<div>
<KuiTextInput
value={this.state.value1}
placeholder="Placeholder text"
onChange={event => this.handleChange(event, 'value1')}
/>
<hr className="guideBreak"/>
<KuiTextInput
value={this.state.value2}
autoFocus
onChange={event => this.handleChange(event, 'value2')}
/>
<hr className="guideBreak"/>
<KuiTextInput
value={this.state.value3}
isInvalid
onChange={event => this.handleChange(event, 'value3')}
/>
<hr className="guideBreak"/>
<KuiTextInput
value={this.state.value4}
isDisabled
onChange={event => this.handleChange(event, 'value4')}
/>
<hr className="guideBreak"/>
<KuiTextInput
value={this.state.value5}
size="small"
placeholder="Small"
onChange={event => this.handleChange(event, 'value5')}
/>
<hr className="guideBreak"/>
<KuiTextInput
value={this.state.value6}
size="large"
placeholder="Large"
onChange={event => this.handleChange(event, 'value6')}
/>
</div>
);
}
}
export default KuiTextInputExample;

View file

@ -1,8 +1,8 @@
@import "assisted_input";
@import "check_box";
@import "label";
@import "search_input";
@import "select";
@import "static_input";
@import "text_area";
@import "text_input";
@import "assisted_input/index";
@import "check_box/index";
@import "label/index";
@import "search_input/index";
@import "select/index";
@import "static_input/index";
@import "text_area/index";
@import "text_input/index";

View file

@ -0,0 +1 @@
@import "assisted_input";

View file

@ -0,0 +1,40 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`KuiCheckBox Props isChecked false renders unchecked 1`] = `
<input
class="kuiCheckBox"
type="checkbox"
/>
`;
exports[`KuiCheckBox Props isChecked true renders checked 1`] = `
<input
checked=""
class="kuiCheckBox"
type="checkbox"
/>
`;
exports[`KuiCheckBox Props isDisabled false renders enabled 1`] = `
<input
class="kuiCheckBox"
type="checkbox"
/>
`;
exports[`KuiCheckBox Props isDisabled true renders disabled 1`] = `
<input
class="kuiCheckBox"
disabled=""
type="checkbox"
/>
`;
exports[`KuiCheckBox renders 1`] = `
<input
aria-label="aria-label"
class="kuiCheckBox testClass1 testClass2"
data-test-subj="test subject string"
type="checkbox"
/>
`;

View file

@ -0,0 +1,91 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`KuiCheckBoxLabel Props isChecked false renders unchecked 1`] = `
<label
class="kuiCheckBoxLabel"
>
<input
class="kuiCheckBox"
type="checkbox"
/>
<span
class="kuiCheckBoxLabel__text"
/>
</label>
`;
exports[`KuiCheckBoxLabel Props isChecked true renders checked 1`] = `
<label
class="kuiCheckBoxLabel"
>
<input
checked=""
class="kuiCheckBox"
type="checkbox"
/>
<span
class="kuiCheckBoxLabel__text"
/>
</label>
`;
exports[`KuiCheckBoxLabel Props isDisabled false renders enabled 1`] = `
<label
class="kuiCheckBoxLabel"
>
<input
class="kuiCheckBox"
type="checkbox"
/>
<span
class="kuiCheckBoxLabel__text"
/>
</label>
`;
exports[`KuiCheckBoxLabel Props isDisabled true renders disabled 1`] = `
<label
class="kuiCheckBoxLabel"
>
<input
class="kuiCheckBox"
disabled=""
type="checkbox"
/>
<span
class="kuiCheckBoxLabel__text"
/>
</label>
`;
exports[`KuiCheckBoxLabel Props text 1`] = `
<label
class="kuiCheckBoxLabel"
>
<input
class="kuiCheckBox"
type="checkbox"
/>
<span
class="kuiCheckBoxLabel__text"
>
text
</span>
</label>
`;
exports[`KuiCheckBoxLabel renders 1`] = `
<label
aria-label="aria-label"
class="kuiCheckBoxLabel testClass1 testClass2"
data-test-subj="test subject string"
>
<input
class="kuiCheckBox"
type="checkbox"
/>
<span
class="kuiCheckBoxLabel__text"
/>
</label>
`;

View file

@ -0,0 +1 @@
@import "check_box";

View file

@ -0,0 +1,36 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
export const KuiCheckBox = ({
className,
isChecked,
isDisabled,
onChange,
...rest
}) => {
const classes = classNames('kuiCheckBox', className);
return (
<input
type="checkbox"
className={classes}
checked={isChecked}
disabled={isDisabled}
onChange={onChange}
{...rest}
/>
);
};
KuiCheckBox.defaultProps = {
isChecked: false,
isDisabled: false,
};
KuiCheckBox.propTypes = {
className: PropTypes.string,
isChecked: PropTypes.bool,
isDisabled: PropTypes.bool,
onChange: PropTypes.func.isRequired,
};

View file

@ -0,0 +1,86 @@
import React from 'react';
import { render, shallow } from 'enzyme';
import { requiredProps } from '../../../test/required_props';
import sinon from 'sinon';
import {
KuiCheckBox,
} from './check_box';
describe('KuiCheckBox', () => {
test('renders', () => {
const component = (
<KuiCheckBox
onChange={()=>{}}
{...requiredProps}
/>
);
expect(render(component)).toMatchSnapshot();
});
describe('Props', () => {
describe('isChecked', () => {
test('true renders checked', () => {
const component = (
<KuiCheckBox
isChecked
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders unchecked', () => {
const component = (
<KuiCheckBox
isChecked={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('isDisabled', () => {
test('true renders disabled', () => {
const component = (
<KuiCheckBox
isDisabled
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders enabled', () => {
const component = (
<KuiCheckBox
isDisabled={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('onChange', () => {
test(`is called when checkbox is changed`, () => {
const onChangeHandler = sinon.spy();
const wrapper = shallow(
<KuiCheckBox
onChange={onChangeHandler}
/>
);
wrapper.simulate('change');
sinon.assert.calledOnce(onChangeHandler);
});
});
});
});

View file

@ -0,0 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { KuiCheckBox } from './check_box';
export const KuiCheckBoxLabel = ({
className,
text,
isChecked,
isDisabled,
onChange,
...rest
}) => {
const classes = classNames('kuiCheckBoxLabel', className);
return (
<label
className={classes}
{...rest}
>
<KuiCheckBox
isChecked={isChecked}
isDisabled={isDisabled}
onChange={onChange}
/>
<span className="kuiCheckBoxLabel__text">
{text}
</span>
</label>
);
};
KuiCheckBoxLabel.defaultProps = {
isChecked: false,
isDisabled: false,
};
KuiCheckBoxLabel.propTypes = {
className: PropTypes.string,
text: PropTypes.string,
isChecked: PropTypes.bool,
isDisabled: PropTypes.bool,
onChange: PropTypes.func.isRequired,
};

View file

@ -0,0 +1,97 @@
import React from 'react';
import { render, shallow } from 'enzyme';
import { requiredProps } from '../../../test/required_props';
import sinon from 'sinon';
import {
KuiCheckBoxLabel,
} from './check_box_label';
describe('KuiCheckBoxLabel', () => {
test('renders', () => {
const component = (
<KuiCheckBoxLabel
onChange={()=>{}}
{...requiredProps}
/>
);
expect(render(component)).toMatchSnapshot();
});
describe('Props', () => {
test('text', () => {
const component = (
<KuiCheckBoxLabel
text="text"
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
describe('isChecked', () => {
test('true renders checked', () => {
const component = (
<KuiCheckBoxLabel
isChecked
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders unchecked', () => {
const component = (
<KuiCheckBoxLabel
isChecked={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('isDisabled', () => {
test('true renders disabled', () => {
const component = (
<KuiCheckBoxLabel
isDisabled
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders enabled', () => {
const component = (
<KuiCheckBoxLabel
isDisabled={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('onChange', () => {
test(`is called when checkbox is changed`, () => {
const onChangeHandler = sinon.spy();
const wrapper = shallow(
<KuiCheckBoxLabel
onChange={onChangeHandler}
/>
);
wrapper.find('KuiCheckBox').simulate('change');
sinon.assert.calledOnce(onChangeHandler);
});
});
});
});

View file

@ -0,0 +1,2 @@
export { KuiCheckBox } from './check_box';
export { KuiCheckBoxLabel } from './check_box_label';

View file

@ -0,0 +1,5 @@
export { KuiLabel } from './label';
export { KuiTextInput } from './text_input';
export { KuiTextArea } from './text_area';
export { KuiSelect } from './select';
export { KuiCheckBox, KuiCheckBoxLabel } from './check_box';

View file

@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`KuiLabel renders 1`] = `
<label
aria-label="aria-label"
class="kuiLabel testClass1 testClass2"
data-test-subj="test subject string"
>
label
</label>
`;

View file

@ -0,0 +1 @@
@import "label";

View file

@ -0,0 +1 @@
export { KuiLabel } from './label';

View file

@ -0,0 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
export const KuiLabel = ({
className,
children,
...rest
}) => {
const classes = classNames('kuiLabel', className);
return (
<label
className={classes}
{...rest}
>
{children}
</label>
);
};
KuiLabel.propTypes = {
className: PropTypes.string,
children: PropTypes.node,
};

View file

@ -0,0 +1,19 @@
import React from 'react';
import { render } from 'enzyme';
import { requiredProps } from '../../../test/required_props';
import {
KuiLabel
} from './label';
describe('KuiLabel', () => {
test('renders', () => {
const component = (
<KuiLabel {...requiredProps}>
{'label'}
</KuiLabel>
);
expect(render(component)).toMatchSnapshot();
});
});

View file

@ -0,0 +1 @@
@import "search_input";

View file

@ -0,0 +1,81 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`KuiSelect Props isDisabled false renders enabled 1`] = `
<select
class="kuiSelect"
/>
`;
exports[`KuiSelect Props isDisabled true renders disabled 1`] = `
<select
class="kuiSelect"
disabled=""
/>
`;
exports[`KuiSelect Props isInvalid false renders valid 1`] = `
<select
class="kuiSelect"
/>
`;
exports[`KuiSelect Props isInvalid true renders invalid 1`] = `
<select
class="kuiSelect kuiSelect-isInvalid"
/>
`;
exports[`KuiSelect Props size renders large 1`] = `
<select
class="kuiSelect kuiSelect--large"
/>
`;
exports[`KuiSelect Props size renders medium 1`] = `
<select
class="kuiSelect"
/>
`;
exports[`KuiSelect Props size renders small 1`] = `
<select
class="kuiSelect kuiSelect--small"
/>
`;
exports[`KuiSelect Props value 1`] = `
<select
class="kuiSelect"
>
<option
selected=""
value="apple"
>
Apple
</option>
<option
value="bread"
>
Bread
</option>
</select>
`;
exports[`KuiSelect renders 1`] = `
<select
aria-label="aria-label"
class="kuiSelect testClass1 testClass2"
data-test-subj="test subject string"
>
<option
value="apple"
>
Apple
</option>
<option
value="bread"
>
Bread
</option>
</select>
`;

View file

@ -0,0 +1 @@
@import "select";

View file

@ -0,0 +1 @@
export { KuiSelect } from './select';

View file

@ -0,0 +1,51 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
const sizeToClassNameMap = {
small: 'kuiSelect--small',
medium: undefined,
large: 'kuiSelect--large',
};
export const SELECT_SIZE = Object.keys(sizeToClassNameMap);
export const KuiSelect = ({
className,
onChange,
isInvalid,
isDisabled,
size,
children,
...rest
}) => {
const classes = classNames('kuiSelect', className, {
'kuiSelect-isInvalid': isInvalid
}, sizeToClassNameMap[size]);
return (
<select
className={classes}
onChange={onChange}
disabled={isDisabled}
{...rest}
>
{children}
</select>
);
};
KuiSelect.defaultProps = {
isInvalid: false,
isDisabled: false,
size: 'medium'
};
KuiSelect.propTypes = {
className: PropTypes.string,
onChange: PropTypes.func.isRequired,
isInvalid: PropTypes.bool,
isDisabled: PropTypes.bool,
size: PropTypes.oneOf(SELECT_SIZE),
children: PropTypes.node
};

View file

@ -0,0 +1,122 @@
import React from 'react';
import { render, shallow } from 'enzyme';
import { requiredProps } from '../../../test/required_props';
import sinon from 'sinon';
import {
KuiSelect,
SELECT_SIZE
} from './select';
describe('KuiSelect', () => {
test('renders', () => {
const component = (
<KuiSelect
onChange={()=>{}}
{...requiredProps}
>
<option value="apple" >Apple</option>
<option value="bread" >Bread</option>
</KuiSelect>
);
expect(render(component)).toMatchSnapshot();
});
describe('Props', () => {
test('value', () => {
const component = (
<KuiSelect
value="apple"
onChange={()=>{}}
>
<option value="apple" >Apple</option>
<option value="bread" >Bread</option>
</KuiSelect>
);
expect(render(component)).toMatchSnapshot();
});
describe('isInvalid', () => {
test('true renders invalid', () => {
const component = (
<KuiSelect
isInvalid
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders valid', () => {
const component = (
<KuiSelect
isInvalid={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('isDisabled', () => {
test('true renders disabled', () => {
const component = (
<KuiSelect
isDisabled
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders enabled', () => {
const component = (
<KuiSelect
isDisabled={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('size', () => {
SELECT_SIZE.forEach(size=>{
test(`renders ${size}`, () => {
const component = (
<KuiSelect
size={size}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
});
describe('onChange', () => {
test(`is called when an option is selected`, () => {
const onChangeHandler = sinon.spy();
const wrapper = shallow(
<KuiSelect
onChange={onChangeHandler}
>
<option value="apple" >Apple</option>
<option value="bread" >Bread</option>
</KuiSelect>
);
wrapper.simulate('change', 'bread');
sinon.assert.calledOnce(onChangeHandler);
});
});
});
});

View file

@ -0,0 +1 @@
@import "static_input";

View file

@ -0,0 +1,81 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`KuiTextArea Props isDisabled false renders enabled 1`] = `
<textarea
class="kuiTextArea"
/>
`;
exports[`KuiTextArea Props isDisabled true renders disabled 1`] = `
<textarea
class="kuiTextArea"
disabled=""
/>
`;
exports[`KuiTextArea Props isInvalid false renders valid 1`] = `
<textarea
class="kuiTextArea"
/>
`;
exports[`KuiTextArea Props isInvalid true renders invalid 1`] = `
<textarea
class="kuiTextArea kuiTextArea-isInvalid"
/>
`;
exports[`KuiTextArea Props isNonResizable false renders resizable 1`] = `
<textarea
class="kuiTextArea"
/>
`;
exports[`KuiTextArea Props isNonResizable true renders non-resizable 1`] = `
<textarea
class="kuiTextArea kuiTextArea--nonResizable"
/>
`;
exports[`KuiTextArea Props placeholder 1`] = `
<textarea
class="kuiTextArea"
placeholder="placeholder"
/>
`;
exports[`KuiTextArea Props size renders large 1`] = `
<textarea
class="kuiTextArea kuiTextArea--large"
/>
`;
exports[`KuiTextArea Props size renders medium 1`] = `
<textarea
class="kuiTextArea"
/>
`;
exports[`KuiTextArea Props size renders small 1`] = `
<textarea
class="kuiTextArea kuiTextArea--small"
/>
`;
exports[`KuiTextArea Props value 1`] = `
<textarea
class="kuiTextArea"
>
value
</textarea>
`;
exports[`KuiTextArea renders 1`] = `
<textarea
aria-label="aria-label"
class="kuiTextArea testClass1 testClass2"
data-test-subj="test subject string"
>
text area
</textarea>
`;

View file

@ -0,0 +1 @@
@import "text_area";

View file

@ -0,0 +1 @@
export { KuiTextArea } from './text_area';

View file

@ -0,0 +1,52 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
const sizeToClassNameMap = {
small: 'kuiTextArea--small',
medium: undefined,
large: 'kuiTextArea--large',
};
export const TEXTAREA_SIZE = Object.keys(sizeToClassNameMap);
export const KuiTextArea = ({
className,
onChange,
isInvalid,
isNonResizable,
isDisabled,
size,
...rest
}) => {
const classes = classNames('kuiTextArea', className, {
'kuiTextArea-isInvalid': isInvalid,
'kuiTextArea--nonResizable': isNonResizable
}, sizeToClassNameMap[size]
);
return (
<textarea
className={classes}
onChange={onChange}
disabled={isDisabled}
{...rest}
/>
);
};
KuiTextArea.defaultProps = {
isInvalid: false,
isNonResizable: false,
isDisabled: false,
size: 'medium'
};
KuiTextArea.propTypes = {
className: PropTypes.string,
onChange: PropTypes.func.isRequired,
isInvalid: PropTypes.bool,
isNonResizable: PropTypes.bool,
isDisabled: PropTypes.bool,
size: PropTypes.oneOf(TEXTAREA_SIZE)
};

View file

@ -0,0 +1,149 @@
import React from 'react';
import { render, shallow } from 'enzyme';
import { requiredProps } from '../../../test/required_props';
import sinon from 'sinon';
import {
KuiTextArea,
TEXTAREA_SIZE
} from './text_area';
describe('KuiTextArea', () => {
test('renders', () => {
const component = (
<KuiTextArea
value="text area"
onChange={()=>{}}
{...requiredProps}
/>
);
expect(render(component)).toMatchSnapshot();
});
describe('Props', () => {
test('placeholder', () => {
const component = (
<KuiTextArea
placeholder="placeholder"
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('value', () => {
const component = (
<KuiTextArea
value="value"
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
describe('isInvalid', () => {
test('true renders invalid', () => {
const component = (
<KuiTextArea
isInvalid
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders valid', () => {
const component = (
<KuiTextArea
isInvalid={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('isNonResizable', () => {
test('true renders non-resizable', () => {
const component = (
<KuiTextArea
isNonResizable
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders resizable', () => {
const component = (
<KuiTextArea
isNonResizable={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('isDisabled', () => {
test('true renders disabled', () => {
const component = (
<KuiTextArea
isDisabled
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders enabled', () => {
const component = (
<KuiTextArea
isDisabled={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('size', () => {
TEXTAREA_SIZE.forEach(size=>{
test(`renders ${size}`, () => {
const component = (
<KuiTextArea
size={size}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
});
describe('onChange', () => {
test(`is called when textarea is written`, () => {
const onChangeHandler = sinon.spy();
const wrapper = shallow(
<KuiTextArea
onChange={onChangeHandler}
/>
);
wrapper.simulate('change');
sinon.assert.calledOnce(onChangeHandler);
});
});
});
});

View file

@ -0,0 +1,77 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`KuiTextInput Props isDisabled false renders enabled 1`] = `
<input
class="kuiTextInput"
type="text"
/>
`;
exports[`KuiTextInput Props isDisabled true renders disabled 1`] = `
<input
class="kuiTextInput"
disabled=""
type="text"
/>
`;
exports[`KuiTextInput Props isInvalid false renders valid 1`] = `
<input
class="kuiTextInput"
type="text"
/>
`;
exports[`KuiTextInput Props isInvalid true renders invalid 1`] = `
<input
class="kuiTextInput kuiTextInput-isInvalid"
type="text"
/>
`;
exports[`KuiTextInput Props placeholder 1`] = `
<input
class="kuiTextInput"
placeholder="placeholder"
type="text"
/>
`;
exports[`KuiTextInput Props size renders large 1`] = `
<input
class="kuiTextInput kuiTextInput--large"
type="text"
/>
`;
exports[`KuiTextInput Props size renders medium 1`] = `
<input
class="kuiTextInput"
type="text"
/>
`;
exports[`KuiTextInput Props size renders small 1`] = `
<input
class="kuiTextInput kuiTextInput--small"
type="text"
/>
`;
exports[`KuiTextInput Props value 1`] = `
<input
class="kuiTextInput"
type="text"
value="value"
/>
`;
exports[`KuiTextInput renders 1`] = `
<input
aria-label="aria-label"
class="kuiTextInput testClass1 testClass2"
data-test-subj="test subject string"
type="text"
value="text input"
/>
`;

View file

@ -0,0 +1 @@
@import "text_input";

View file

@ -0,0 +1 @@
export { KuiTextInput } from './text_input';

View file

@ -0,0 +1,49 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
const sizeToClassNameMap = {
small: 'kuiTextInput--small',
medium: undefined,
large: 'kuiTextInput--large',
};
export const TEXTINPUT_SIZE = Object.keys(sizeToClassNameMap);
export const KuiTextInput = ({
className,
onChange,
isInvalid,
isDisabled,
size,
...rest
}) => {
const classes = classNames('kuiTextInput', className, {
'kuiTextInput-isInvalid': isInvalid
}, sizeToClassNameMap[size]
);
return (
<input
type="text"
className={classes}
onChange={onChange}
disabled={isDisabled}
{...rest}
/>
);
};
KuiTextInput.defaultProps = {
isInvalid: false,
isDisabled: false,
size: 'medium'
};
KuiTextInput.propTypes = {
className: PropTypes.string,
onChange: PropTypes.func.isRequired,
isInvalid: PropTypes.bool,
isDisabled: PropTypes.bool,
size: PropTypes.oneOf(TEXTINPUT_SIZE)
};

View file

@ -0,0 +1,154 @@
import React from 'react';
import { render, shallow, mount } from 'enzyme';
import { requiredProps } from '../../../test/required_props';
import sinon from 'sinon';
import {
KuiTextInput,
TEXTINPUT_SIZE
} from './text_input';
describe('KuiTextInput', () => {
test('renders', () => {
const component = (
<KuiTextInput
value="text input"
onChange={()=>{}}
{...requiredProps}
/>
);
expect(render(component)).toMatchSnapshot();
});
describe('Props', () => {
test('placeholder', () => {
const component = (
<KuiTextInput
placeholder="placeholder"
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('value', () => {
const component = (
<KuiTextInput
value="value"
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
describe('autoFocus', () => {
test('sets focus on the element', () => {
const component = mount(
<KuiTextInput
autoFocus
onChange={()=>{}}
data-test-subj="input"
/>
);
expect(
component.find('[data-test-subj="input"]').matchesElement(document.activeElement)
).toBe(true);
});
test('does not focus the element by default', () => {
const component = mount(
<KuiTextInput
onChange={()=>{}}
data-test-subj="input"
/>
);
expect(
component.find('[data-test-subj="input"]').matchesElement(document.activeElement)
).toBe(false);
});
});
describe('isInvalid', () => {
test('true renders invalid', () => {
const component = (
<KuiTextInput
isInvalid
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders valid', () => {
const component = (
<KuiTextInput
isInvalid={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('isDisabled', () => {
test('true renders disabled', () => {
const component = (
<KuiTextInput
isDisabled
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
test('false renders enabled', () => {
const component = (
<KuiTextInput
isDisabled={false}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
describe('size', () => {
TEXTINPUT_SIZE.forEach(size=>{
test(`renders ${size}`, () => {
const component = (
<KuiTextInput
size={size}
onChange={()=>{}}
/>
);
expect(render(component)).toMatchSnapshot();
});
});
});
describe('onChange', () => {
test(`is called when input is changed`, () => {
const onChangeHandler = sinon.spy();
const wrapper = shallow(
<KuiTextInput
onChange={onChangeHandler}
/>
);
wrapper.simulate('change');
sinon.assert.calledOnce(onChangeHandler);
});
});
});
});

View file

@ -90,6 +90,15 @@ export {
export { KuiInfoButton } from './info_button';
export {
KuiLabel,
KuiTextInput,
KuiTextArea,
KuiSelect,
KuiCheckBox,
KuiCheckBoxLabel,
} from './form';
export {
KuiLocalNav,
KuiLocalNavRow,