diff --git a/ui_framework/components/button/__snapshots__/button.test.js.snap b/ui_framework/components/button/__snapshots__/button.test.js.snap
index 76c0cececf9b3..28b1c59bf466e 100644
--- a/ui_framework/components/button/__snapshots__/button.test.js.snap
+++ b/ui_framework/components/button/__snapshots__/button.test.js.snap
@@ -31,16 +31,6 @@ exports[`KuiButton Props className renders the classes 1`] = `
`;
-exports[`KuiButton Props href changes the element from a button to an anchor tag 1`] = `
-
-
-
-`;
-
exports[`KuiButton Props icon is rendered with children 1`] = `
`;
-exports[`KuiButton Props isSubmit changes the element from a button to an input[type=submit] 1`] = `
-
-`;
-
-exports[`KuiButton Props isSubmit doesn't throw an error if it receives string children 1`] = `
-
-`;
-
-exports[`KuiButton Props isSubmit throws an error if it receives icon 1`] = `
-"KuiButton with isSubmit prop set to true can only accept string children, and can't
- display icons. This is because input is a void element and can't contain children."
-`;
-
-exports[`KuiButton Props isSubmit throws an error if it receives non-string children 1`] = `
-"KuiButton with isSubmit prop set to true can only accept string children, and can't
- display icons. This is because input is a void element and can't contain children."
-`;
-
-exports[`KuiButton Props target is rendered 1`] = `
-
-
-
-`;
-
exports[`KuiButton Props testSubject is rendered 1`] = `
+
+
+`;
+
+exports[`KuiLinkButton Props children is rendered 1`] = `
+
+
+
+ Hello
+
+
+
+`;
+
+exports[`KuiLinkButton Props className renders the classes 1`] = `
+
+
+
+`;
+
+exports[`KuiLinkButton Props href is rendered 1`] = `
+
+
+
+`;
+
+exports[`KuiLinkButton Props icon is rendered with children 1`] = `
+
+
+ Icon
+
+ Hello
+
+
+
+`;
+
+exports[`KuiLinkButton Props icon is rendered without children 1`] = `
+
+
+ Icon
+
+
+`;
+
+exports[`KuiLinkButton Props isDisabled sets the disabled attribute 1`] = `
+
+
+
+`;
+
+exports[`KuiLinkButton Props isIconOnRight moves the icon to the right 1`] = `
+
+
+
+ Hello
+
+ Icon
+
+
+`;
+
+exports[`KuiLinkButton Props isLoading doesn't render the icon prop 1`] = `
+
+
+
+
+
+`;
+
+exports[`KuiLinkButton Props isLoading renders a spinner 1`] = `
+
+
+
+
+
+`;
+
+exports[`KuiLinkButton Props target is rendered 1`] = `
+
+
+
+`;
+
+exports[`KuiLinkButton Props testSubject is rendered 1`] = `
+
+
+
+`;
+
+exports[`KuiLinkButton Props type basic renders the basic class 1`] = `
+
+
+
+`;
+
+exports[`KuiLinkButton Props type danger renders the danger class 1`] = `
+
+
+
+`;
+
+exports[`KuiLinkButton Props type hollow renders the hollow class 1`] = `
+
+
+
+`;
+
+exports[`KuiLinkButton Props type primary renders the primary class 1`] = `
+
+
+
+`;
diff --git a/ui_framework/components/button/__snapshots__/submit_button.test.js.snap b/ui_framework/components/button/__snapshots__/submit_button.test.js.snap
new file mode 100644
index 0000000000000..74d182abff555
--- /dev/null
+++ b/ui_framework/components/button/__snapshots__/submit_button.test.js.snap
@@ -0,0 +1,72 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`KuiSubmitButton Baseline is rendered 1`] = `
+
+`;
+
+exports[`KuiSubmitButton Props children is rendered as value 1`] = `
+
+`;
+
+exports[`KuiSubmitButton Props className renders the classes 1`] = `
+
+`;
+
+exports[`KuiSubmitButton Props isDisabled sets the disabled attribute 1`] = `
+
+`;
+
+exports[`KuiSubmitButton Props testSubject is rendered 1`] = `
+
+`;
+
+exports[`KuiSubmitButton Props type basic renders the basic class 1`] = `
+
+`;
+
+exports[`KuiSubmitButton Props type danger renders the danger class 1`] = `
+
+`;
+
+exports[`KuiSubmitButton Props type hollow renders the hollow class 1`] = `
+
+`;
+
+exports[`KuiSubmitButton Props type primary renders the primary class 1`] = `
+
+`;
diff --git a/ui_framework/components/button/button.js b/ui_framework/components/button/button.js
index 6e89fe6fbcad7..28c3f96c82fc1 100644
--- a/ui_framework/components/button/button.js
+++ b/ui_framework/components/button/button.js
@@ -7,12 +7,26 @@ import keyMirror from 'keymirror';
import { KuiButtonIcon } from './button_icon';
-const KuiButton = props => {
- const icon =
- props.isLoading
+const commonPropTypes = {
+ type: PropTypes.string,
+ testSubject: PropTypes.string,
+ isDisabled: PropTypes.bool,
+ onClick: PropTypes.func,
+ data: PropTypes.any,
+ className: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.array,
+ PropTypes.object,
+ ]),
+};
+
+const getIcon = props => (
+ props.isLoading
?
- : props.icon;
+ : props.icon
+);
+const getClassName = (props, icon) => {
const typeToClassNameMap = {
[KuiButton.TYPE.BASIC]: 'kuiButton--basic',
[KuiButton.TYPE.HOLLOW]: 'kuiButton--hollow',
@@ -20,54 +34,18 @@ const KuiButton = props => {
[KuiButton.TYPE.PRIMARY]: 'kuiButton--primary',
};
- const className = classNames('kuiButton', props.className, {
+ return classNames('kuiButton', props.className, {
[typeToClassNameMap[KuiButton.TYPE[props.type]]]: props.type,
'kuiButton--iconText': icon,
});
+};
- let wrappedChildren;
-
- if (props.children) {
- // We need to wrap the text so that the icon's :first-child etc. seudo-selectors get applied
- // correctly.
- wrappedChildren = (
- {props.children}
- );
- }
-
- // onClick is optional, so don't even call it if it's not passed in, or if we're disabled.
- const onClick =
- props.onClick && !props.isDisabled
- ? () => props.onClick(props.data)
- : () => {};
-
- const baseProps = {
- 'data-test-subj': props.testSubject,
- className,
- disabled: props.isDisabled,
- onClick,
- };
-
- if (props.isSubmit) {
- // input is a void element tag and can't have children.
- if ((props.children && typeof props.children !== 'string') || props.icon) {
- throw new Error(
- `KuiButton with isSubmit prop set to true can only accept string children, and can't
- display icons. This is because input is a void element and can't contain children.`
- );
- }
-
- return (
-
- );
- }
+const getChildren = (props, icon) => {
+ // We need to wrap the text so that the icon's :first-child etc. seudo-selectors get applied
+ // correctly.
+ const wrappedChildren = props.children ? {props.children} : undefined;
- const children =
- props.isIconOnRight
+ return props.isIconOnRight
? (
{wrappedChildren}
@@ -79,83 +57,92 @@ const KuiButton = props => {
{wrappedChildren}
);
+};
- if (props.href) {
- return (
-
- {children}
-
- );
- }
+const getOnClick = props => (
+ // onClick is optional, so don't even call it if it's not passed in, or if we're disabled.
+ props.onClick && !props.isDisabled
+ ? () => props.onClick(props.data)
+ : () => {}
+);
+
+const getCommonProps = (props, icon) => ({
+ 'data-test-subj': props.testSubject,
+ className: getClassName(props, icon),
+ onClick: getOnClick(props),
+ disabled: props.isDisabled,
+});
+
+const KuiButton = props => {
+ const icon = getIcon(props);
+ const children = getChildren(props, icon);
+ const commonProps = getCommonProps(props, icon);
return (
-
+
{children}
);
};
-KuiButton.TYPE = keyMirror({
- BASIC: null,
- HOLLOW: null,
- DANGER: null,
- PRIMARY: null,
-});
-
-KuiButton.propTypes = {
- type: PropTypes.string,
- testSubject: PropTypes.string,
+KuiButton.propTypes = Object.assign({}, commonPropTypes, {
icon: PropTypes.node,
isIconOnRight: PropTypes.bool,
children: PropTypes.node,
- isSubmit: PropTypes.bool,
- href: PropTypes.string,
- target: PropTypes.string,
- onClick: PropTypes.func,
- data: PropTypes.any,
- isDisabled: PropTypes.bool,
isLoading: PropTypes.bool,
- className: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.array,
- PropTypes.object,
- ]),
-};
+});
-// function createButtonVariation(hardCodedProps) {
-// const ButtonVariation = props => {
-// return React.createElement(KuiButton, Object.assign({}, props, hardCodedProps));
-// };
+const KuiLinkButton = props => {
+ const icon = getIcon(props);
+ const children = getChildren(props, icon);
+ const commonProps = getCommonProps(props, icon);
-// ButtonVariation.propTypes = Object.assign({}, KuiButton.propTypes);
+ return (
+
+ {children}
+
+ );
+};
-// return ButtonVariation;
-// }
+KuiLinkButton.propTypes = Object.assign({}, commonPropTypes, {
+ href: PropTypes.string,
+ target: PropTypes.string,
+ icon: PropTypes.node,
+ isIconOnRight: PropTypes.bool,
+ children: PropTypes.node,
+ isLoading: PropTypes.bool,
+});
-// const KuiBasicButton = createButtonVariation({
-// className: 'kuiButton--basic',
-// });
+const KuiSubmitButton = props => {
+ const icon = getIcon(props);
+ const commonProps = getCommonProps(props, icon);
-// const KuiHollowButton = createButtonVariation({
-// className: 'kuiButton--hollow',
-// });
+ return (
+
+ );
+};
-// const KuiDangerButton = createButtonVariation({
-// className: 'kuiButton--danger',
-// });
+KuiSubmitButton.propTypes = Object.assign({}, commonPropTypes, {
+ children: PropTypes.string,
+});
-// const KuiPrimaryButton = createButtonVariation({
-// className: 'kuiButton--primary',
-// });
+KuiButton.TYPE = KuiSubmitButton.TYPE = KuiLinkButton.TYPE = keyMirror({
+ BASIC: null,
+ HOLLOW: null,
+ DANGER: null,
+ PRIMARY: null,
+});
export {
KuiButton,
- // KuiBasicButton,
- // KuiHollowButton,
- // KuiDangerButton,
- // KuiPrimaryButton,
+ KuiLinkButton,
+ KuiSubmitButton,
};
diff --git a/ui_framework/components/button/button.test.js b/ui_framework/components/button/button.test.js
index 93e690510512f..252c8942e6a75 100644
--- a/ui_framework/components/button/button.test.js
+++ b/ui_framework/components/button/button.test.js
@@ -127,78 +127,6 @@ describe('KuiButton', () => {
});
});
- describe('isSubmit', () => {
- test('changes the element from a button to an input[type=submit]', () => {
- const $button = shallow(
-
- );
-
- expect($button)
- .toMatchSnapshot();
- });
-
- describe(`doesn't throw an error`, () => {
- test('if it receives string children', () => {
- const $button = shallow(
-
- Hello
-
- );
-
- expect($button)
- .toMatchSnapshot();
- });
- });
-
- describe('throws an error', () => {
- test('if it receives non-string children', () => {
- const $button = () => {
- shallow(
-
- Hello
-
- );
- };
-
- expect($button)
- .toThrowErrorMatchingSnapshot();
- });
-
- test('if it receives icon', () => {
- const $button = () => {
- shallow(
-
- );
- };
-
- expect($button)
- .toThrowErrorMatchingSnapshot();
- });
- });
- });
-
- describe('href', () => {
- test('changes the element from a button to an anchor tag', () => {
- const $button = shallow(
-
- );
-
- expect($button)
- .toMatchSnapshot();
- });
- });
-
- describe('target', () => {
- test('is rendered', () => {
- const $button = shallow(
-
- );
-
- expect($button)
- .toMatchSnapshot();
- });
- });
-
describe('onClick', () => {
test(`isn't called upon instantiation`, () => {
const onClickHandler = sinon.stub();
@@ -217,8 +145,7 @@ describe('KuiButton', () => {
);
- $button.find('button')
- .simulate('click');
+ $button.simulate('click');
sinon.assert.calledOnce(onClickHandler);
});
@@ -231,8 +158,7 @@ describe('KuiButton', () => {
);
- $button.find('button')
- .simulate('click');
+ $button.simulate('click');
sinon.assert.calledWith(onClickHandler, data);
});
@@ -255,8 +181,7 @@ describe('KuiButton', () => {
);
- $button.find('button')
- .simulate('click');
+ $button.simulate('click');
sinon.assert.notCalled(onClickHandler);
});
diff --git a/ui_framework/components/button/index.js b/ui_framework/components/button/index.js
index d0be996b7294e..5ed3264c72ca7 100644
--- a/ui_framework/components/button/index.js
+++ b/ui_framework/components/button/index.js
@@ -1,3 +1,7 @@
-export { KuiButton } from './button';
+export {
+ KuiButton,
+ KuiLinkButton,
+ KuiSubmitButton,
+} from './button';
export { KuiButtonIcon } from './button_icon';
export { KuiButtonGroup } from './button_group';
diff --git a/ui_framework/components/button/link_button.test.js b/ui_framework/components/button/link_button.test.js
new file mode 100644
index 0000000000000..6a645962577e1
--- /dev/null
+++ b/ui_framework/components/button/link_button.test.js
@@ -0,0 +1,243 @@
+import React from 'react';
+import {
+ shallow,
+ render,
+} from 'enzyme';
+import sinon from 'sinon';
+
+import {
+ KuiLinkButton,
+} from './button';
+
+describe('KuiLinkButton', () => {
+ describe('Baseline', () => {
+ test('is rendered', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('Props', () => {
+ describe('type', () => {
+ describe('basic', () => {
+ test('renders the basic class', () => {
+ const $button = render(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('hollow', () => {
+ test('renders the hollow class', () => {
+ const $button = render(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('danger', () => {
+ test('renders the danger class', () => {
+ const $button = render(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('primary', () => {
+ test('renders the primary class', () => {
+ const $button = render(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+ });
+
+ describe('testSubject', () => {
+ test('is rendered', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('icon', () => {
+ test('is rendered with children', () => {
+ const $button = shallow(
+
+ Hello
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+
+ test('is rendered without children', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('isIconOnRight', () => {
+ test('moves the icon to the right', () => {
+ const $button = shallow(
+
+ Hello
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('children', () => {
+ test('is rendered', () => {
+ const $button = shallow(
+
+ Hello
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('href', () => {
+ test('is rendered', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('target', () => {
+ test('is rendered', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('onClick', () => {
+ test(`isn't called upon instantiation`, () => {
+ const onClickHandler = sinon.stub();
+
+ const $button = shallow(
+
+ );
+
+ sinon.assert.notCalled(onClickHandler);
+ });
+
+ test('is called when the button is clicked', () => {
+ const onClickHandler = sinon.stub();
+
+ const $button = shallow(
+
+ );
+
+ $button.simulate('click');
+
+ sinon.assert.calledOnce(onClickHandler);
+ });
+
+ test('receives the data prop', () => {
+ const onClickHandler = sinon.stub();
+ const data = 'data';
+
+ const $button = shallow(
+
+ );
+
+ $button.simulate('click');
+
+ sinon.assert.calledWith(onClickHandler, data);
+ });
+ });
+
+ describe('isDisabled', () => {
+ test('sets the disabled attribute', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+
+ test(`prevents onClick from being called`, () => {
+ const onClickHandler = sinon.stub();
+
+ const $button = shallow(
+
+ );
+
+ $button.simulate('click');
+
+ sinon.assert.notCalled(onClickHandler);
+ });
+ });
+
+ describe('isLoading', () => {
+ test('renders a spinner', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+
+ test(`doesn't render the icon prop`, () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('className', () => {
+ test('renders the classes', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+ });
+});
diff --git a/ui_framework/components/button/submit_button.test.js b/ui_framework/components/button/submit_button.test.js
new file mode 100644
index 0000000000000..58c7c82052410
--- /dev/null
+++ b/ui_framework/components/button/submit_button.test.js
@@ -0,0 +1,166 @@
+import React from 'react';
+import {
+ shallow,
+ render,
+} from 'enzyme';
+import sinon from 'sinon';
+
+import {
+ KuiSubmitButton,
+} from './button';
+
+describe('KuiSubmitButton', () => {
+ describe('Baseline', () => {
+ test('is rendered', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('Props', () => {
+ describe('type', () => {
+ describe('basic', () => {
+ test('renders the basic class', () => {
+ const $button = render(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('hollow', () => {
+ test('renders the hollow class', () => {
+ const $button = render(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('danger', () => {
+ test('renders the danger class', () => {
+ const $button = render(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('primary', () => {
+ test('renders the primary class', () => {
+ const $button = render(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+ });
+
+ describe('testSubject', () => {
+ test('is rendered', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('children', () => {
+ test('is rendered as value', () => {
+ const $button = shallow(
+
+ Hello
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+
+ describe('onClick', () => {
+ test(`isn't called upon instantiation`, () => {
+ const onClickHandler = sinon.stub();
+
+ const $button = shallow(
+
+ );
+
+ sinon.assert.notCalled(onClickHandler);
+ });
+
+ test('is called when the button is clicked', () => {
+ const onClickHandler = sinon.stub();
+
+ const $button = shallow(
+
+ );
+
+ $button.simulate('click');
+
+ sinon.assert.calledOnce(onClickHandler);
+ });
+
+ test('receives the data prop', () => {
+ const onClickHandler = sinon.stub();
+ const data = 'data';
+
+ const $button = shallow(
+
+ );
+
+ $button.simulate('click');
+
+ sinon.assert.calledWith(onClickHandler, data);
+ });
+ });
+
+ describe('isDisabled', () => {
+ test('sets the disabled attribute', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+
+ test(`prevents onClick from being called`, () => {
+ const onClickHandler = sinon.stub();
+
+ const $button = shallow(
+
+ );
+
+ $button.simulate('click');
+
+ sinon.assert.notCalled(onClickHandler);
+ });
+ });
+
+ describe('className', () => {
+ test('renders the classes', () => {
+ const $button = shallow(
+
+ );
+
+ expect($button)
+ .toMatchSnapshot();
+ });
+ });
+ });
+});
diff --git a/ui_framework/components/index.js b/ui_framework/components/index.js
index 5ea582bfcadb3..105bb302e746e 100644
--- a/ui_framework/components/index.js
+++ b/ui_framework/components/index.js
@@ -2,4 +2,6 @@ export {
KuiButton,
KuiButtonGroup,
KuiButtonIcon,
+ KuiLinkButton,
+ KuiSubmitButton,
} from './button';
diff --git a/ui_framework/doc_site/src/views/button/button_elements.js b/ui_framework/doc_site/src/views/button/button_elements.js
index 2fc90b8952434..7eaad1ab41188 100644
--- a/ui_framework/doc_site/src/views/button/button_elements.js
+++ b/ui_framework/doc_site/src/views/button/button_elements.js
@@ -2,6 +2,8 @@ import React from 'react';
import {
KuiButton,
+ KuiLinkButton,
+ KuiSubmitButton,
} from '../../../../components';
export default () => (
@@ -14,21 +16,21 @@ export default () => (
-
Anchor element
-
+
);