diff --git a/ui_framework/dist/ui_framework.css b/ui_framework/dist/ui_framework.css
index aae083006bf4b..3b1d18e63275b 100644
--- a/ui_framework/dist/ui_framework.css
+++ b/ui_framework/dist/ui_framework.css
@@ -491,8 +491,8 @@ table {
transform: translateY(2px); } }
.kuiCallOut {
- padding: 12px 24px;
- border-left: 4px solid transparent; }
+ padding: 16px;
+ border-left: 2px solid transparent; }
.kuiCallOut--info {
border-color: #0079a5;
@@ -528,13 +528,13 @@ table {
/**
* 1. Align icon with first line of title text if it wraps.
- * 2. Apply margin to all but last item in the flex.
+ * 2. If content exists under the header, space it appropriately.
+ * 3. Apply margin to all but last item in the flex.
*/
.kuiCallOutHeader {
font-size: 16px;
font-size: 1rem;
line-height: 24px;
- margin-bottom: 9px;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
@@ -544,9 +544,12 @@ table {
-ms-flex-align: baseline;
align-items: baseline;
/* 1 */ }
+ .kuiCallOutHeader + * {
+ margin-top: 8px;
+ /* 1 */ }
.kuiCallOutHeader > * + * {
margin-left: 8px;
- /* 2 */ }
+ /* 3 */ }
/**
* 1. Vertically center icon with first line of title.
@@ -560,6 +563,389 @@ table {
transform: translateY(2px);
/* 1 */ }
+.kuiForm__error {
+ font-size: 14px;
+ font-size: 0.875rem;
+ line-height: 18px;
+ list-style: disc;
+ margin-left: 32px; }
+
+.kuiForm__errors {
+ margin-bottom: 16px; }
+
+.kuiFormRow {
+ display: -webkit-box;
+ display: -webkit-flex;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-orient: vertical;
+ -webkit-box-direction: normal;
+ -webkit-flex-direction: column;
+ -ms-flex-direction: column;
+ flex-direction: column;
+ position: relative;
+ max-width: 400px; }
+ .kuiFormRow + * {
+ margin-top: 24px; }
+ .kuiFormRow.kuiFormRow--withIcon input {
+ padding-left: 40px; }
+ .kuiFormRow.kuiFormRow--dropdown input {
+ padding-left: 12px;
+ padding-right: 40px; }
+ .kuiFormRow.kuiFormRow--select .kuiFormRow__icon {
+ left: auto;
+ right: 12px; }
+ .kuiFormRow .kuiFormRow__label {
+ font-size: 12px;
+ padding-bottom: 8px;
+ cursor: pointer;
+ transition: all 150ms cubic-bezier(0.694, 0.0482, 0.335, 1);
+ font-weight: 500;
+ -webkit-box-ordinal-group: 0;
+ -webkit-order: -1;
+ -ms-flex-order: -1;
+ order: -1; }
+ .kuiFormRow .kuiFormRow__helpText {
+ font-size: 12px;
+ padding: 8px 0;
+ color: #666; }
+ .kuiFormRow .kuiFormRow__error {
+ font-size: 12px;
+ padding: 8px 0;
+ color: #A30000; }
+ .kuiFormRow .kuiFormRow__error + * {
+ padding-top: 0; }
+ .kuiFormRow .kuiFormRow__icon {
+ position: absolute;
+ top: 32px;
+ left: 12px; }
+ .kuiFormRow input:focus + label,
+ .kuiFormRow select:focus + label,
+ .kuiFormRow textarea:focus + label {
+ color: #0079a5; }
+ .kuiFormRow.kuiFormRow--invalid .kuiFormRow__label {
+ color: #A30000 !important; }
+ .kuiFormRow.kuiFormRow--invalid input[type="text"],
+ .kuiFormRow.kuiFormRow--invalid input[type="password"],
+ .kuiFormRow.kuiFormRow--invalid input[type="number"],
+ .kuiFormRow.kuiFormRow--invalid input[type="search"],
+ .kuiFormRow.kuiFormRow--invalid select,
+ .kuiFormRow.kuiFormRow--invalid textarea {
+ box-shadow: 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.16), inset 0 0 0 0 #FFF, inset 0 -2px 0 0 #A30000 !important; }
+
+.kuiCheckbox {
+ position: relative;
+ height: 24px;
+ margin-top: 8px; }
+ .kuiCheckbox .kuiCheckbox__label {
+ position: absolute;
+ padding-left: 32px;
+ line-height: 24px;
+ display: block;
+ font-weight: 400;
+ z-index: 2;
+ font-size: 14px;
+ cursor: pointer; }
+ .kuiCheckbox .kuiCheckbox__square {
+ position: absolute;
+ height: 24px;
+ width: 24px;
+ border-radius: 4px;
+ border: 1px solid #D9D9D9;
+ background: #fbfbfb;
+ z-index: 0; }
+ .kuiCheckbox .kuiCheckbox__check {
+ height: 100%;
+ width: 100%; }
+ .kuiCheckbox .kuiCheckbox__input {
+ position: absolute;
+ opacity: 0; }
+ .kuiCheckbox .kuiCheckbox__input:checked ~ .kuiCheckbox__square .kuiCheckbox__check {
+ background-color: #3F3F3F;
+ -webkit-mask: url("../src/components/icon/assets/check.svg") center center no-repeat;
+ mask: url("../src/components/icon/assets/check.svg") center center no-repeat; }
+ .kuiCheckbox .kuiCheckbox__input:focus ~ .kuiCheckbox__square {
+ background-color: #e6f2f6;
+ border-color: #0079a5; }
+ .kuiCheckbox .kuiCheckbox__input:checked:focus ~ .kuiCheckbox__square .kuiCheckbox__check {
+ background-color: #3F3F3F;
+ -webkit-mask: url("../src/components/icon/assets/check.svg") center center no-repeat;
+ mask: url("../src/components/icon/assets/check.svg") center center no-repeat; }
+
+.kuiFieldNumber {
+ border: none;
+ font-size: 14px;
+ font-family: "Roboto", Helvetica, Arial, sans-serif;
+ padding: 12px;
+ color: #3F3F3F;
+ min-width: 256px;
+ background: #fbfbfb;
+ box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.08), inset -400px 0 0 0 #fbfbfb;
+ transition: box-shadow 250ms ease-in, background 250ms ease-in;
+ max-width: 400px;
+ border-radius: 0; }
+ .kuiFieldNumber:focus {
+ background: #FFF;
+ box-shadow: 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.16), inset 0 0 0 0 #FFF, inset 0 -2px 0 0 #0079a5; }
+
+.kuiFieldPassword {
+ border: none;
+ font-size: 14px;
+ font-family: "Roboto", Helvetica, Arial, sans-serif;
+ padding: 12px;
+ color: #3F3F3F;
+ min-width: 256px;
+ background: #fbfbfb;
+ box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.08), inset -400px 0 0 0 #fbfbfb;
+ transition: box-shadow 250ms ease-in, background 250ms ease-in;
+ max-width: 400px;
+ border-radius: 0; }
+ .kuiFieldPassword:focus {
+ background: #FFF;
+ box-shadow: 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.16), inset 0 0 0 0 #FFF, inset 0 -2px 0 0 #0079a5; }
+
+.kuiFieldSearch {
+ border: none;
+ font-size: 14px;
+ font-family: "Roboto", Helvetica, Arial, sans-serif;
+ padding: 12px;
+ color: #3F3F3F;
+ min-width: 256px;
+ background: #fbfbfb;
+ box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.08), inset -400px 0 0 0 #fbfbfb;
+ transition: box-shadow 250ms ease-in, background 250ms ease-in;
+ max-width: 400px;
+ border-radius: 0; }
+ .kuiFieldSearch:focus {
+ background: #FFF;
+ box-shadow: 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.16), inset 0 0 0 0 #FFF, inset 0 -2px 0 0 #0079a5; }
+
+.kuiFieldText {
+ border: none;
+ font-size: 14px;
+ font-family: "Roboto", Helvetica, Arial, sans-serif;
+ padding: 12px;
+ color: #3F3F3F;
+ min-width: 256px;
+ background: #fbfbfb;
+ box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.08), inset -400px 0 0 0 #fbfbfb;
+ transition: box-shadow 250ms ease-in, background 250ms ease-in;
+ max-width: 400px;
+ border-radius: 0; }
+ .kuiFieldText:focus {
+ background: #FFF;
+ box-shadow: 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.16), inset 0 0 0 0 #FFF, inset 0 -2px 0 0 #0079a5; }
+
+.kuiRange {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ margin: 8px 0;
+ width: 100%; }
+ .kuiRange:focus::-webkit-slider-thumb {
+ border: 2px solid #0079a5; }
+ .kuiRange:focus::-moz-range-thumb {
+ border: 2px solid #0079a5; }
+ .kuiRange:focus::-ms-thumb {
+ border: 2px solid #0079a5; }
+ .kuiRange:focus::-webkit-slider-runnable-track {
+ background-color: #0079a5; }
+ .kuiRange::-webkit-slider-runnable-track {
+ cursor: pointer;
+ height: 2px;
+ transition: all 250ms ease-in;
+ width: 100%;
+ background: #D9D9D9;
+ border: 0 solid #D9D9D9;
+ border-radius: 4px; }
+ .kuiRange::-webkit-slider-thumb {
+ background: #FFF;
+ border: 2px solid #3F3F3F;
+ border-radius: 50%;
+ cursor: pointer;
+ height: 16px;
+ width: 16px;
+ -webkit-appearance: none;
+ margin-top: -7px; }
+ .kuiRange::-moz-range-track {
+ cursor: pointer;
+ height: 2px;
+ transition: all 250ms ease-in;
+ width: 100%;
+ background: #D9D9D9;
+ border: 0 solid #D9D9D9;
+ border-radius: 4px; }
+ .kuiRange::-moz-range-thumb {
+ background: #FFF;
+ border: 2px solid #3F3F3F;
+ border-radius: 50%;
+ cursor: pointer;
+ height: 16px;
+ width: 16px; }
+ .kuiRange::-ms-track {
+ cursor: pointer;
+ height: 2px;
+ transition: all 250ms ease-in;
+ width: 100%;
+ background: transparent;
+ border-color: transparent;
+ border-width: 8px 0;
+ color: transparent; }
+ .kuiRange::-ms-fill-lower {
+ background: #D9D9D9;
+ border: 0 solid #D9D9D9;
+ border-radius: 8px; }
+ .kuiRange::-ms-fill-upper {
+ background: #D9D9D9;
+ border: 0 solid #D9D9D9;
+ border-radius: 8px; }
+ .kuiRange::-ms-thumb {
+ background: #FFF;
+ border: 2px solid #3F3F3F;
+ border-radius: 50%;
+ cursor: pointer;
+ height: 16px;
+ width: 16px;
+ margin-top: 0; }
+
+.kuiSelect {
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ border: none;
+ font-size: 14px;
+ font-family: "Roboto", Helvetica, Arial, sans-serif;
+ padding: 12px;
+ color: #3F3F3F;
+ min-width: 256px;
+ background: #fbfbfb;
+ box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.08), inset -400px 0 0 0 #fbfbfb;
+ transition: box-shadow 250ms ease-in, background 250ms ease-in;
+ max-width: 400px;
+ border-radius: 0; }
+ .kuiSelect:focus {
+ background: #FFF;
+ box-shadow: 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.16), inset 0 0 0 0 #FFF, inset 0 -2px 0 0 #0079a5; }
+ .kuiSelect::-ms-expand {
+ display: none; }
+
+.kuiSwitch {
+ position: relative;
+ display: inline-block;
+ height: 24px;
+ cursor: pointer;
+ /**
+ * 1. The label is our main clickable area. It sits above the actual switch.
+ */
+ /**
+ * 1. The input is "hidden" but still focusable.
+ */
+ /**
+ * 1. Accounts for the border on the body.
+ */
+ /**
+ * 1. Mask is used to color the svg. Text color is used so works regardless of theme.
+ */
+ /**
+ * The thumb is slightly scaled when in use.
+ */
+ /**
+ * When input is not checked, we shift around the positioning of sibling/child selectors.
+ */ }
+ .kuiSwitch .kuiSwitch__label {
+ position: absolute;
+ left: 0;
+ padding-left: 60px;
+ z-index: 2;
+ line-height: 24px;
+ font-size: 14px;
+ cursor: pointer; }
+ .kuiSwitch .kuiSwitch__input {
+ position: absolute;
+ opacity: 0;
+ z-index: -1; }
+ .kuiSwitch .kuiSwitch__input:focus + .kuiSwitch__body {
+ background: #FFF; }
+ .kuiSwitch .kuiSwitch__input:focus + .kuiSwitch__body .kuiSwitch__thumb {
+ border-color: #0079a5;
+ background-color: #0079a5; }
+ .kuiSwitch .kuiSwitch__body {
+ width: 52px;
+ height: 24px;
+ background: #fbfbfb;
+ border: 1px solid #D9D9D9;
+ display: inline-block;
+ position: relative;
+ border-radius: 24px;
+ vertical-align: middle; }
+ .kuiSwitch .kuiSwitch__thumb {
+ position: absolute;
+ width: 24px;
+ height: 24px;
+ display: inline-block;
+ background-color: #FFF;
+ left: 28px;
+ top: -1px;
+ border-radius: 50%;
+ border: 1px solid #D9D9D9;
+ transition: border-color 250ms cubic-bezier(0.34, 1.61, 0.7, 1), background-color 250ms cubic-bezier(0.34, 1.61, 0.7, 1), left 250ms cubic-bezier(0.34, 1.61, 0.7, 1), -webkit-transform 250ms cubic-bezier(0.34, 1.61, 0.7, 1);
+ transition: border-color 250ms cubic-bezier(0.34, 1.61, 0.7, 1), background-color 250ms cubic-bezier(0.34, 1.61, 0.7, 1), left 250ms cubic-bezier(0.34, 1.61, 0.7, 1), transform 250ms cubic-bezier(0.34, 1.61, 0.7, 1);
+ transition: border-color 250ms cubic-bezier(0.34, 1.61, 0.7, 1), background-color 250ms cubic-bezier(0.34, 1.61, 0.7, 1), left 250ms cubic-bezier(0.34, 1.61, 0.7, 1), transform 250ms cubic-bezier(0.34, 1.61, 0.7, 1), -webkit-transform 250ms cubic-bezier(0.34, 1.61, 0.7, 1);
+ z-index: 1; }
+ .kuiSwitch .kuiSwitch__track {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ overflow: hidden;
+ border-radius: 24px; }
+ .kuiSwitch .kuiSwitch__icon {
+ position: absolute;
+ right: -40px;
+ top: 0;
+ bottom: 0;
+ width: 48px;
+ transition: left 250ms cubic-bezier(0.34, 1.61, 0.7, 1), right 250ms cubic-bezier(0.34, 1.61, 0.7, 1);
+ -webkit-mask: url("../src/components/icon/assets/cross.svg") center center no-repeat;
+ mask: url("../src/components/icon/assets/cross.svg") center center no-repeat;
+ background-color: #3F3F3F; }
+ .kuiSwitch .kuiSwitch__icon--checked {
+ right: auto;
+ left: -8px;
+ background-color: #3F3F3F;
+ -webkit-mask: url("../src/components/icon/assets/check.svg") center center no-repeat;
+ mask: url("../src/components/icon/assets/check.svg") center center no-repeat; }
+ .kuiSwitch:hover .kuiSwitch__thumb {
+ -webkit-transform: scale(1.05);
+ transform: scale(1.05); }
+ .kuiSwitch:active .kuiSwitch__thumb {
+ -webkit-transform: scale(0.95);
+ transform: scale(0.95); }
+ .kuiSwitch .kuiSwitch__input:not(:checked) ~ .kuiSwitch__body .kuiSwitch__thumb {
+ left: -1px; }
+ .kuiSwitch .kuiSwitch__input:not(:checked) ~ .kuiSwitch__body .kuiSwitch__icon {
+ right: -8px; }
+ .kuiSwitch .kuiSwitch__input:not(:checked) ~ .kuiSwitch__body .kuiSwitch__icon.kuiSwitch__icon--checked {
+ right: auto;
+ left: -40px; }
+
+.kuiTextArea {
+ border: none;
+ font-size: 14px;
+ font-family: "Roboto", Helvetica, Arial, sans-serif;
+ padding: 12px;
+ color: #3F3F3F;
+ min-width: 256px;
+ background: #fbfbfb;
+ box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.08), inset -400px 0 0 0 #fbfbfb;
+ transition: box-shadow 250ms ease-in, background 250ms ease-in;
+ max-width: 400px;
+ border-radius: 0; }
+ .kuiTextArea:focus {
+ background: #FFF;
+ box-shadow: 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0, 0, 0, 0.16), inset 0 0 0 0 #FFF, inset 0 -2px 0 0 #0079a5; }
+
.kuiHeader {
box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1);
display: -webkit-box;
@@ -754,7 +1140,8 @@ table {
.kuiIcon {
display: inline-block;
- vertical-align: middle; }
+ vertical-align: middle;
+ fill: #3F3F3F; }
.kuiIcon:focus {
opacity: 1;
background: #e6f2f6; }
@@ -1363,7 +1750,7 @@ table {
.kuiPopover.kuiPopover-isOpen .kuiPopover__body {
opacity: 1;
visibility: visible;
- z-index: 0;
+ z-index: 2000;
margin-top: 8px; }
.kuiPopover__body {
diff --git a/ui_framework/doc_site/src/components/guide_components.scss b/ui_framework/doc_site/src/components/guide_components.scss
index 450e357978fca..c2e0d78e6109f 100644
--- a/ui_framework/doc_site/src/components/guide_components.scss
+++ b/ui_framework/doc_site/src/components/guide_components.scss
@@ -10,7 +10,7 @@ $guideChromeTransition: 0.3s ease;
// Colors
$guideBaseBackgroundColor: #f7f7f7;
-$guidePanelBackgroundColor: #ffffff;
+$guidePanelBackgroundColor: $kuiColorEmptyShade;
$guideTextColor: #444;
$guideLinkColor: #00a9e5;
$guideLinkHoverColor: #00a9e5;
diff --git a/ui_framework/doc_site/src/components/guide_page/_guide_page.scss b/ui_framework/doc_site/src/components/guide_page/_guide_page.scss
index 3c3249a940109..e18ce8f986031 100644
--- a/ui_framework/doc_site/src/components/guide_page/_guide_page.scss
+++ b/ui_framework/doc_site/src/components/guide_page/_guide_page.scss
@@ -11,7 +11,7 @@
$scrollBarWidth: 20px;
background-color: $guidePanelBackgroundColor;
- border: 1px solid #CCCCCC;
+ border: 1px solid $kuiColorLightShade;
border-radius: 4px;
flex: 1 1 auto;
padding: 40px 60px;
diff --git a/ui_framework/doc_site/src/main.scss b/ui_framework/doc_site/src/main.scss
index f1707487acfbf..7472366bdd57d 100644
--- a/ui_framework/doc_site/src/main.scss
+++ b/ui_framework/doc_site/src/main.scss
@@ -1,2 +1,3 @@
@import "../../dist/ui_framework.css";
+@import "../../src/global_styling/variables/_index.scss";
@import "./components/guide_components";
diff --git a/ui_framework/doc_site/src/services/routes/routes.js b/ui_framework/doc_site/src/services/routes/routes.js
index 2e701b73103c8..d257c1f77ddd9 100644
--- a/ui_framework/doc_site/src/services/routes/routes.js
+++ b/ui_framework/doc_site/src/services/routes/routes.js
@@ -9,6 +9,9 @@ import ButtonExample
import CallOutExample
from '../../views/call_out/call_out_example';
+import FormExample
+ from '../../views/form/form_example';
+
import IconExample
from '../../views/icon/icon_example';
@@ -64,6 +67,10 @@ const components = [{
name: 'CallOut',
component: CallOutExample,
hasReact: true,
+}, {
+ name: 'Form',
+ component: FormExample,
+ hasReact: true,
}, {
name: 'Header',
component: HeaderExample,
diff --git a/ui_framework/doc_site/src/views/form/field_text.js b/ui_framework/doc_site/src/views/form/field_text.js
new file mode 100644
index 0000000000000..19b8bc4c1f5ce
--- /dev/null
+++ b/ui_framework/doc_site/src/views/form/field_text.js
@@ -0,0 +1,65 @@
+import React, {
+ Component,
+} from 'react';
+
+import {
+ KuiFormRow,
+ KuiFieldText,
+} from '../../../../components';
+
+// Don't use this, make proper ids instead. This is just for the example.
+function makeId() {
+ return Math.random().toString(36).substr(2, 5);
+}
+
+export default class extends Component {
+
+ render() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
diff --git a/ui_framework/doc_site/src/views/form/form_everything.js b/ui_framework/doc_site/src/views/form/form_everything.js
new file mode 100644
index 0000000000000..7170b429cce32
--- /dev/null
+++ b/ui_framework/doc_site/src/views/form/form_everything.js
@@ -0,0 +1,154 @@
+import React, {
+ Component,
+} from 'react';
+
+import {
+ KuiForm,
+ KuiCheckbox,
+ KuiFieldNumber,
+ KuiFieldPassword,
+ KuiRange,
+ KuiFormRow,
+ KuiFieldSearch,
+ KuiSelect,
+ KuiSwitch,
+ KuiFieldText,
+ KuiTextArea,
+} from '../../../../components';
+
+
+// Don't use this, make proper ids instead. This is just for the example.
+function makeId() {
+ return Math.random().toString(36).substr(2, 5);
+}
+
+export default class extends Component {
+
+ render() {
+
+ // Checkboxes are passed as an array of objects. They can be optionally checked to start.
+ const checkboxOptions = [
+ { id: makeId(), label: 'Option one' },
+ { id: makeId(), label: 'Option two is checked by default', checked: true },
+ { id: makeId(), label: 'Option three' },
+ ];
+
+ // Select options are passed as an array of objects.
+ const selectOptions = [
+ { value: 'option_one', text: 'Option one' },
+ { value: 'option_two', text: 'Option two' },
+ { value: 'option_three', text: 'Option three' },
+ ];
+
+
+ const formSample = (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+
+ return (
+
+ {formSample}
+
+ );
+ }
+}
diff --git a/ui_framework/doc_site/src/views/form/form_example.js b/ui_framework/doc_site/src/views/form/form_example.js
new file mode 100644
index 0000000000000..7cec41f68425e
--- /dev/null
+++ b/ui_framework/doc_site/src/views/form/form_example.js
@@ -0,0 +1,112 @@
+import React from 'react';
+
+import { renderToHtml } from '../../services';
+
+import {
+ GuideDemo,
+ GuideCode,
+ GuidePage,
+ GuideSection,
+ GuideSectionTypes,
+ GuideText,
+} from '../../components';
+
+import FieldText from './field_text';
+const fieldTextSource = require('!!raw!./field_text');
+const fieldTextHtml = renderToHtml(FieldText);
+
+import FormEverything from './form_everything';
+const formEverythingSource = require('!!raw!./form_everything');
+const formEverythingHtml = renderToHtml(FormEverything);
+
+import FormValidation from './form_validation';
+const formValidationSource = require('!!raw!./form_validation');
+const formValidationHtml = renderToHtml(FormValidation);
+
+import FormPopover from './form_popover';
+const formPopoverSource = require('!!raw!./form_popover');
+const formPopoverHtml = renderToHtml(FormPopover);
+
+export default props => (
+
+
+
+ Each form input has a base component to cover generic use cases. These are raw HTML elements with only basic styling.
+ Additionally, you can wrap any of these elements with a FormRow which gives you optional
+ prebuilt labels, help text and validation. Below is an example showing the FieldText component
+ in a bunch of configurations, but they all act roughly the same. Farther down in the docs you can see an example
+ showing every form element and their individual prop settings (which mirror their HTML counterparts).
+
+
+
+
+
+
+
+
+ This example shows every form element in use and showcases a variety of styles. Note that each one of these elements is wrapped
+ by FormRow .
+
+
+
+
+
+
+
+
+ Forms can be placed within popovers and should scale accordingly.
+
+
+
+
+
+
+
+
+ Validation is achieved by applying invalid and optionally error props
+ onto the KuiForm or KuiFormRow components. Errors are optional
+ and are passed as an array in case you need to list many errors.
+
+
+
+
+
+
+
+);
diff --git a/ui_framework/doc_site/src/views/form/form_popover.js b/ui_framework/doc_site/src/views/form/form_popover.js
new file mode 100644
index 0000000000000..0002fd22d0044
--- /dev/null
+++ b/ui_framework/doc_site/src/views/form/form_popover.js
@@ -0,0 +1,80 @@
+import React, {
+ Component,
+} from 'react';
+
+import {
+ KuiButton,
+ KuiPopover,
+ KuiForm,
+ KuiRange,
+ KuiFormRow,
+ KuiSwitch,
+ KuiFieldText,
+} from '../../../../components';
+
+function makeId() {
+ return Math.random().toString(36).substr(2, 5);
+}
+
+export default class extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ isPopoverOpen: false,
+ };
+ }
+
+ onButtonClick() {
+ this.setState({
+ isPopoverOpen: !this.state.isPopoverOpen,
+ });
+ }
+
+ closePopover() {
+ this.setState({
+ isPopoverOpen: false,
+ });
+ }
+
+ render() {
+ const button = (
+
+ Form in a popover
+
+ );
+
+ const formSample = (
+
+
+
+
+
+
+
+
+
+
+
+ );
+
+ return (
+
+ );
+ }
+}
diff --git a/ui_framework/doc_site/src/views/form/form_validation.js b/ui_framework/doc_site/src/views/form/form_validation.js
new file mode 100644
index 0000000000000..b29e3d75bdece
--- /dev/null
+++ b/ui_framework/doc_site/src/views/form/form_validation.js
@@ -0,0 +1,84 @@
+import React, {
+ Component,
+} from 'react';
+
+import {
+ KuiButton,
+ KuiForm,
+ KuiCheckbox,
+ KuiFormRow,
+ KuiFieldText,
+} from '../../../../components';
+
+function makeId() {
+ return Math.random().toString(36).substr(2, 5);
+}
+
+export default class extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ showErrors: false,
+ };
+ }
+
+ onButtonClick() {
+ this.setState({
+ showErrors: !this.state.showErrors,
+ });
+ }
+
+ render() {
+ const button = (
+
+ Toggle errors
+
+ );
+
+ const checkboxOptions = [
+ { id: makeId(), label: 'Option one' },
+ { id: makeId(), label: 'Option two' },
+ { id: makeId(), label: 'Option three' },
+ ];
+
+ let errors = null;
+ if (this.state.showErrors) {
+ errors = ['Here\'s an example of an error', 'You might have more than one error, so pass an array.'];
+ } else {
+ errors = null;
+ }
+
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ {button}
+
+
+ );
+ }
+}
+
diff --git a/ui_framework/doc_site/src/views/kibana/kibana.js b/ui_framework/doc_site/src/views/kibana/kibana.js
index 7b7c30b1a79ba..ac372ae5b6c8c 100644
--- a/ui_framework/doc_site/src/views/kibana/kibana.js
+++ b/ui_framework/doc_site/src/views/kibana/kibana.js
@@ -408,9 +408,7 @@ export default class extends Component {
{/* Kibana section */}
-
- Kibana
-
+ Kibana
@@ -434,9 +432,7 @@ export default class extends Component {
{/* Logstash section */}
-
- Logstash
-
+ Logstash
diff --git a/ui_framework/src/components/call_out/_call_out.scss b/ui_framework/src/components/call_out/_call_out.scss
index 5ac6f6740ad80..8ce78f71b0db0 100644
--- a/ui_framework/src/components/call_out/_call_out.scss
+++ b/ui_framework/src/components/call_out/_call_out.scss
@@ -1,6 +1,6 @@
.kuiCallOut {
- padding: $kuiSizeM $kuiSizeL;
- border-left: 4px solid transparent;
+ padding: $kuiSize;
+ border-left: $kuiSizeXS / 2 solid transparent;
}
// Modifier naming and colors.
@@ -31,17 +31,20 @@ $callOutTypes: (
/**
* 1. Align icon with first line of title text if it wraps.
- * 2. Apply margin to all but last item in the flex.
+ * 2. If content exists under the header, space it appropriately.
+ * 3. Apply margin to all but last item in the flex.
*/
.kuiCallOutHeader {
@include kuiFontSizeM;
-
- margin-bottom: $kuiVerticalRhythmS;
display: flex;
align-items: baseline; /* 1 */
+ + * {
+ margin-top: $kuiSizeS; /* 1 */
+ }
+
> * + * {
- margin-left: $kuiSizeS; /* 2 */
+ margin-left: $kuiSizeS; /* 3 */
}
}
diff --git a/ui_framework/src/components/form/_form.scss b/ui_framework/src/components/form/_form.scss
new file mode 100644
index 0000000000000..e9b56f5e0a4dc
--- /dev/null
+++ b/ui_framework/src/components/form/_form.scss
@@ -0,0 +1,9 @@
+.kuiForm__error {
+ @include kuiFontSizeS;
+ list-style: disc;
+ margin-left: $kuiSizeXL;
+}
+
+.kuiForm__errors {
+ margin-bottom: $kuiSize;
+}
diff --git a/ui_framework/src/components/form/_index.scss b/ui_framework/src/components/form/_index.scss
new file mode 100644
index 0000000000000..0e5c3264c6a27
--- /dev/null
+++ b/ui_framework/src/components/form/_index.scss
@@ -0,0 +1,33 @@
+$kuiFormMaxWidth: 400px;
+$kuiFormBackgroundColor: tintOrShade($kuiColorLightestShade, 60%, 25%);
+
+@mixin kuiFieldStyle {
+ border: none;
+ font-size: $kuiFontSizeS;
+ font-family: $kuiFontFamily;
+ padding: $kuiSizeM;
+ color: $kuiTextColor;
+ min-width: $kuiSize * 16;
+ background: $kuiFormBackgroundColor;
+ box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0,0,0,0.08), inset -400px 0 0 0 $kuiFormBackgroundColor;
+ transition: box-shadow $kuiAnimSpeedNormal ease-in, background $kuiAnimSpeedNormal ease-in;
+ max-width: $kuiFormMaxWidth;
+ border-radius: 0;
+
+ &:focus {
+ background: $kuiColorEmptyShade;
+ box-shadow: 0 4px 4px -2px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0,0,0,0.16), inset 0 0 0 0 $kuiColorEmptyShade, inset 0 -2px 0 0 $kuiColorPrimary;
+ }
+}
+
+@import 'form';
+@import 'form_row/index';
+@import 'checkbox/index';
+@import 'field_number/index';
+@import 'field_password/index';
+@import 'field_search/index';
+@import 'field_text/index';
+@import 'range/index';
+@import 'select/index';
+@import 'switch/index';
+@import 'text_area/index';
diff --git a/ui_framework/src/components/form/checkbox/_checkbox.scss b/ui_framework/src/components/form/checkbox/_checkbox.scss
new file mode 100644
index 0000000000000..171c9a1e011a8
--- /dev/null
+++ b/ui_framework/src/components/form/checkbox/_checkbox.scss
@@ -0,0 +1,51 @@
+.kuiCheckbox {
+ position: relative;
+ height: $kuiSizeL;
+ margin-top: $kuiSizeS;
+
+ .kuiCheckbox__label {
+ position: absolute;
+ padding-left: $kuiSizeXL;
+ line-height: $kuiSizeL;
+ display: block;
+ font-weight: $kuiFontWeightRegular;
+ z-index: 2;
+ font-size: $kuiFontSizeS;
+ cursor: pointer;
+ }
+
+ .kuiCheckbox__square {
+ position: absolute;
+ height: $kuiSizeL;
+ width: $kuiSizeL;
+ border-radius: $kuiBorderRadius;
+ border: $kuiBorderThin;
+ background: $kuiFormBackgroundColor;
+ z-index: 0;
+ }
+
+ .kuiCheckbox__check {
+ height: 100%;
+ width: 100%;
+ }
+
+ .kuiCheckbox__input {
+ position: absolute;
+ opacity: 0;
+
+ &:checked ~ .kuiCheckbox__square .kuiCheckbox__check {
+ background-color: $kuiTextColor;
+ mask: url('../src/components/icon/assets/check.svg') center center no-repeat;
+ }
+
+ &:focus ~ .kuiCheckbox__square {
+ background-color: $kuiFocusBackgroundColor;
+ border-color: $kuiColorPrimary;
+ }
+
+ &:checked:focus ~ .kuiCheckbox__square .kuiCheckbox__check {
+ background-color: $kuiTextColor;
+ mask: url('../src/components/icon/assets/check.svg') center center no-repeat;
+ }
+ }
+}
diff --git a/ui_framework/src/components/form/checkbox/_index.scss b/ui_framework/src/components/form/checkbox/_index.scss
new file mode 100644
index 0000000000000..e6e53eb712869
--- /dev/null
+++ b/ui_framework/src/components/form/checkbox/_index.scss
@@ -0,0 +1 @@
+@import 'checkbox';
diff --git a/ui_framework/src/components/form/checkbox/checkbox.js b/ui_framework/src/components/form/checkbox/checkbox.js
new file mode 100644
index 0000000000000..55490f3161149
--- /dev/null
+++ b/ui_framework/src/components/form/checkbox/checkbox.js
@@ -0,0 +1,29 @@
+import React, {
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+
+export const KuiCheckbox = ({ options, className, ...rest }) => {
+ const classes = classNames('kuiCheckbox', className);
+
+ return (
+
+ {options.map((option, index) => {
+
+ return (
+
+ );
+ })}
+
+ );
+};
+
+KuiCheckbox.propTypes = {
+ options: PropTypes.arrayOf(React.PropTypes.object).isRequired,
+};
diff --git a/ui_framework/src/components/form/checkbox/checkbox.test.js b/ui_framework/src/components/form/checkbox/checkbox.test.js
new file mode 100644
index 0000000000000..74479a5a2c408
--- /dev/null
+++ b/ui_framework/src/components/form/checkbox/checkbox.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../../test/required_props';
+
+import { KuiCheckbox } from './checkbox';
+
+describe('KuiCheckbox', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/form/checkbox/index.js b/ui_framework/src/components/form/checkbox/index.js
new file mode 100644
index 0000000000000..4551ccb9b4155
--- /dev/null
+++ b/ui_framework/src/components/form/checkbox/index.js
@@ -0,0 +1 @@
+export { KuiCheckbox } from './checkbox';
diff --git a/ui_framework/src/components/form/field_number/_field_number.scss b/ui_framework/src/components/form/field_number/_field_number.scss
new file mode 100644
index 0000000000000..4e7c4bcc0fd65
--- /dev/null
+++ b/ui_framework/src/components/form/field_number/_field_number.scss
@@ -0,0 +1,3 @@
+.kuiFieldNumber {
+ @include kuiFieldStyle;
+}
diff --git a/ui_framework/src/components/form/field_number/_index.scss b/ui_framework/src/components/form/field_number/_index.scss
new file mode 100644
index 0000000000000..497eee52289d4
--- /dev/null
+++ b/ui_framework/src/components/form/field_number/_index.scss
@@ -0,0 +1 @@
+@import 'field_number';
diff --git a/ui_framework/src/components/form/field_number/field_number.js b/ui_framework/src/components/form/field_number/field_number.js
new file mode 100644
index 0000000000000..e9affb862ebd1
--- /dev/null
+++ b/ui_framework/src/components/form/field_number/field_number.js
@@ -0,0 +1,36 @@
+import React, {
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+
+export const KuiFieldNumber = ({ className, id, placeholder, name, min, max, value, ...rest }) => {
+ const classes = classNames('kuiFieldNumber', className);
+
+ return (
+
+ );
+};
+
+KuiFieldNumber.propTypes = {
+ id: PropTypes.string,
+ name: PropTypes.string.isRequired,
+ min: PropTypes.number,
+ max: PropTypes.number,
+ step: PropTypes.number,
+ value: PropTypes.number,
+};
+
+KuiFieldNumber.defaultProps = {
+ value: undefined,
+};
+
diff --git a/ui_framework/src/components/form/field_number/field_number.test.js b/ui_framework/src/components/form/field_number/field_number.test.js
new file mode 100644
index 0000000000000..2fb206e545c59
--- /dev/null
+++ b/ui_framework/src/components/form/field_number/field_number.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../../test/required_props';
+
+import { KuiFieldNumber } from './field_number';
+
+describe('KuiFieldNumber', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/form/field_number/index.js b/ui_framework/src/components/form/field_number/index.js
new file mode 100644
index 0000000000000..5ae02c1c69034
--- /dev/null
+++ b/ui_framework/src/components/form/field_number/index.js
@@ -0,0 +1 @@
+export { KuiFieldNumber } from './field_number';
diff --git a/ui_framework/src/components/form/field_password/_field_password.scss b/ui_framework/src/components/form/field_password/_field_password.scss
new file mode 100644
index 0000000000000..6b511ea2298fb
--- /dev/null
+++ b/ui_framework/src/components/form/field_password/_field_password.scss
@@ -0,0 +1,3 @@
+.kuiFieldPassword {
+ @include kuiFieldStyle;
+}
diff --git a/ui_framework/src/components/form/field_password/_index.scss b/ui_framework/src/components/form/field_password/_index.scss
new file mode 100644
index 0000000000000..aab5cd9d3b3a2
--- /dev/null
+++ b/ui_framework/src/components/form/field_password/_index.scss
@@ -0,0 +1 @@
+@import 'field_password';
diff --git a/ui_framework/src/components/form/field_password/field_password.js b/ui_framework/src/components/form/field_password/field_password.js
new file mode 100644
index 0000000000000..a6bcadd17894a
--- /dev/null
+++ b/ui_framework/src/components/form/field_password/field_password.js
@@ -0,0 +1,31 @@
+import React, {
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+
+export const KuiFieldPassword = ({ className, id, name, placeholder, value, ...rest }) => {
+ const classes = classNames('kuiFieldPassword', className);
+
+ return (
+
+ );
+};
+
+KuiFieldPassword.propTypes = {
+ name: PropTypes.string.isRequired,
+ id: PropTypes.string,
+ placeholder: PropTypes.string,
+ value: PropTypes.string,
+};
+
+KuiFieldPassword.defaultProps = {
+ value: undefined,
+};
diff --git a/ui_framework/src/components/form/field_password/field_password.test.js b/ui_framework/src/components/form/field_password/field_password.test.js
new file mode 100644
index 0000000000000..a70c135388164
--- /dev/null
+++ b/ui_framework/src/components/form/field_password/field_password.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../../test/required_props';
+
+import { KuiFieldPassword } from './field_password';
+
+describe('KuiFieldPassword', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/form/field_password/index.js b/ui_framework/src/components/form/field_password/index.js
new file mode 100644
index 0000000000000..1596934c1917a
--- /dev/null
+++ b/ui_framework/src/components/form/field_password/index.js
@@ -0,0 +1 @@
+export { KuiFieldPassword } from './field_password';
diff --git a/ui_framework/src/components/form/field_search/_field_search.scss b/ui_framework/src/components/form/field_search/_field_search.scss
new file mode 100644
index 0000000000000..15530ebedc5c6
--- /dev/null
+++ b/ui_framework/src/components/form/field_search/_field_search.scss
@@ -0,0 +1,4 @@
+.kuiFieldSearch {
+ @include kuiFieldStyle;
+}
+
diff --git a/ui_framework/src/components/form/field_search/_index.scss b/ui_framework/src/components/form/field_search/_index.scss
new file mode 100644
index 0000000000000..b9652e5dde154
--- /dev/null
+++ b/ui_framework/src/components/form/field_search/_index.scss
@@ -0,0 +1 @@
+@import 'field_search';
diff --git a/ui_framework/src/components/form/field_search/field_search.js b/ui_framework/src/components/form/field_search/field_search.js
new file mode 100644
index 0000000000000..0253d6c5fd630
--- /dev/null
+++ b/ui_framework/src/components/form/field_search/field_search.js
@@ -0,0 +1,32 @@
+import React, {
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+
+
+export const KuiFieldSearch = ({ className, id, name, placeholder, value, ...rest }) => {
+ const classes = classNames('kuiFieldSearch', className);
+
+ return (
+
+ );
+};
+
+KuiFieldSearch.propTypes = {
+ name: PropTypes.string.isRequired,
+ id: PropTypes.string,
+ placeholder: PropTypes.string,
+ value: PropTypes.string,
+};
+
+KuiFieldSearch.defaultProps = {
+ value: undefined,
+};
diff --git a/ui_framework/src/components/form/field_search/field_search.test.js b/ui_framework/src/components/form/field_search/field_search.test.js
new file mode 100644
index 0000000000000..bf703e790687d
--- /dev/null
+++ b/ui_framework/src/components/form/field_search/field_search.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../../test/required_props';
+
+import { KuiFieldSearch } from './field_search';
+
+describe('KuiFieldSearch', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/form/field_search/index.js b/ui_framework/src/components/form/field_search/index.js
new file mode 100644
index 0000000000000..d91a57353e651
--- /dev/null
+++ b/ui_framework/src/components/form/field_search/index.js
@@ -0,0 +1 @@
+export { KuiFieldSearch } from './field_search';
diff --git a/ui_framework/src/components/form/field_text/_field_text.scss b/ui_framework/src/components/form/field_text/_field_text.scss
new file mode 100644
index 0000000000000..4b8afbedfb849
--- /dev/null
+++ b/ui_framework/src/components/form/field_text/_field_text.scss
@@ -0,0 +1,3 @@
+.kuiFieldText {
+ @include kuiFieldStyle;
+}
diff --git a/ui_framework/src/components/form/field_text/_index.scss b/ui_framework/src/components/form/field_text/_index.scss
new file mode 100644
index 0000000000000..937e44ac8d6e6
--- /dev/null
+++ b/ui_framework/src/components/form/field_text/_index.scss
@@ -0,0 +1 @@
+@import 'field_text';
diff --git a/ui_framework/src/components/form/field_text/field_text.js b/ui_framework/src/components/form/field_text/field_text.js
new file mode 100644
index 0000000000000..f04889921bb78
--- /dev/null
+++ b/ui_framework/src/components/form/field_text/field_text.js
@@ -0,0 +1,31 @@
+import React, {
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+
+export const KuiFieldText = ({ id, name, placeholder, value, className, ...rest }) => {
+ const classes = classNames('kuiFieldText', className);
+
+ return (
+
+ );
+};
+
+KuiFieldText.propTypes = {
+ name: PropTypes.string.isRequired,
+ id: PropTypes.string,
+ placeholder: PropTypes.string,
+ value: PropTypes.string,
+};
+
+KuiFieldText.defaultProps = {
+ value: undefined,
+};
diff --git a/ui_framework/src/components/form/field_text/field_text.test.js b/ui_framework/src/components/form/field_text/field_text.test.js
new file mode 100644
index 0000000000000..f6061c3ec1c3c
--- /dev/null
+++ b/ui_framework/src/components/form/field_text/field_text.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../../test/required_props';
+
+import { KuiFieldText } from './field_text';
+
+describe('KuiFieldText', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/form/field_text/index.js b/ui_framework/src/components/form/field_text/index.js
new file mode 100644
index 0000000000000..ff6960d5f2af6
--- /dev/null
+++ b/ui_framework/src/components/form/field_text/index.js
@@ -0,0 +1 @@
+export { KuiFieldText } from './field_text';
diff --git a/ui_framework/src/components/form/form.js b/ui_framework/src/components/form/form.js
new file mode 100644
index 0000000000000..a9b6907e1c2c5
--- /dev/null
+++ b/ui_framework/src/components/form/form.js
@@ -0,0 +1,46 @@
+import React, {
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+import { KuiCallOut } from '../../../components';
+
+export const KuiForm = ({ children, className, invalid, errors, ...rest }) => {
+ const classes = classNames('kuiForm', className);
+
+
+
+ let optionalErrors = null;
+ if (errors) {
+ optionalErrors = (
+
+ {errors.map(function (error, index) {
+ return {error} ;
+ })}
+
+ );
+ }
+
+ let optionalErrorAlert = null;
+ if (invalid) {
+ optionalErrorAlert = (
+
+ {optionalErrors}
+
+ );
+ }
+
+ return (
+
+ {optionalErrorAlert}
+ {children}
+
+ );
+};
+
+KuiForm.propTypes = {
+ invalid: PropTypes.bool,
+ errors: PropTypes.array,
+};
diff --git a/ui_framework/src/components/form/form.test.js b/ui_framework/src/components/form/form.test.js
new file mode 100644
index 0000000000000..8db361d646f93
--- /dev/null
+++ b/ui_framework/src/components/form/form.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../test/required_props';
+
+import { KuiForm } from './form';
+
+describe('KuiForm', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/form/form_row/_form_row.scss b/ui_framework/src/components/form/form_row/_form_row.scss
new file mode 100644
index 0000000000000..35834f9952e79
--- /dev/null
+++ b/ui_framework/src/components/form/form_row/_form_row.scss
@@ -0,0 +1,80 @@
+.kuiFormRow {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ max-width: $kuiFormMaxWidth;
+
+ + * {
+ margin-top: $kuiSizeL;
+ }
+
+ &.kuiFormRow--withIcon input {
+ padding-left: $kuiSizeXXL;
+ }
+
+ &.kuiFormRow--dropdown input {
+ padding-left: $kuiSizeM;
+ padding-right: $kuiSizeXXL;
+ }
+
+ &.kuiFormRow--select .kuiFormRow__icon {
+ left: auto;
+ right: $kuiSizeM;
+ }
+
+ .kuiFormRow__label {
+ font-size: $kuiFontSizeXS;
+ padding-bottom: $kuiSizeS;
+ cursor: pointer;
+ transition: all $kuiAnimSpeedFast $kuiAnimSlightResistance;
+ font-weight: $kuiFontWeightMedium;
+ order: -1; // Force the label to be first, so we can use sibling:focus selectors.
+ }
+
+ .kuiFormRow__helpText {
+ font-size: $kuiFontSizeXS;
+ padding: $kuiSizeS 0;
+ color: $kuiColorDarkShade;
+ }
+
+ .kuiFormRow__error {
+ font-size: $kuiFontSizeXS;
+ padding: $kuiSizeS 0;
+ color: $kuiColorDanger;
+ }
+
+ .kuiFormRow__error + * {
+ padding-top: 0;
+ }
+
+ .kuiFormRow__icon {
+ position: absolute;
+ top: $kuiSizeXL;
+ left: $kuiSizeM;
+ }
+
+ // Naked selectors on purpose to touch inner components.
+ input:focus + label,
+ select:focus + label,
+ textarea:focus + label, {
+ color: $kuiColorPrimary;
+ }
+
+ &.kuiFormRow--invalid {
+
+ .kuiFormRow__label {
+ color: $kuiColorDanger !important;
+ }
+
+ // Naked selectors on purpose to touch inner components.
+ input[type="text"],
+ input[type="password"],
+ input[type="number"],
+ input[type="search"],
+ select,
+ textarea,
+ {
+ box-shadow: 0 $kuiSizeXS $kuiSizeXS (-$kuiSizeXS / 2) rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(0,0,0,0.16), inset 0 0 0 0 $kuiColorEmptyShade, inset 0 (-$kuiSizeXS / 2) 0 0 $kuiColorDanger !important;
+ }
+ }
+}
diff --git a/ui_framework/src/components/form/form_row/_index.scss b/ui_framework/src/components/form/form_row/_index.scss
new file mode 100644
index 0000000000000..b95cb0dc91828
--- /dev/null
+++ b/ui_framework/src/components/form/form_row/_index.scss
@@ -0,0 +1 @@
+@import 'form_row';
diff --git a/ui_framework/src/components/form/form_row/form_row.js b/ui_framework/src/components/form/form_row/form_row.js
new file mode 100644
index 0000000000000..ca73ed5e93cb3
--- /dev/null
+++ b/ui_framework/src/components/form/form_row/form_row.js
@@ -0,0 +1,82 @@
+import React, {
+ cloneElement,
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+import { KuiIcon } from '../../../components';
+
+export const KuiFormRow = ({ children, icon, containsSelect, helpText, invalid, errors, label, id, className, ...rest }) => {
+ const classes = classNames(
+ 'kuiFormRow',
+ className,
+ {
+ 'kuiFormRow--withIcon' : icon,
+ 'kuiFormRow--invalid' : invalid,
+ 'kuiFormRow--select' : containsSelect,
+ }
+ );
+
+ let field;
+ let optionalIcon = null;
+ let optionalHelpText = null;
+ let optionalErrors = null;
+ let optionalLabel = null;
+
+ if (icon) {
+ optionalIcon = ;
+ }
+
+ if (helpText) {
+ optionalHelpText = {helpText}
;
+ }
+
+ if (errors) {
+ optionalErrors = (
+ errors.map(function (error, index) {
+ return {error}
;
+ })
+ );
+ }
+
+ if (label) {
+ optionalLabel = {label} ;
+ }
+
+ if (id) {
+ field = cloneElement(children, {
+ id,
+ });
+ } else {
+ field = children;
+ }
+
+ return (
+
+ {/*
+ Order is important here. The label needs to be UNDER the field.
+ We rearrange the flex order in the CSS so the label ends up
+ displaying above the children / input. This allows us to still
+ use sibling selectors against the label that are tiggered by the
+ focus state of the input.
+ */}
+ {field}
+ {optionalLabel}
+ {optionalErrors}
+ {optionalHelpText}
+ {optionalIcon}
+
+ );
+};
+
+KuiFormRow.propTypes = {
+ label: PropTypes.string,
+ id: PropTypes.string,
+ icon: PropTypes.string,
+ invalid: PropTypes.bool,
+ containsSelect: PropTypes.bool,
+ errors: PropTypes.array,
+ helpText: PropTypes.string,
+};
diff --git a/ui_framework/src/components/form/form_row/form_row.test.js b/ui_framework/src/components/form/form_row/form_row.test.js
new file mode 100644
index 0000000000000..9f1d85f2586a6
--- /dev/null
+++ b/ui_framework/src/components/form/form_row/form_row.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../../test/required_props';
+
+import { KuiFormRow } from './form_row';
+
+describe('KuiFormRow', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/form/form_row/index.js b/ui_framework/src/components/form/form_row/index.js
new file mode 100644
index 0000000000000..29d7fb780579d
--- /dev/null
+++ b/ui_framework/src/components/form/form_row/index.js
@@ -0,0 +1 @@
+export { KuiFormRow } from './form_row';
diff --git a/ui_framework/src/components/form/index.js b/ui_framework/src/components/form/index.js
new file mode 100644
index 0000000000000..5ea96bd92a7a0
--- /dev/null
+++ b/ui_framework/src/components/form/index.js
@@ -0,0 +1,11 @@
+export { KuiForm } from './form';
+export { KuiCheckbox } from './checkbox';
+export { KuiFieldNumber } from './field_number';
+export { KuiFieldPassword } from './field_password';
+export { KuiRange } from './range';
+export { KuiFormRow } from './form_row';
+export { KuiFieldSearch } from './field_search';
+export { KuiSelect } from './select';
+export { KuiSwitch } from './switch';
+export { KuiFieldText } from './field_text';
+export { KuiTextArea } from './text_area';
diff --git a/ui_framework/src/components/form/range/_index.scss b/ui_framework/src/components/form/range/_index.scss
new file mode 100644
index 0000000000000..3dabcb56381af
--- /dev/null
+++ b/ui_framework/src/components/form/range/_index.scss
@@ -0,0 +1,32 @@
+$kuiRangeTrackColor: $kuiColorLightShade;
+$kuiRangeThumbColor: $kuiColorEmptyShade;
+
+$kuiRangeThumbRadius: 50%;
+$kuiRangeThumbHeight: $kuiSize;
+$kuiRangeThumbWidth: $kuiSize;
+$kuiRangeBorderWidth: 2px;
+$kuiRangeBorderColor: $kuiTextColor;
+
+$kuiRangeTrackWidth: 100%;
+$kuiRangeTrackHeight: 2px;
+$kuiRangeTrackBorderWidth: 0;
+$kuiRangeTrackBorderColor: $kuiColorLightShade;
+$kuiRangeTrackRadius: $kuiBorderRadius;
+
+@mixin track {
+ cursor: pointer;
+ height: $kuiRangeTrackHeight;
+ transition: all $kuiAnimSpeedNormal ease-in;
+ width: $kuiRangeTrackWidth;
+}
+
+@mixin thumb {
+ background: $kuiRangeThumbColor;
+ border: $kuiRangeBorderWidth solid $kuiRangeBorderColor;
+ border-radius: $kuiRangeThumbRadius;
+ cursor: pointer;
+ height: $kuiRangeThumbHeight;
+ width: $kuiRangeThumbWidth;
+}
+
+@import 'range';
diff --git a/ui_framework/src/components/form/range/_range.scss b/ui_framework/src/components/form/range/_range.scss
new file mode 100644
index 0000000000000..1aca75b7eac3f
--- /dev/null
+++ b/ui_framework/src/components/form/range/_range.scss
@@ -0,0 +1,83 @@
+// The following code is inspired by...
+
+// Github: https://github.com/darlanrod/input-range-sass
+// Author: Darlan Rod https://github.com/darlanrod
+// Version 1.4.1
+// MIT License
+
+// It has been modified to fit the styling patterns of Kibana and
+// to be more easily maintained / themeable going forward.
+
+.kuiRange {
+ appearance: none;
+ margin: $kuiRangeThumbHeight / 2 0;
+ width: $kuiRangeTrackWidth;
+
+ &:focus {
+
+ &::-webkit-slider-thumb {
+ border: $kuiRangeBorderWidth solid $kuiColorPrimary;
+ }
+
+ &::-moz-range-thumb {
+ border: $kuiRangeBorderWidth solid $kuiColorPrimary;
+ }
+
+ &::-ms-thumb {
+ border: $kuiRangeBorderWidth solid $kuiColorPrimary;
+ }
+ &::-webkit-slider-runnable-track {
+ background-color: $kuiColorPrimary;
+ }
+
+ }
+
+ &::-webkit-slider-runnable-track {
+ @include track;
+ background: $kuiRangeTrackColor;
+ border: $kuiRangeTrackBorderWidth solid $kuiRangeTrackBorderColor;
+ border-radius: $kuiRangeTrackRadius;
+ }
+
+ &::-webkit-slider-thumb {
+ @include thumb;
+ -webkit-appearance: none;
+ margin-top: ((-$kuiRangeTrackBorderWidth * 2 + $kuiRangeTrackHeight) / 2) - ($kuiRangeThumbHeight / 2);
+ }
+
+ &::-moz-range-track {
+ @include track;
+ background: $kuiRangeTrackColor;
+ border: $kuiRangeTrackBorderWidth solid $kuiRangeTrackBorderColor;
+ border-radius: $kuiRangeTrackRadius;
+ }
+
+ &::-moz-range-thumb {
+ @include thumb;
+ }
+
+ &::-ms-track {
+ @include track;
+ background: transparent;
+ border-color: transparent;
+ border-width: ($kuiRangeThumbHeight / 2) 0;
+ color: transparent;
+ }
+
+ &::-ms-fill-lower {
+ background: $kuiRangeTrackColor;
+ border: $kuiRangeTrackBorderWidth solid $kuiRangeTrackBorderColor;
+ border-radius: $kuiRangeTrackRadius * 2;
+ }
+
+ &::-ms-fill-upper {
+ background: $kuiRangeTrackColor;
+ border: $kuiRangeTrackBorderWidth solid $kuiRangeTrackBorderColor;
+ border-radius: $kuiRangeTrackRadius * 2;
+ }
+
+ &::-ms-thumb {
+ @include thumb;
+ margin-top: 0;
+ }
+}
diff --git a/ui_framework/src/components/form/range/index.js b/ui_framework/src/components/form/range/index.js
new file mode 100644
index 0000000000000..ffb6d6bf2097e
--- /dev/null
+++ b/ui_framework/src/components/form/range/index.js
@@ -0,0 +1 @@
+export { KuiRange } from './range';
diff --git a/ui_framework/src/components/form/range/range.js b/ui_framework/src/components/form/range/range.js
new file mode 100644
index 0000000000000..36b3b514f81f2
--- /dev/null
+++ b/ui_framework/src/components/form/range/range.js
@@ -0,0 +1,36 @@
+import React, {
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+
+export const KuiRange = ({ className, id, name, min, max, value, ...rest }) => {
+ const classes = classNames('kuiRange', className);
+
+ return (
+
+ );
+};
+
+KuiRange.propTypes = {
+ name: PropTypes.string.isRequired,
+ id: PropTypes.string,
+ min: PropTypes.number.isRequired,
+ max: PropTypes.number.isRequired,
+ value: PropTypes.string,
+};
+
+KuiRange.defaultProps = {
+ value: undefined,
+ min: 1,
+ max: 100,
+};
+
diff --git a/ui_framework/src/components/form/range/range.test.js b/ui_framework/src/components/form/range/range.test.js
new file mode 100644
index 0000000000000..5641e202ad207
--- /dev/null
+++ b/ui_framework/src/components/form/range/range.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../../test/required_props';
+
+import { KuiRange } from './range';
+
+describe('KuiRange', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/form/select/_index.scss b/ui_framework/src/components/form/select/_index.scss
new file mode 100644
index 0000000000000..e16bb999b26fd
--- /dev/null
+++ b/ui_framework/src/components/form/select/_index.scss
@@ -0,0 +1 @@
+@import 'select';
diff --git a/ui_framework/src/components/form/select/_select.scss b/ui_framework/src/components/form/select/_select.scss
new file mode 100644
index 0000000000000..50c8ca17fb418
--- /dev/null
+++ b/ui_framework/src/components/form/select/_select.scss
@@ -0,0 +1,10 @@
+.kuiSelect {
+ appearance: none;
+ @include kuiFieldStyle;
+
+ &::-ms-expand {
+ display: none;
+ }
+}
+
+
diff --git a/ui_framework/src/components/form/select/index.js b/ui_framework/src/components/form/select/index.js
new file mode 100644
index 0000000000000..b006942416d67
--- /dev/null
+++ b/ui_framework/src/components/form/select/index.js
@@ -0,0 +1 @@
+export { KuiSelect } from './select';
diff --git a/ui_framework/src/components/form/select/select.js b/ui_framework/src/components/form/select/select.js
new file mode 100644
index 0000000000000..b4ba9e0a87183
--- /dev/null
+++ b/ui_framework/src/components/form/select/select.js
@@ -0,0 +1,27 @@
+import React, {
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+
+export const KuiSelect = ({ className, options, id, name, ...rest }) => {
+ const classes = classNames('kuiSelect', className);
+
+ return (
+
+ {options.map((option, index) => {
+ return {option.text} ;
+ })}
+
+ );
+};
+
+KuiSelect.propTypes = {
+ name: PropTypes.string.isRequired,
+ id: PropTypes.string,
+ options: PropTypes.arrayOf(React.PropTypes.object).isRequired,
+};
diff --git a/ui_framework/src/components/form/select/select.test.js b/ui_framework/src/components/form/select/select.test.js
new file mode 100644
index 0000000000000..755d95488561c
--- /dev/null
+++ b/ui_framework/src/components/form/select/select.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../../test/required_props';
+
+import { KuiSelect } from './select';
+
+describe('KuiSelect', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/form/switch/_index.scss b/ui_framework/src/components/form/switch/_index.scss
new file mode 100644
index 0000000000000..d77dd0ef13e79
--- /dev/null
+++ b/ui_framework/src/components/form/switch/_index.scss
@@ -0,0 +1,5 @@
+$kuiSwitchHeight: $kuiSizeL;
+$kuiSwitchWidth: ($kuiSizeL * 2) + $kuiSizeXS;
+$kuiSwitchThumbSize: $kuiSizeL;
+
+@import 'switch';
diff --git a/ui_framework/src/components/form/switch/_switch.scss b/ui_framework/src/components/form/switch/_switch.scss
new file mode 100644
index 0000000000000..c98ad07f972cc
--- /dev/null
+++ b/ui_framework/src/components/form/switch/_switch.scss
@@ -0,0 +1,130 @@
+.kuiSwitch {
+ position: relative;
+ display: inline-block;
+ height: $kuiSwitchHeight;
+ cursor: pointer;
+
+ /**
+ * 1. The label is our main clickable area. It sits above the actual switch.
+ */
+ .kuiSwitch__label {
+ position: absolute;
+ left: 0;
+ padding-left: $kuiSwitchWidth + $kuiSizeS; // 1
+ z-index: 2; // 1
+ line-height: $kuiSwitchHeight;
+ font-size: $kuiFontSizeS;
+ cursor: pointer;
+ }
+
+ /**
+ * 1. The input is "hidden" but still focusable.
+ */
+ .kuiSwitch__input {
+ position: absolute;
+ opacity: 0; // 1
+ z-index: -1; // 1
+ }
+
+ .kuiSwitch__input:focus + .kuiSwitch__body {
+ background: $kuiColorEmptyShade;
+
+ .kuiSwitch__thumb {
+ border-color: $kuiColorPrimary;
+ background-color: $kuiColorPrimary;
+ }
+ }
+
+ .kuiSwitch__body {
+ width: $kuiSwitchWidth;
+ height: $kuiSwitchHeight;
+ background: $kuiFormBackgroundColor;
+ border: $kuiBorderThin;
+ display: inline-block;
+ position: relative;
+ border-radius: $kuiSwitchHeight;
+ vertical-align: middle;
+ }
+
+ /**
+ * 1. Accounts for the border on the body.
+ */
+ .kuiSwitch__thumb {
+ position: absolute;
+ width: $kuiSwitchHeight;
+ height: $kuiSwitchHeight;
+ display: inline-block;
+ background-color: $kuiColorEmptyShade;
+ left: $kuiSwitchWidth - $kuiSwitchThumbSize;
+ top: -1px; // 1
+ border-radius: 50%;
+ border: $kuiBorderThin;
+ transition: border-color $kuiAnimSpeedNormal $kuiAnimSlightBounce, background-color $kuiAnimSpeedNormal $kuiAnimSlightBounce, left $kuiAnimSpeedNormal $kuiAnimSlightBounce, transform $kuiAnimSpeedNormal $kuiAnimSlightBounce;
+ z-index: 1;
+ }
+
+ .kuiSwitch__track {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ overflow: hidden;
+ border-radius: $kuiSwitchHeight;
+ }
+
+ /**
+ * 1. Mask is used to color the svg. Text color is used so works regardless of theme.
+ */
+ .kuiSwitch__icon {
+ position: absolute;
+ right: -($kuiSwitchWidth - ($kuiSwitchThumbSize / 2));
+ top: 0;
+ bottom: 0;
+ width: $kuiSwitchWidth - ($kuiSwitchThumbSize / 2) + $kuiSizeS;
+ transition: left $kuiAnimSpeedNormal $kuiAnimSlightBounce, right $kuiAnimSpeedNormal $kuiAnimSlightBounce;
+ mask: url('../src/components/icon/assets/cross.svg') center center no-repeat; // 1
+ background-color: $kuiTextColor; // 1
+ }
+
+ .kuiSwitch__icon--checked {
+ right: auto;
+ left: -$kuiSizeS;
+ background-color: $kuiTextColor;
+ mask: url('../src/components/icon/assets/check.svg') center center no-repeat;
+ }
+
+ /**
+ * The thumb is slightly scaled when in use.
+ */
+ &:hover {
+ .kuiSwitch__thumb {
+ transform: scale(1.05);
+ }
+ }
+
+ &:active {
+ .kuiSwitch__thumb {
+ transform: scale(.95);
+ }
+ }
+
+ /**
+ * When input is not checked, we shift around the positioning of sibling/child selectors.
+ */
+ .kuiSwitch__input:not(:checked) ~ .kuiSwitch__body {
+
+ .kuiSwitch__thumb {
+ left: -1px;
+ }
+
+ .kuiSwitch__icon {
+ right: -$kuiSizeS;
+
+ &.kuiSwitch__icon--checked {
+ right: auto;
+ left: -($kuiSwitchWidth - ($kuiSwitchThumbSize / 2));
+ }
+ }
+ }
+}
diff --git a/ui_framework/src/components/form/switch/index.js b/ui_framework/src/components/form/switch/index.js
new file mode 100644
index 0000000000000..5d52adb1d6c7c
--- /dev/null
+++ b/ui_framework/src/components/form/switch/index.js
@@ -0,0 +1 @@
+export { KuiSwitch } from './switch';
diff --git a/ui_framework/src/components/form/switch/switch.js b/ui_framework/src/components/form/switch/switch.js
new file mode 100644
index 0000000000000..255e498764b55
--- /dev/null
+++ b/ui_framework/src/components/form/switch/switch.js
@@ -0,0 +1,33 @@
+import React, {
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+
+export const KuiSwitch = ({ label, id, name, defaultChecked, className, ...rest }) => {
+ const classes = classNames('kuiSwitch', className);
+
+ return (
+
+
+
+
+
+
+
+
+
+ {label}
+
+ );
+};
+
+KuiSwitch.propTypes = {
+ name: PropTypes.string.isRequired,
+ id: PropTypes.string,
+ label: PropTypes.string.isRequired,
+ defaultChecked: PropTypes.bool,
+};
+
+KuiSwitch.defaultProps = {
+ defaultChecked: false,
+};
diff --git a/ui_framework/src/components/form/switch/switch.test.js b/ui_framework/src/components/form/switch/switch.test.js
new file mode 100644
index 0000000000000..79bb53b29ac98
--- /dev/null
+++ b/ui_framework/src/components/form/switch/switch.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../../test/required_props';
+
+import { KuiSwitch } from './switch';
+
+describe('KuiSwitch', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/form/text_area/_index.scss b/ui_framework/src/components/form/text_area/_index.scss
new file mode 100644
index 0000000000000..f14b87660661e
--- /dev/null
+++ b/ui_framework/src/components/form/text_area/_index.scss
@@ -0,0 +1 @@
+@import 'text_area';
diff --git a/ui_framework/src/components/form/text_area/_text_area.scss b/ui_framework/src/components/form/text_area/_text_area.scss
new file mode 100644
index 0000000000000..97320031ead3f
--- /dev/null
+++ b/ui_framework/src/components/form/text_area/_text_area.scss
@@ -0,0 +1,3 @@
+.kuiTextArea {
+ @include kuiFieldStyle;
+}
diff --git a/ui_framework/src/components/form/text_area/index.js b/ui_framework/src/components/form/text_area/index.js
new file mode 100644
index 0000000000000..6d9dfe10b23dc
--- /dev/null
+++ b/ui_framework/src/components/form/text_area/index.js
@@ -0,0 +1 @@
+export { KuiTextArea } from './text_area';
diff --git a/ui_framework/src/components/form/text_area/text_area.js b/ui_framework/src/components/form/text_area/text_area.js
new file mode 100644
index 0000000000000..6c9e13b7f231b
--- /dev/null
+++ b/ui_framework/src/components/form/text_area/text_area.js
@@ -0,0 +1,33 @@
+import React, {
+ PropTypes,
+} from 'react';
+import classNames from 'classnames';
+
+export const KuiTextArea = ({ children, rows, name, id, placeholder, className, ...rest }) => {
+ const classes = classNames('kuiTextArea', className);
+
+ return (
+
+ );
+};
+
+KuiTextArea.propTypes = {
+ name: PropTypes.string.isRequired,
+ id: PropTypes.string,
+ placeholder: PropTypes.string,
+ rows: PropTypes.number,
+};
+
+KuiTextArea.defaultProps = {
+ rows: 6,
+};
+
diff --git a/ui_framework/src/components/form/text_area/text_area.test.js b/ui_framework/src/components/form/text_area/text_area.test.js
new file mode 100644
index 0000000000000..4014c2c8d412e
--- /dev/null
+++ b/ui_framework/src/components/form/text_area/text_area.test.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import { render } from 'enzyme';
+import { requiredProps } from '../../../test/required_props';
+
+import { KuiTextArea } from './text_area';
+
+describe('KuiTextArea', () => {
+ test('is rendered', () => {
+ const component = render(
+
+ );
+
+ expect(component)
+ .toMatchSnapshot();
+ });
+});
diff --git a/ui_framework/src/components/icon/_icon.scss b/ui_framework/src/components/icon/_icon.scss
index 87749f040d62a..994677dbed148 100644
--- a/ui_framework/src/components/icon/_icon.scss
+++ b/ui_framework/src/components/icon/_icon.scss
@@ -1,6 +1,7 @@
.kuiIcon {
display: inline-block;
vertical-align: middle;
+ fill: $kuiTextColor;
&:focus {
opacity: 1; // We often hide icons on hover. Make sure they appear on focus.
diff --git a/ui_framework/src/components/icon/assets/check.svg b/ui_framework/src/components/icon/assets/check.svg
new file mode 100644
index 0000000000000..71bda73605bc6
--- /dev/null
+++ b/ui_framework/src/components/icon/assets/check.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/ui_framework/src/components/icon/assets/lock.svg b/ui_framework/src/components/icon/assets/lock.svg
new file mode 100644
index 0000000000000..4dda0d8f86d1b
--- /dev/null
+++ b/ui_framework/src/components/icon/assets/lock.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/ui_framework/src/components/icon/icon.js b/ui_framework/src/components/icon/icon.js
index 2039fd39ca610..ddd719c1098c1 100644
--- a/ui_framework/src/components/icon/icon.js
+++ b/ui_framework/src/components/icon/icon.js
@@ -16,6 +16,8 @@ import '!!svg-sprite!./assets/search.svg';
import '!!svg-sprite!./assets/user.svg';
import '!!svg-sprite!./assets/help.svg';
import '!!svg-sprite!./assets/cross.svg';
+import '!!svg-sprite!./assets/check.svg';
+import '!!svg-sprite!./assets/lock.svg';
import '!!svg-sprite!./assets/arrow_up.svg';
import '!!svg-sprite!./assets/arrow_down.svg';
import '!!svg-sprite!./assets/arrow_left.svg';
@@ -40,6 +42,8 @@ const typeToIconMap = {
user: 'user',
help: 'help',
cross: 'cross',
+ check: 'check',
+ lock: 'lock',
arrowUp: 'arrow_up',
arrowDown: 'arrow_down',
arrowLeft: 'arrow_left',
diff --git a/ui_framework/src/components/index.js b/ui_framework/src/components/index.js
index c3f9ad5b40954..23cb80f7708fc 100644
--- a/ui_framework/src/components/index.js
+++ b/ui_framework/src/components/index.js
@@ -10,6 +10,21 @@ export {
KuiCallOut,
} from './call_out';
+export {
+ KuiForm,
+ KuiCheckbox,
+ KuiFieldNumber,
+ KuiFieldPassword,
+ KuiRadio,
+ KuiRange,
+ KuiFormRow,
+ KuiFieldSearch,
+ KuiSelect,
+ KuiSwitch,
+ KuiFieldText,
+ KuiTextArea,
+} from './form';
+
export {
KuiHeader,
KuiHeaderBreadcrumb,
diff --git a/ui_framework/src/components/index.scss b/ui_framework/src/components/index.scss
index 20d002454b7c8..017c8029b6277 100644
--- a/ui_framework/src/components/index.scss
+++ b/ui_framework/src/components/index.scss
@@ -1,6 +1,7 @@
@import 'avatar/index';
@import 'button/index';
@import 'call_out/index';
+@import 'form/index';
@import 'header/index';
@import 'icon/index';
@import 'key_pad_menu/index';
diff --git a/ui_framework/src/components/popover/_popover.scss b/ui_framework/src/components/popover/_popover.scss
index 2b6fb8efd2a28..61dc0dca34c25 100644
--- a/ui_framework/src/components/popover/_popover.scss
+++ b/ui_framework/src/components/popover/_popover.scss
@@ -10,7 +10,7 @@
.kuiPopover__body {
opacity: 1;
visibility: visible;
- z-index: $kuiZContent;
+ z-index: $kuiZContentMenu;
margin-top: $kuiSizeS;
}
}
diff --git a/ui_framework/src/global_styling/variables/_colors.scss b/ui_framework/src/global_styling/variables/_colors.scss
index cc78279cb3963..841ca00b0bff1 100644
--- a/ui_framework/src/global_styling/variables/_colors.scss
+++ b/ui_framework/src/global_styling/variables/_colors.scss
@@ -56,6 +56,7 @@ $kuiColorFullShade: #000 !default;
// $kuiColorDarkestShade: #F5F5F5;
// $kuiColorFullShade: #FFF;
// $kuiColorPrimary: tint($kuiColorPrimary, 30%);
+// $kuiColorDanger: tint($kuiColorDanger, 30%);
// Every color below must be based mathmatically on the set above.