Repository: uber-web/baseui
Pull request #103 (2018-08-02 18:01:17)

feat: select and autucomplete component, rfc
#### Major: Select and  Search\Autocomplete component

CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/uber-web/baseui?pullRequest=103) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you all sign our [Contributor License Agreement](https://cla-assistant.io/uber-web/baseui?pullRequest=103) before we can accept your contribution.<br/>**0** out of **2** committers have signed the CLA.<br/><br/>:x: DianaSuvorova<br/>:x: blackswanny<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/uber-web/baseui?pullRequest=103) it.</sub>
benshope: I have a hard time evaluating this as a single RFC because stateful autocompletes are so complex.  I would love to see us break this apart into several RFCs and land the stateless parts first:

1. Create stateless style components for the inputs, the list items, and the popover (and create stories showing them used individually and as a stateless autocomplete)
2. Create a virtual scroll component (and create stories showing it's usage)
3. Create string filter functions (perhaps with memoization) (and create stories showing their usage)
4. Create stateful stories showing all these ^ components composed together as an autocomplete
5. Create a stateful autocomplete component informed by what we learned in step 4 writing stateful autocomplete stories out of these individual components

What we gain by breaking this apart is a more human development process with steady progress, clear goals, and great visibility into each stage.
benshope: Although if we do want to go for it all in one go, we could completely avoid duplicating native props with an API like this:  ```
<StatefulSelect value="merp" onChange={this.onSelectChange} filterFunction={(inputValue, options => options.filter(option => option.contains(inputValue)))} renderSelect={(inputValue, onInputChange) => (<Select>
      {['hello', 'we', 'are', 'some', 'pills'].map(pillText => (
        <Pill text={pillText} onRemove={action(`pill/onRemove ${pillText}`)} />
      ))}
      <ExpandingInput
        text="yeee"
        input={<input onChange={onInputChange} value={inputValue} />}
      />
      <Suggestions>
        {['these', 'are', 'some', 'suggestions'].map((suggestion, i) => (
          <Suggestion
            className={i === 1 ? 'selected' : ''}
            onClick={action('autocomplete/onClick')}
            key={suggestion}
          >
            {suggestion}
          </Suggestions>
        ))}
      </Suggestions>
    </Select>)}>
```
gergelyke: @blackswanny do you think we can break this PR down to smaller PRs? I believe if we would do that we could move faster, as we could review things in a shorter amount of time.
blackswanny: @gergelyke i cannot split to smaller PRs right now as it has already been reviewed and there are just a few requests left for existing code in this PR. I should have split to smaller PRs before all of that.
nguyensomniac: To clarify—tags in the Storybook are placeholders and will be updated when we have a proper tag to use, yes?
ardok: @blackswanny 
Do you have an example of how a dev would use this component to build a hierarchy select dropdown with search box?
Just a rough idea would be enough.
blackswanny: @ardok You mean nested levels options? No, currently it's not supported, as `click` automatically selects option. But if option is replaced with the same nested dropdown (it is easy as its a separate subcomponent) and `click` is redefined to show dropdown, than it would be easy. If you think it's needed, we can make such change here
nguyensomniac: Some more design feedback

![frame 16](https://user-images.githubusercontent.com/6953488/44815373-335c5a00-ab94-11e8-8c27-d43608225be4.png)

blackswanny: @nguyensomniac fixed all 1-5 items , except some in 3). The styles are mostly after original Input. It has line-height 24 for main input exact control (line height is applicable only for inline elements). However, there is some padding for parent container, so total size is 62px, not 40px. So if I make 40px of most outer container, it will be totally different and small
![image](https://user-images.githubusercontent.com/1851469/44819220-e632b500-aba0-11e8-8c8b-39b507a14652.png)

blackswanny: As Tags work already started here, with @nguyensomniac we decided to extract Tags from this component to separate one and reuse it here
nguyensomniac: 40px in the outermost container is correct; these need to match input styles whenever possible. May help to look with 24px tall tags—i.e. text at 12px font-size, 20px line-height, 2px each for vertical padding
ardok: @blackswanny nah, just for future consideration because hierarchy dropdown is pretty big for my team. Especially for location and departments.

We have this currently:
![aug-29-2018 17-00-26](https://user-images.githubusercontent.com/1380581/44821964-0ec0ac00-abad-11e8-9956-fbcf2a3ac312.gif)

blackswanny: @ardok interesting case, I think we can just change Dropdown itself, no need to change Select logic. Do you want me to try PoC for this right now?
blackswanny: @nguyensomniac fixed

Patch:
diff --git a/.prettierignore b/.prettierignore
index 0e75fe557..5a19e8ace 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,3 +1,3 @@
 node_modules
 dist
-coverage
+coverage
\ No newline at end of file
diff --git a/rfcs/select-component.md b/rfcs/select-component.md
new file mode 100644
index 000000000..8c35530d0
--- /dev/null
+++ b/rfcs/select-component.md
@@ -0,0 +1,162 @@
+# Select Component
+
+### Exports
+
+- `StatefulSelect`
+- `StatefulSelectContainer`
+- `Select`
+- `StyledSelectInput`
+- `StyledOption`
+- `StyledDropDown`
+- `StyledLabel`
+- `StyledHint`
+
+### `Select` and `StatefulSelect` API
+
+- `type: TYPE.search | TYPE.select`:
+  type of component to be in select or search mode (with lookup input)
+- `selectedOptions: Array<Object>`:
+  Current selected options. Every option object has `id: string` and `label: string|Object`. Label is defaulted to display for selected option, otherwise see `getSelectedOptionLabel` method
+- `placeholder: ?string`:
+  Placeholder text if nothing is selected. Default is `Choose one...`
+- `rows: ?number`:
+  Represents maximum visible length of options, all other will be scrolled. If not defined, all options will be visible.
+- `options: Array<Object>`:
+  All Options in dropdown. Should be provided for Select and Search mode equally. Every option object has `id: string` and `label: string|Object`. Label is defaulted to display for option in dropdown, otherwise see `getOptionLabel` method. Optional `disabled: boolean` for option to be disabled from selection.
+- `label: ?React$Node`:
+  Component or String representing label for Select component. Default is `''`
+- `error: ?string`:
+  Error message and error state. Default is `''`
+- `multiple: ?boolean`:
+  Sets if multiple choices are allowed in Select component. Default is `false`
+- `simpleFilter: ?boolean`:
+  Sets if simple String lookup should filter options by default on keyUp event. It works only in Search mode and with `string` labels (case-insensitive). Custom keyUp handler is still called if provided. Default is `false`
+- `autoFocus: boolean`:
+  make the control focused (active). Default is `false`
+- `disabled: boolean`:
+  Disable control from being changed
+- `required: boolean`:
+  Mark control as required
+- `overrides: {}`
+  - `DropDown: ?React.ComponentType` component to use for dropdown list
+  - `Option: ?React.ComponentType` component to use for options in dropdown list
+  - `Root: ?React.ComponentType` component to use for most top of the select component
+  - `Input: ?React.ComponentType` component for Input showing current selected value(s). See `Input` Control of this framework for reference to override it's functionality.
+  - `SearchIcon: ?React.ComponentType` component for all icons appearing in Select component. It's provided `$type: ICON.loop | ICON.clearTag | ICON.clearAll | ICON.selected` to setup corresponding icon of Select component
+  - `Tag: ?React.ComponentType` component for selected options Tags shown in Input for multiple mode selection
+- `onChange: func(e: SyntheticInputEvent, params: Object)`:
+  handler for events on trigger element when option are changing selection or text of search input (in Search mode has changed). `params` has `type` indicating which action is performed, `id` and `label` of selected\unselected option and `selectedOptions` array of all of selected, has new `textValue` set in input (for `keyUp`)
+- `onMouseEnter: func`:
+  handler for events on trigger element
+- `onMouseLeave: func`:
+  handler for events on trigger element
+- `onFocus: func`:
+  handler for events on trigger element
+- `onBlur: func`:
+  handler for events on trigger element
+
+### `StatefulSelectContainer` API
+
+- `initialState: {}`
+  Initial state of an uncontrolled popover component.
+  - `selectedOptions` - an initial set of selected options. They are prepended to all Options array if not found there.
+- `stateReducer: (type: text, nextState: {}, currentState: {}, e: any, params: Object) => nextState`
+  A state change handler.
+  - `type` - state change type
+  - `nextState` - a new state changes that will be set
+  - `currentState` - current full state of the component
+  - `params` may contain `id` and `label` of selected option and `selectedOptions` array of all of selected, as well as new `textValue` set in input.
+- `options: func` should return `Select` instance with standard or customized inner elements.
+- `onChange: func(e: SyntheticInputEvent, params: Object)`:
+  handler for events on trigger element when option are changing selection or text of search input (in Search mode has changed). `params` has `type` indicating which action is performed, `id` and `label` of selected\unselected option and `selectedOptions` array of all of selected, has new `textValue` set in input (for `keyUp`)
+- `onMouseEnter: func`:
+  handler for events on trigger element
+- `onMouseLeave: func`:
+  handler for events on trigger element
+- `onFocus: func`:
+  handler for events on trigger element
+- `onBlur: func`:
+  handler for events on trigger element
+
+### Usage
+
+```js
+import {
+  StatefulSelect,
+  Select,
+  StyledRoot,
+  StyledInput,
+  StyledInputContainer,
+  StyledTag,
+  StyledSearchIcon,
+  StyledDropDown,
+  StyledOption,
+  ICON,
+  OPTIONS,
+  TYPE,
+} from './index';
+
+import {withStyle} from 'styletron-react';
+
+const CustomOption = withStyle(StyledOption, {
+  textColor: 'red',
+});
+
+export default () => {
+  const options = [
+    {
+      id: '1',
+      label: {
+        text: 'First',
+        imgSrc: '1.jpg',
+      },
+    },
+    {
+      id: '2',
+      disabled: true,
+      label: {
+        text: 'Second',
+        imgSrc: '2.jpg',
+      },
+    },
+  ];
+  return (
+    <React.Fragment>
+      <StatefulSelect
+        options={options}
+        initialState={{
+          selectedOptions: [
+            {
+              id: '3',
+              label: {
+                text: 'Third',
+                imgSrc: '3.jpg',
+              },
+            },
+          ],
+        }}
+        getOptionLabel={option => (
+          <span>
+            <img
+              style={{
+                borderRadius: '50%',
+                height: '75px',
+              }}
+              src={option.label.imgSrc}
+            />
+            {option.label.text}
+          </span>
+        )}
+        label="Select option..."
+        placeholder="Choose one..."
+        type={TYPE.select}
+        multiple={true}
+        onChange={this.onChange}
+        overrides={{
+          Option: props => <CustomOption>Select {props.children}</CustomOption>,
+        }}
+      />
+    </React.Fragment>
+  );
+};
+```
diff --git a/src/input/stories.js b/src/input/stories.js
index 8bc8c8e06..75a573c2d 100644
--- a/src/input/stories.js
+++ b/src/input/stories.js
@@ -371,6 +371,95 @@ storiesOf('Input', module)
       </React.Fragment>
     );
   })
+  .add('Input with Search tags', () => {
+    return (
+      <React.Fragment>
+        <Input
+          label="Input with search tags"
+          overrides={{
+            Input: withStyle(StyledInput, props => {
+              return {width: 'auto'};
+            }),
+            InputContainer: withStyle(StyledInputContainer, props => {
+              return {
+                flexWrap: 'wrap',
+                padding: '5px',
+                alignItems: 'center',
+                position: 'relative',
+                backgroundColor: 'white',
+                borderColor: '#276EF1',
+              };
+            }),
+            After: function After(props) {
+              const Img = styled('img', props => {
+                return {
+                  marginLeft: 'auto',
+                  position: 'absolute',
+                  right: '10px',
+                };
+              });
+              return (
+                <Img
+                  src={
+                    'data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 16C12.4183 16 16 12.4183 16 8C16 3.58173 12.4183 0 8 0C3.58173 0 0 3.58173 0 8C0 12.4183 3.58173 16 8 16ZM6.03033 4.96967C5.73743 4.67679 5.26257 4.67679 4.96967 4.96967C4.67676 5.26257 4.67676 5.73743 4.96967 6.03033L6.93933 8L4.96967 9.96967C4.67676 10.2626 4.67676 10.7374 4.96967 11.0303C5.26257 11.3232 5.73743 11.3232 6.03033 11.0303L8 9.06067L9.96967 11.0303C10.2626 11.3232 10.7374 11.3232 11.0303 11.0303C11.3232 10.7374 11.3232 10.2626 11.0303 9.96967L9.06067 8L11.0303 6.03033C11.3232 5.73743 11.3232 5.26257 11.0303 4.96967C10.7374 4.67679 10.2626 4.67679 9.96967 4.96967L8 6.93933L6.03033 4.96967Z" fill="#999999"/></svg>'
+                  }
+                />
+              );
+            },
+            Before: function Before(props) {
+              const Icon = styled('span', props => {
+                return {
+                  fontWeight: 'bold',
+                  display: 'flex',
+                  alignItems: 'center',
+                  padding: '5px',
+                  margin: '5px',
+                  borderWidth: '1px',
+                  borderColor: '#276EF1',
+                  color: '#276EF1',
+                  borderRadius: '7px',
+                  lineHeight: '24px',
+                  backgroundColor: '#EDF3FE',
+                };
+              });
+              const Img = styled('img', props => {
+                return {};
+              });
+              return (
+                <React.Fragment>
+                  <Img
+                    src={
+                      'data:image/svg+xml;utf8,<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 9L13 13M10 5C10 7.76142 7.76142 10 5 10C2.23858 10 0 7.76142 0 5C0 2.23858 2.23858 0 5 0C7.76142 0 10 2.23858 10 5Z" transform="translate(1 1)" stroke="#1B6DE0" stroke-width="2" stroke-linecap="round"/></svg>'
+                    }
+                  />
+                  {[
+                    'tag 1 to search',
+                    'tag 2 to search',
+                    'tag 3 to search',
+                    'tag 4 to search',
+                    'tag 5 to search',
+                    'tag 6 to search',
+                    'tag 7 to search',
+                    'tag 8 to search',
+                  ].map(text => (
+                    <Icon key={text}>
+                      {text}
+                      <Img
+                        src={
+                          'data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M0.195262 0.195262C0.455612 -0.0650874 0.877722 -0.0650874 1.13807 0.195262L3.33333 2.39052L5.5286 0.195262C5.78895 -0.0650874 6.21106 -0.0650874 6.4714 0.195262C6.73175 0.455612 6.73175 0.877722 6.4714 1.13807L4.27614 3.33333L6.4714 5.5286C6.73175 5.78895 6.73175 6.21106 6.4714 6.4714C6.21106 6.73175 5.78895 6.73175 5.5286 6.4714L3.33333 4.27614L1.13807 6.4714C0.877722 6.73175 0.455612 6.73175 0.195262 6.4714C-0.0650874 6.21106 -0.0650874 5.78895 0.195262 5.5286L2.39052 3.33333L0.195262 1.13807C-0.0650874 0.877722 -0.0650874 0.455612 0.195262 0.195262Z" transform="translate(4.66675 4.6665)" fill="#276EF1"/></svg>'
+                        }
+                      />
+                    </Icon>
+                  ))}
+                </React.Fragment>
+              );
+            },
+          }}
+          placeholder="Start searching"
+        />
+      </React.Fragment>
+    );
+  })
   .add('Input with Before and After', () => {
     return (
       <React.Fragment>
diff --git a/src/menu/stateful-container.js b/src/menu/stateful-container.js
index 487a26cdd..8a94820b6 100644
--- a/src/menu/stateful-container.js
+++ b/src/menu/stateful-container.js
@@ -109,8 +109,7 @@ export default class MenuStatefulContainer extends React.Component<
 
   getRequiredItemProps: GetRequiredItemPropsFnT = (item, index) => {
     const {highlightedIndex} = this.state;
-    const {getItemLabel, onItemSelect} = this.props;
-    const itemString = getItemLabel(item);
+    const {onItemSelect} = this.props;
     // Create and store ref or re-use
     let itemRef = this.refList[index];
     if (!itemRef) {
@@ -118,7 +117,7 @@ export default class MenuStatefulContainer extends React.Component<
       this.refList[index] = itemRef;
     }
     return {
-      key: `${itemString}-${index}`,
+      key: `${index}`,
       ref: itemRef,
       isHighlighted: highlightedIndex === index,
       // $FlowFixMe
diff --git a/src/menu/styled-components.js b/src/menu/styled-components.js
index 5e595848c..c85531975 100644
--- a/src/menu/styled-components.js
+++ b/src/menu/styled-components.js
@@ -11,7 +11,7 @@ type StyledListItemPropsT = {
   $isHighlighted: boolean,
 } & StyledPropsT;
 
-export const List = styled('ul', ({$theme}: StyledPropsT) => ({
+export const getListStyles = ({$theme}: StyledPropsT) => ({
   position: 'relative',
   display: 'flex',
   flexDirection: 'column',
@@ -23,22 +23,26 @@ export const List = styled('ul', ({$theme}: StyledPropsT) => ({
   paddingBottom: $theme.sizing.scale200,
   paddingLeft: $theme.sizing.scale400,
   paddingRight: $theme.sizing.scale400,
-}));
+});
 
-export const ListItem = styled(
-  'li',
-  ({$theme, $isHighlighted}: StyledListItemPropsT) => ({
-    ...$theme.typography.font400,
-    position: 'relative',
-    display: 'block',
-    color: $isHighlighted ? $theme.colors.primary : $theme.colors.black,
-    margin: 0,
-    cursor: 'pointer',
-    transitionProperty: 'color',
-    transitionDuration: $theme.animation.timing100,
-    transitionTimingFunction: $theme.animation.easeOutCurve,
-    ':hover': {
-      color: $theme.colors.primary400,
-    },
-  }),
-);
+export const getListItemStyles = ({
+  $theme,
+  $isHighlighted,
+}: StyledListItemPropsT) => ({
+  ...$theme.typography.font400,
+  position: 'relative',
+  display: 'block',
+  color: $isHighlighted ? $theme.colors.primary : $theme.colors.black,
+  margin: 0,
+  cursor: 'pointer',
+  transitionProperty: 'color',
+  transitionDuration: $theme.animation.timing100,
+  transitionTimingFunction: $theme.animation.easeOutCurve,
+  ':hover': {
+    color: $theme.colors.primary400,
+  },
+});
+
+export const List = styled('ul', getListStyles);
+
+export const ListItem = styled('li', getListItemStyles);
diff --git a/src/menu/types.js b/src/menu/types.js
index e3ba31381..3b1b991d0 100644
--- a/src/menu/types.js
+++ b/src/menu/types.js
@@ -6,7 +6,7 @@ import {STATE_CHANGE_TYPES} from './constants';
 
 export type ItemsT = Array<*>;
 
-export type GetItemLabelFnT = (item: *) => string;
+export type GetItemLabelFnT = (item: *) => React$Node;
 
 export type SetRootRefFnT = (ref: React$ElementRef<*>) => void;
 
diff --git a/src/select/constants.js b/src/select/constants.js
new file mode 100644
index 000000000..cc58a2029
--- /dev/null
+++ b/src/select/constants.js
@@ -0,0 +1,26 @@
+// @flow
+
+export const OPTIONS = {
+  single: 'single',
+  multiple: 'multiple',
+};
+
+export const TYPE = {
+  select: 'select',
+  search: 'search',
+};
+
+export const ICON = {
+  clearAll: 'clear_all',
+  clearTag: 'clear_tag',
+  select: 'select',
+  selected: 'selected',
+  loop: 'loop',
+};
+
+export const STATE_CHANGE_TYPE = {
+  select: 'select',
+  unselect: 'unselect',
+  clearAll: 'clear_all',
+  keyUp: 'key_up',
+};
diff --git a/src/select/dropdown.js b/src/select/dropdown.js
new file mode 100644
index 000000000..1064a589e
--- /dev/null
+++ b/src/select/dropdown.js
@@ -0,0 +1,113 @@
+/*
+MIT License
+
+Copyright (c) 2018 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+// @flow
+import * as React from 'react';
+import {
+  SearchIcon as StyledSearchIcon,
+  DropDown as StyledDropDown,
+  DropDownItem as StyledDropDownItem,
+  Option as StyledOption,
+} from './styled-components';
+
+import {ICON, STATE_CHANGE_TYPE} from './constants';
+
+import {StatefulMenu} from '../menu';
+import type {DropDownPropsT} from './types';
+import {getOverride} from '../helpers/overrides';
+
+const SelectDropDown = function(props: DropDownPropsT) {
+  const {
+    overrides: {
+      SearchIcon: SearchIconOverride,
+      DropDown: DropDownOverride,
+      DropDownItem: DropDownItemOverride,
+      Option: OptionOverride,
+    } = {},
+  } = props;
+  const SearchIcon = getOverride(SearchIconOverride) || StyledSearchIcon;
+  const DropDown = getOverride(DropDownOverride) || StyledDropDown;
+  const DropDownItem = getOverride(DropDownItemOverride) || StyledDropDownItem;
+  const Option = getOverride(OptionOverride) || StyledOption;
+  const {
+    options = [],
+    getOptionLabel,
+    isDropDownOpen,
+    selectedOptions,
+    onChange,
+    type,
+    rows,
+  } = props;
+  return options.length ? (
+    <StatefulMenu
+      getItemLabel={option => {
+        const $selected = selectedOptions.find(
+          selected => selected.id === option.id,
+        );
+        const events = option.disabled
+          ? {
+              onClickCapture: e => e.stopPropagation(),
+            }
+          : {
+              onClick: e =>
+                onChange(e, STATE_CHANGE_TYPE.select, option.id, option.label),
+            };
+        return (
+          <Option
+            disabled={option.disabled}
+            $selected={$selected}
+            {...events}
+            key={option.id}
+          >
+            {$selected && (
+              <SearchIcon
+                $type={ICON.selected}
+                src={
+                  'data:image/svg+xml;utf8,<svg width="11" height="10" viewBox="0 0 11 10" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 6L4 9L10 1" stroke="#1B6DE0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>'
+                }
+              />
+            )}
+            {getOptionLabel(option)}
+          </Option>
+        );
+      }}
+      overrides={{
+        /* eslint-disable-next-line react/display-name*/
+        List: listProps => (
+          <DropDown
+            {...listProps}
+            $rows={rows}
+            $type={type}
+            $isOpen={isDropDownOpen}
+          />
+        ),
+        ListItem: DropDownItem,
+      }}
+      items={options}
+    />
+  ) : (
+    <div />
+  );
+};
+SelectDropDown.displayName = 'SelectDropDown';
+export default SelectDropDown;
diff --git a/src/select/examples.js b/src/select/examples.js
new file mode 100644
index 000000000..3c07dca80
--- /dev/null
+++ b/src/select/examples.js
@@ -0,0 +1,315 @@
+// @flow
+/*
+MIT License
+
+Copyright (c) 2018 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+
+/* eslint-disable react/display-name*/
+
+import * as React from 'react';
+// Styled elements
+import {StatefulSelect, TYPE} from './index';
+import type {LabelT, OptionT} from './types';
+import {STATE_CHANGE_TYPE} from './constants';
+
+type ExamplePropsT = {
+  multiple?: boolean,
+  options?: Array<OptionT>,
+  simpleFilter?: boolean,
+  getOptionLabel?: LabelT => React$Node,
+  getSelectedOptionLabel?: LabelT => React$Node,
+};
+
+type ExampleStateT = {
+  options: Array<OptionT>,
+  selectedOptions?: Array<OptionT>,
+};
+
+const generateOptions = text => {
+  const options = [];
+  const optionsLength = Math.round(Math.random() * 10);
+  for (let i = 0; i < optionsLength; i++) {
+    const id = '' + Math.round(Math.random() * 10000);
+    const label = text + id;
+    options.push({id, label});
+  }
+  return options;
+};
+
+class ParentSearch extends React.Component<ExamplePropsT, ExampleStateT> {
+  static defaultProps: {} = {};
+  constructor(props: ExamplePropsT) {
+    super(props);
+    this.state = {
+      options: props.simpleFilter ? generateOptions('text to search') : [],
+      selectedOptions: [
+        {
+          id: '123',
+          label: 'label for 123',
+        },
+      ],
+    };
+  }
+  render() {
+    return (
+      <React.Fragment>
+        <StatefulSelect
+          rows={8}
+          options={this.state.options}
+          multiple={true}
+          simpleFilter={this.props.simpleFilter}
+          type={TYPE.search}
+          initialState={{
+            selectedOptions: this.state.selectedOptions,
+          }}
+          label="Search for tags"
+          placeholder="Start searching"
+          onChange={(e, {type, id = '', label, selectedOptions}) => {
+            switch (type) {
+              case STATE_CHANGE_TYPE.select:
+                // eslint-disable-next-line no-console
+                console.log('Selected id:' + id);
+                break;
+              case STATE_CHANGE_TYPE.unselect:
+                // eslint-disable-next-line no-console
+                console.log('Unselected id:' + id);
+                break;
+              case STATE_CHANGE_TYPE.clearAll:
+                // eslint-disable-next-line no-console
+                console.log('Cleared all tags');
+                break;
+              case STATE_CHANGE_TYPE.keyUp: {
+                // $FlowFixMe
+                let text = e.target.value;
+                let options = [];
+                if (!this.props.simpleFilter && text.length > 5) {
+                  options = generateOptions(text);
+                  this.setState({options: options});
+                }
+                break;
+              }
+            }
+          }}
+        />
+      </React.Fragment>
+    );
+  }
+}
+
+class ParentSelect extends React.Component<ExamplePropsT, ExampleStateT> {
+  static defaultProps: {} = {};
+  constructor(props: ExamplePropsT) {
+    super(props);
+    const {options = []} = props;
+    this.state = {
+      options: options.length
+        ? options
+        : [
+            {
+              id: '1',
+              label: 'label for 1',
+            },
+            {
+              id: '2',
+              label: 'label for 2',
+            },
+            {
+              id: '3',
+              label: 'label for 3',
+            },
+            {
+              id: '4',
+              label: 'label for 4',
+            },
+          ],
+      selectedOptions: options.length
+        ? [options[0]]
+        : [
+            {
+              id: '123',
+              label: 'preselected label for 123',
+            },
+          ],
+    };
+  }
+  render() {
+    return (
+      <React.Fragment>
+        <StatefulSelect
+          options={this.state.options}
+          getSelectedOptionLabel={this.props.getSelectedOptionLabel}
+          getOptionLabel={this.props.getOptionLabel}
+          multiple={this.props.multiple}
+          type={TYPE.select}
+          initialState={{
+            selectedOptions: this.state.selectedOptions,
+          }}
+          label="Select"
+          placeholder={this.props.multiple ? null : 'Choose one'}
+          onChange={(e, {type, id = '', label}) => {
+            if (type === STATE_CHANGE_TYPE.select) {
+              // eslint-disable-next-line no-console
+              console.log('Selected id:' + id);
+            }
+          }}
+        />
+      </React.Fragment>
+    );
+  }
+}
+
+export const suite = 'Select Test Suite';
+export const tests = {
+  AS_SEARCH_WITH_TAGS_ADDED: 'In search\\autocomplete mode with tags added',
+  AS_SEARCH_WITH_SIMPLE_SEARCH:
+    'In search\\autocomplete mode with simple search filter of options',
+  AS_SELECT_WITH_MULTIPLE_CHOICE: 'In Select multiple mode',
+  AS_SELECT_WITH_SINGLE_CHOICE: 'In Select single mode',
+  AS_SELECT_WITH_CUSTOM_LABELS: 'In Select multiple mode with Custom Labels',
+  AS_SELECT_WITH_CUSTOM_SELECTED_LABELS:
+    'In Select multiple mode with Custom Labels and Custom Selected labels',
+  AS_SELECT_WITH_DISABLED_CHOICES: 'In Select single mode with some disabled',
+};
+
+export default {
+  [tests.AS_SEARCH_WITH_TAGS_ADDED]: () => {
+    return <ParentSearch />;
+  },
+  [tests.AS_SEARCH_WITH_SIMPLE_SEARCH]: () => {
+    return <ParentSearch simpleFilter />;
+  },
+  [tests.AS_SELECT_WITH_MULTIPLE_CHOICE]: () => {
+    return <ParentSelect multiple={true} />;
+  },
+  [tests.AS_SELECT_WITH_SINGLE_CHOICE]: () => {
+    return <ParentSelect />;
+  },
+  [tests.AS_SELECT_WITH_CUSTOM_LABELS]: () => {
+    return (
+      <ParentSelect
+        multiple={true}
+        getOptionLabel={option => (
+          <span>
+            <img
+              style={{
+                borderRadius: '50%',
+                height: '75px',
+              }}
+              src={option.label.imgSrc}
+            />
+            {option.label.text}
+          </span>
+        )}
+        options={[
+          {
+            id: '1',
+            label: {
+              imgSrc: 'https://fusionjs.com/static/nadiia.37070301.jpg',
+              text: 'Nadiia',
+            },
+          },
+          {
+            id: '2',
+            label: {
+              imgSrc: 'https://fusionjs.com/static/mlmorg.c28c19d7.jpeg',
+              text: 'Matt',
+            },
+          },
+        ]}
+      />
+    );
+  },
+  [tests.AS_SELECT_WITH_CUSTOM_SELECTED_LABELS]: () => {
+    return (
+      <ParentSelect
+        multiple={true}
+        getOptionLabel={option => (
+          <span>
+            <img
+              style={{
+                borderRadius: '50%',
+                height: '75px',
+              }}
+              src={option.label.imgSrc}
+            />
+            {option.label.text}
+          </span>
+        )}
+        options={[
+          {
+            id: '1',
+            label: {
+              imgSrc: 'https://fusionjs.com/static/nadiia.37070301.jpg',
+              text: 'Nadiia',
+            },
+          },
+          {
+            id: '2',
+            label: {
+              imgSrc: 'https://fusionjs.com/static/mlmorg.c28c19d7.jpeg',
+              text: 'Matt',
+            },
+          },
+        ]}
+        getSelectedOptionLabel={option => (
+          <span>
+            <img
+              style={{
+                borderRadius: '50%',
+                height: '50px',
+              }}
+              src={option.label.imgSrc}
+            />
+            {option.label.text + ' SELECTED'}
+          </span>
+        )}
+      />
+    );
+  },
+  [tests.AS_SELECT_WITH_DISABLED_CHOICES]: () => {
+    return (
+      <ParentSelect
+        multiple={true}
+        options={[
+          {
+            id: '1',
+            label: 'label for 1',
+          },
+          {
+            id: '2',
+            label: 'label for 2',
+            disabled: true,
+          },
+          {
+            id: '3',
+            label: 'label for 3',
+          },
+          {
+            id: '4',
+            label: 'label for 4',
+            disabled: true,
+          },
+        ]}
+      />
+    );
+  },
+};
diff --git a/src/select/index.js b/src/select/index.js
new file mode 100644
index 000000000..d584408be
--- /dev/null
+++ b/src/select/index.js
@@ -0,0 +1,16 @@
+// @flow
+export {default as Select} from './select';
+export {default as StatefulSelect} from './stateful-select';
+export {default as StatefulSelectContainer} from './stateful-select-container';
+// Styled elements
+export {
+  Root as StyledRoot,
+  Input as StyledInput,
+  InputContainer as StyledInputContainer,
+  Tag as StyledTag,
+  SearchIcon as StyledSearchIcon,
+  DropDown as StyledDropDown,
+  Option as StyledOption,
+  DropDownItem as StyledDropDownItem,
+} from './styled-components';
+export {ICON, OPTIONS, TYPE} from './constants';
diff --git a/src/select/select.js b/src/select/select.js
new file mode 100644
index 000000000..1fe7e9f3a
--- /dev/null
+++ b/src/select/select.js
@@ -0,0 +1,341 @@
+// @flow
+import * as React from 'react';
+import {findDOMNode} from 'react-dom';
+import {
+  Root as StyledRoot,
+  Input as StyledInput,
+  InputContainer as StyledInputContainer,
+  Tag as StyledTag,
+  SearchIcon as StyledSearchIcon,
+} from './styled-components';
+
+import {Input as InputComponent} from '../input';
+import {ICON, TYPE, STATE_CHANGE_TYPE} from './constants';
+import SelectDropDown from './dropdown';
+import type {
+  DefaultPropsT,
+  LabelT,
+  OptionT,
+  PropsT,
+  StatelessStateT,
+  ChangeActionT,
+} from './types';
+import {getOverride} from '../helpers/overrides';
+
+class Select extends React.Component<PropsT, StatelessStateT> {
+  static defaultProps: DefaultPropsT = {
+    overrides: {},
+    selectedOptions: [],
+    options: [],
+    onChange: () => {},
+    onBlur: () => {},
+    onFocus: () => {},
+    onMouseEnter: () => {},
+    onMouseLeave: () => {},
+    onMouseDown: () => {},
+    onMouseUp: () => {},
+    error: '',
+    autoFocus: false,
+  };
+
+  state = {
+    filteredOptions: null,
+    textValue: this.props.textValue,
+    selectedOptions: this.props.selectedOptions,
+    isDropDownOpen: this.props.type === TYPE.search,
+  };
+
+  constructor(props: PropsT) {
+    super(props);
+    if (props.type === TYPE.select) {
+      const {selectedOptions} = this.state;
+      const options = this.getOptions();
+      selectedOptions.forEach(selectedOption => {
+        // selected option is not in all options and needs to be added on top
+        if (!options.find(selected => selected.id === selectedOption.id)) {
+          options.unshift({
+            id: selectedOption.id,
+            label: this.getOptionLabel(selectedOption),
+          });
+        }
+      });
+    }
+  }
+
+  componentDidMount() {
+    /* eslint-disable-next-line cup/no-undef  */
+    document.addEventListener('click', this.handleClickEvent, {
+      capture: true,
+    });
+  }
+
+  componentWillUnmount() {
+    /* eslint-disable-next-line cup/no-undef  */
+    document.removeEventListener('click', this.handleClickEvent, {
+      capture: true,
+    });
+  }
+
+  handleClickEvent = (event: MouseEvent) => {
+    // eslint-disable-next-line react/no-find-dom-node
+    const el = findDOMNode(this);
+    /* eslint-disable-next-line flowtype/no-weak-types */
+    if (el && !el.contains((event.target: any))) {
+      this.setState({isDropDownOpen: false});
+    }
+  };
+
+  onFocus = (e: SyntheticEvent<HTMLInputElement>) => {};
+
+  onBlur = (e: SyntheticEvent<HTMLInputElement>) => {};
+
+  onMouseEnter = (e: SyntheticEvent<HTMLInputElement>) => {};
+
+  onMouseLeave = (e: SyntheticEvent<HTMLInputElement>) => {};
+
+  onChange = (
+    e: SyntheticEvent<HTMLInputElement>,
+    type: ChangeActionT,
+    id?: string = '',
+    label?: LabelT,
+  ) => {
+    const multiple = this.isMultiple();
+    const selected = this.state.selectedOptions.find(tag => tag.id === id);
+    let selectedOptions;
+    switch (type) {
+      case STATE_CHANGE_TYPE.select:
+        if (!selected) {
+          selectedOptions = multiple ? this.state.selectedOptions.slice() : [];
+          selectedOptions.push({id, label});
+          this.setState({selectedOptions: selectedOptions});
+          if (this.props.type === TYPE.select && !multiple) {
+            this.setState({isDropDownOpen: false});
+          }
+          this.props.onChange(e, {type: type, id, label, selectedOptions});
+        } else if (multiple) {
+          selectedOptions = this.state.selectedOptions.filter(
+            selectedOption => selectedOption.id !== selected.id,
+          );
+          this.setState({
+            selectedOptions: selectedOptions,
+          });
+          this.props.onChange(e, {
+            type: STATE_CHANGE_TYPE.unselect,
+            id,
+            label,
+            selectedOptions,
+          });
+        }
+        break;
+      case STATE_CHANGE_TYPE.clearAll: {
+        const selectedOptions = [];
+        this.setState({selectedOptions: selectedOptions});
+        this.props.onChange(e, {type: type, selectedOptions});
+        break;
+      }
+      case STATE_CHANGE_TYPE.keyUp: {
+        // $FlowFixMe
+        const newTextValue = e.target.value;
+        this.setState({textValue: newTextValue});
+        if (this.props.simpleFilter) {
+          let filteredOptions = this.props.options.filter(
+            option =>
+              typeof option.label === 'string' &&
+              option.label.toLowerCase().indexOf(newTextValue.toLowerCase()) >=
+                0,
+          );
+          filteredOptions = filteredOptions.length
+            ? filteredOptions
+            : newTextValue
+              ? []
+              : null;
+          this.setState({filteredOptions});
+        }
+        this.props.onChange(e, {type: type, textValue: newTextValue});
+      }
+    }
+  };
+
+  getOptions() {
+    return this.state.filteredOptions || this.props.options || [];
+  }
+
+  render() {
+    const {overrides: {Root: RootOverride} = {}} = this.props;
+    const Root = getOverride(RootOverride) || StyledRoot;
+    return (
+      <Root>
+        {this.props.type === TYPE.search ? this.getSearch() : this.getSelect()}
+        {this.getDropDown()}
+      </Root>
+    );
+  }
+
+  getSelect() {
+    const {
+      overrides: {
+        Root: RootOverride,
+        Input: InputOverride,
+        SearchIcon: SearchIconOverride,
+      } = {},
+    } = this.props;
+    const Root = getOverride(RootOverride) || StyledRoot;
+    const Input = getOverride(InputOverride) || StyledInput;
+    const SearchIcon = getOverride(SearchIconOverride) || StyledSearchIcon;
+    const {label, placeholder} = this.props;
+    const {selectedOptions} = this.state;
+    return (
+      <div
+        onClick={() => {
+          this.setState({isDropDownOpen: !this.state.isDropDownOpen});
+        }}
+      >
+        <InputComponent
+          disabled={true}
+          label={label}
+          placeholder={!selectedOptions.length ? placeholder : ''}
+          overrides={{
+            Root: Root,
+            Input: Input,
+            After: () => (
+              <SearchIcon
+                $type={ICON.select}
+                src={
+                  'data:image/svg+xml;utf8,<svg width="12" height="6" viewBox="0 0 12 6" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5.29289 5.29289L0.853553 0.853553C0.538571 0.538571 0.761654 0 1.20711 0H10.7929C11.2383 0 11.4614 0.538571 11.1464 0.853554L6.70711 5.29289C6.31658 5.68342 5.68342 5.68342 5.29289 5.29289Z" transform="translate(12) scale(-1 1)" fill="#666666"/></svg>'
+                }
+              />
+            ),
+            Before: () => this.getMultipleSelections(),
+          }}
+        />
+      </div>
+    );
+  }
+
+  getSearch() {
+    const {
+      overrides: {
+        Input: InputOverride,
+        InputContainer: InputContainerOverride,
+        SearchIcon: SearchIconOverride,
+      } = {},
+    } = this.props;
+    const Input = getOverride(InputOverride) || StyledInput;
+    const InputContainer =
+      getOverride(InputContainerOverride) || StyledInputContainer;
+    const SearchIcon = getOverride(SearchIconOverride) || StyledSearchIcon;
+    const {label, placeholder, error} = this.props;
+    const {textValue} = this.state;
+    return (
+      <InputComponent
+        error={error}
+        label={label}
+        placeholder={placeholder}
+        value={textValue}
+        //$FlowFixMe
+        onChange={(e: SyntheticInputEvent<HTMLElement>) =>
+          this.setState({textValue: e.target.value})
+        }
+        overrides={{
+          Input: {
+            props: {
+              onKeyUp: e => this.onChange(e, STATE_CHANGE_TYPE.keyUp),
+            },
+            component: Input,
+          },
+          InputContainer: InputContainer,
+          After: () => (
+            <SearchIcon
+              onClick={e => this.onChange(e, STATE_CHANGE_TYPE.clearAll)}
+              $type={ICON.clearAll}
+              src={
+                'data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M8 16C12.4183 16 16 12.4183 16 8C16 3.58173 12.4183 0 8 0C3.58173 0 0 3.58173 0 8C0 12.4183 3.58173 16 8 16ZM6.03033 4.96967C5.73743 4.67679 5.26257 4.67679 4.96967 4.96967C4.67676 5.26257 4.67676 5.73743 4.96967 6.03033L6.93933 8L4.96967 9.96967C4.67676 10.2626 4.67676 10.7374 4.96967 11.0303C5.26257 11.3232 5.73743 11.3232 6.03033 11.0303L8 9.06067L9.96967 11.0303C10.2626 11.3232 10.7374 11.3232 11.0303 11.0303C11.3232 10.7374 11.3232 10.2626 11.0303 9.96967L9.06067 8L11.0303 6.03033C11.3232 5.73743 11.3232 5.26257 11.0303 4.96967C10.7374 4.67679 10.2626 4.67679 9.96967 4.96967L8 6.93933L6.03033 4.96967Z" fill="#999999"/></svg>'
+              }
+            />
+          ),
+          Before: () => this.getMultipleSelections(),
+        }}
+      />
+    );
+  }
+
+  getMultipleSelections() {
+    const {
+      overrides: {Tag: TagOverride, SearchIcon: SearchIconOverride} = {},
+      type,
+    } = this.props;
+    const Tag = getOverride(TagOverride) || StyledTag;
+    const SearchIcon = getOverride(SearchIconOverride) || StyledSearchIcon;
+    const {selectedOptions} = this.state;
+    const multiple = this.isMultiple();
+    return (
+      <React.Fragment>
+        {type === TYPE.search && (
+          <SearchIcon
+            $type={ICON.loop}
+            src={
+              'data:image/svg+xml;utf8,<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M9 9L13 13M10 5C10 7.76142 7.76142 10 5 10C2.23858 10 0 7.76142 0 5C0 2.23858 2.23858 0 5 0C7.76142 0 10 2.23858 10 5Z" transform="translate(1 1)" stroke="#1B6DE0" stroke-width="2" stroke-linecap="round"/></svg>'
+            }
+          />
+        )}
+        {selectedOptions.map(option => (
+          <Tag key={option.id} $multiple={multiple}>
+            {this.getSelectedOptionLabel(option)}
+            {multiple && (
+              <SearchIcon
+                onClick={e => {
+                  this.setState({
+                    selectedOptions: this.state.selectedOptions.filter(
+                      selectedOption => selectedOption.id !== option.id,
+                    ),
+                  });
+                  e.stopPropagation();
+                }}
+                $type={ICON.clearTag}
+                src={
+                  'data:image/svg+xml;utf8,<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" clip-rule="evenodd" d="M0.195262 0.195262C0.455612 -0.0650874 0.877722 -0.0650874 1.13807 0.195262L3.33333 2.39052L5.5286 0.195262C5.78895 -0.0650874 6.21106 -0.0650874 6.4714 0.195262C6.73175 0.455612 6.73175 0.877722 6.4714 1.13807L4.27614 3.33333L6.4714 5.5286C6.73175 5.78895 6.73175 6.21106 6.4714 6.4714C6.21106 6.73175 5.78895 6.73175 5.5286 6.4714L3.33333 4.27614L1.13807 6.4714C0.877722 6.73175 0.455612 6.73175 0.195262 6.4714C-0.0650874 6.21106 -0.0650874 5.78895 0.195262 5.5286L2.39052 3.33333L0.195262 1.13807C-0.0650874 0.877722 -0.0650874 0.455612 0.195262 0.195262Z" transform="translate(4.66675 4.6665)" fill="#276EF1"/></svg>'
+                }
+              />
+            )}
+          </Tag>
+        ))}
+      </React.Fragment>
+    );
+  }
+
+  getDropDown() {
+    const {overrides, type, rows} = this.props;
+    const options = this.getOptions();
+    const {isDropDownOpen, selectedOptions} = this.state;
+    const dropDownProps = {
+      rows,
+      type,
+      options,
+      overrides,
+      isDropDownOpen,
+      selectedOptions,
+      getOptionLabel: this.getOptionLabel.bind(this),
+      onChange: this.onChange.bind(this),
+    };
+    return <SelectDropDown {...dropDownProps} />;
+  }
+
+  getOptionLabel(option: OptionT) {
+    const {getOptionLabel} = this.props;
+    return getOptionLabel ? getOptionLabel(option) : option.label;
+  }
+
+  getSelectedOptionLabel(option: OptionT) {
+    const {getSelectedOptionLabel} = this.props;
+    return getSelectedOptionLabel
+      ? getSelectedOptionLabel(option)
+      : this.getOptionLabel(option);
+  }
+
+  isMultiple() {
+    const {type, multiple} = this.props;
+    return type === TYPE.search ? true : multiple;
+  }
+}
+
+export default Select;
diff --git a/src/select/stateful-select-container.js b/src/select/stateful-select-container.js
new file mode 100644
index 000000000..f42eaeb1c
--- /dev/null
+++ b/src/select/stateful-select-container.js
@@ -0,0 +1,128 @@
+/*
+MIT License
+
+Copyright (c) 2018 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+// @flow
+import React from 'react';
+import {STATE_CHANGE_TYPE} from './constants';
+import type {
+  StatefulContainerPropsT,
+  StateReducerT,
+  DefaultStatefulPropsT,
+  StateT,
+  ParamsT,
+  ChangeActionT,
+} from './types';
+
+const defaultStateReducer: StateReducerT = (type, nextState) => nextState;
+
+class StatefulSelectContainer extends React.Component<
+  StatefulContainerPropsT,
+  StateT,
+> {
+  static defaultProps: DefaultStatefulPropsT = {
+    initialState: {
+      selectedOptions: [],
+    },
+    stateReducer: defaultStateReducer,
+    onChange: () => {},
+    onMouseEnter: () => {},
+    onMouseLeave: () => {},
+    onFocus: () => {},
+    onBlur: () => {},
+  };
+
+  state = {...this.props.initialState};
+
+  onChange = (e: SyntheticInputEvent<HTMLInputElement>, params: ParamsT) => {
+    this.stateReducer(params.type, e, params);
+    const {onChange} = this.props;
+    onChange && onChange(e, params);
+  };
+
+  onMouseEnter = (e: SyntheticInputEvent<HTMLInputElement>) => {
+    const {onMouseEnter} = this.props;
+    onMouseEnter && onMouseEnter(e);
+  };
+
+  onMouseLeave = (e: SyntheticInputEvent<HTMLInputElement>) => {
+    const {onMouseLeave} = this.props;
+    onMouseLeave && onMouseLeave(e);
+  };
+
+  onFocus = (e: SyntheticInputEvent<HTMLInputElement>) => {
+    const {onFocus} = this.props;
+    onFocus && onFocus(e);
+  };
+
+  onBlur = (e: SyntheticInputEvent<HTMLInputElement>) => {
+    const {onBlur} = this.props;
+    onBlur && onBlur(e);
+  };
+
+  stateReducer = (
+    type: ChangeActionT,
+    e: SyntheticInputEvent<HTMLInputElement>,
+    params: ParamsT,
+  ) => {
+    let nextState = {};
+    switch (type) {
+      case STATE_CHANGE_TYPE.select:
+      case STATE_CHANGE_TYPE.unselect:
+      case STATE_CHANGE_TYPE.clearAll:
+        nextState = {
+          selectedOptions: params.selectedOptions,
+        };
+        break;
+      case STATE_CHANGE_TYPE.keyUp: {
+        nextState = {
+          textValue: params.textValue,
+        };
+        break;
+      }
+    }
+    const {stateReducer} = this.props;
+    const newState = stateReducer(type, nextState, this.state, e, params);
+    this.setState(newState);
+  };
+
+  render() {
+    const {
+      children = (childProps: {}) => null, // eslint-disable-line no-unused-vars
+      initialState, // eslint-disable-line no-unused-vars
+      stateReducer, // eslint-disable-line no-unused-vars
+      ...rest
+    } = this.props;
+    const {onChange, onMouseEnter, onMouseLeave, onFocus, onBlur} = this;
+    return children({
+      ...rest,
+      ...this.state,
+      onChange,
+      onMouseEnter,
+      onMouseLeave,
+      onFocus,
+      onBlur,
+    });
+  }
+}
+
+export default StatefulSelectContainer;
diff --git a/src/select/stateful-select.js b/src/select/stateful-select.js
new file mode 100644
index 000000000..489cf8292
--- /dev/null
+++ b/src/select/stateful-select.js
@@ -0,0 +1,41 @@
+/*
+MIT License
+
+Copyright (c) 2018 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+// @flow
+import React from 'react';
+// eslint-disable-next-line import/no-named-default
+import {default as StatefulContainer} from './stateful-select-container';
+// eslint-disable-next-line import/no-named-default
+import {default as Select} from './select';
+import type {PropsT, StatefulSelectPropsT} from './types';
+// Styled elements
+
+const StatefulSelect = function(props: StatefulSelectPropsT) {
+  return (
+    <StatefulContainer {...props}>
+      {(childrenProps: PropsT) => <Select {...childrenProps} />}
+    </StatefulContainer>
+  );
+};
+StatefulSelect.displayName = 'StatefulSelect';
+export default StatefulSelect;
diff --git a/src/select/stories.js b/src/select/stories.js
new file mode 100644
index 000000000..15187f7c4
--- /dev/null
+++ b/src/select/stories.js
@@ -0,0 +1,16 @@
+// @flow
+/* global module */
+import {storiesOf} from '@storybook/react';
+
+// Styled elements
+import examples from './examples';
+import {withReadme} from 'storybook-readme';
+//$FlowFixMe
+import SelectReadme from '../../rfcs/select-component.md';
+
+Object.entries(examples).forEach(([description, example]) =>
+  storiesOf('Select', module)
+    .addDecorator(withReadme(SelectReadme))
+    // $FlowFixMe
+    .add(description, example),
+);
diff --git a/src/select/styled-components.js b/src/select/styled-components.js
new file mode 100644
index 000000000..cee8b89c2
--- /dev/null
+++ b/src/select/styled-components.js
@@ -0,0 +1,170 @@
+// @flow
+import {styled} from '../styles/index';
+import {ICON, TYPE} from './constants';
+
+import {SIZE} from '../input';
+import {
+  getInputStyles,
+  getInputContainerStyles,
+} from '../input/styled-components';
+
+import {getListStyles, getListItemStyles} from '../menu/styled-components';
+
+export const Root = styled('div', props => {
+  return {
+    position: 'relative',
+  };
+});
+
+export const Input = styled('input', props => {
+  return {
+    ...getInputStyles({...props, $size: SIZE.default}),
+    cursor: 'pointer',
+    width: 'auto',
+    flexGrow: '1',
+  };
+});
+
+export const InputContainer = styled('div', props => {
+  const {$theme, $isFocused} = props;
+  const {
+    colors: {mono700},
+    sizing: {scale300, scale1000},
+  } = $theme;
+  const color = $isFocused ? {} : {borderColor: mono700};
+  return {
+    ...getInputContainerStyles({...props, $size: SIZE.default}),
+    height: scale1000,
+    flexWrap: 'wrap',
+    padding: scale300,
+    alignItems: 'center',
+    position: 'relative',
+    ...color,
+  };
+});
+
+export const Tag = styled('span', props => {
+  const {$theme, $multiple} = props;
+  const {
+    colors: {primary400, mono400},
+    sizing: {scale800, scale0},
+    typography: {font200},
+  } = $theme;
+  return $multiple
+    ? {
+        ...font200,
+        height: scale800,
+        fontWeight: 'bold',
+        display: 'flex',
+        alignItems: 'center',
+        paddingTop: scale0,
+        paddingBottom: scale0,
+        paddingRight: '5px',
+        paddingLeft: '5px',
+        margin: '5px',
+        borderWidth: '1px',
+        borderColor: primary400,
+        color: primary400,
+        borderRadius: '7px',
+        backgroundColor: mono400,
+      }
+    : {
+        ...getInputStyles({...props, $size: SIZE.default, $disabled: true}),
+        cursor: 'pointer',
+        width: 'auto',
+        flexGrow: '1',
+        ':hover': {
+          cursor: 'pointer',
+        },
+      };
+});
+
+export const SearchIcon = styled('img', props => {
+  const {$theme} = props;
+  const {
+    sizing: {scale300, scale600, scale500},
+  } = $theme;
+  switch (props.$type) {
+    case ICON.clearAll:
+      return {
+        marginLeft: 'auto',
+        position: 'absolute',
+        right: scale600,
+        cursor: 'pointer',
+      };
+    case ICON.select:
+      return {
+        marginRight: scale500,
+      };
+    case ICON.selected:
+      return {
+        paddingRight: scale300,
+      };
+    case ICON.loop:
+      return {
+        paddingLeft: scale300,
+      };
+    case ICON.clearTag:
+    default:
+      return {};
+  }
+});
+
+export const DropDown = styled('ul', props => {
+  const {$theme, $isOpen, $type, $rows} = props;
+  const {
+    sizing: {scale300, scale600, scale1000},
+  } = $theme;
+  return {
+    ...getListStyles(props),
+    height: $rows ? parseInt(scale600) * $rows + 'px' : null,
+    overflowY: $rows ? 'scroll' : null,
+    top: $type === TYPE.select ? scale1000 : null,
+    display: !$isOpen ? 'none' : null,
+    width: '96%',
+    position: 'absolute',
+    padding: scale600,
+    listStyle: 'none',
+    borderRadius: scale300,
+    boxShadow: $theme.lighting.shadow600,
+  };
+});
+
+export const DropDownItem = styled('li', props => {
+  const {$theme} = props;
+  const {
+    sizing: {scale600},
+  } = $theme;
+  return {
+    ...getListItemStyles(props),
+    lineHeight: scale600,
+  };
+});
+
+export const Option = styled('div', props => {
+  const {$selected, disabled, $theme} = props;
+  const {
+    colors: {mono700, primary400},
+    sizing: {scale300},
+  } = $theme;
+  const padding = $selected
+    ? {
+        paddingTop: scale300,
+        paddingBottom: scale300,
+        paddingRight: '0px',
+        paddingLeft: '0px',
+      }
+    : {
+        paddingTop: scale300,
+        paddingBottom: scale300,
+        paddingRight: '18px',
+        paddingLeft: '18px',
+      };
+  return {
+    ':hover': {
+      cursor: disabled ? 'not-allowed' : 'pointer',
+    },
+    color: disabled ? mono700 : $selected ? primary400 : null,
+    ...padding,
+  };
+});
diff --git a/src/select/types.js b/src/select/types.js
new file mode 100644
index 000000000..42d4b7c1b
--- /dev/null
+++ b/src/select/types.js
@@ -0,0 +1,174 @@
+/*
+MIT License
+
+Copyright (c) 2018 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+// @flow
+
+import type {OverrideT} from '../helpers/overrides';
+import {STATE_CHANGE_TYPE} from './constants';
+
+export type ChangeActionT = $Values<typeof STATE_CHANGE_TYPE>;
+
+export type LabelT = *;
+export type OptionT = {
+  id: string,
+  label: LabelT,
+  disabled?: boolean,
+};
+
+export type ParamsT = {
+  id?: string,
+  label?: LabelT,
+  selectedOptions?: Array<OptionT>,
+  textValue?: string,
+  type: ChangeActionT,
+};
+
+export type OverridesT = {
+  Root?: OverrideT<*>,
+  Input?: OverrideT<*>,
+  SearchIcon?: OverrideT<*>,
+  InputContainer?: OverrideT<*>,
+  Tag?: OverrideT<*>,
+  Option?: OverrideT<*>,
+  DropDown?: OverrideT<*>,
+  SearchIcon?: OverrideT<*>,
+  DropDownItem?: OverrideT<*>,
+};
+
+export type OverridesDropDownT = {
+  Option?: OverrideT<*>,
+  DropDown?: OverrideT<*>,
+  SearchIcon?: OverrideT<*>,
+  DropDownItem?: OverrideT<*>,
+};
+
+export type DefaultPropsT = {
+  overrides?: OverridesT,
+  options?: Array<OptionT>,
+  error: string,
+  autoFocus: boolean,
+  onChange: (e: SyntheticEvent<HTMLInputElement>, params: ParamsT) => void,
+  onMouseEnter: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onMouseLeave: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onMouseDown: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onMouseUp: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onFocus: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onBlur: (e: SyntheticEvent<HTMLInputElement>) => void,
+};
+
+export type PropsT = {
+  options: Array<OptionT>,
+  overrides?: OverridesT,
+  selectedOptions: Array<OptionT>,
+  rows: number,
+  textValue: string,
+  multiple: boolean,
+  error?: string,
+  autoFocus?: boolean,
+  type?: string,
+  simpleFilter?: boolean,
+  label?: string,
+  placeholder?: string,
+  getOptionLabel?: OptionT => React$Node,
+  getSelectedOptionLabel?: OptionT => React$Node,
+  $theme?: *,
+  onChange: (e: SyntheticEvent<HTMLInputElement>, params: ParamsT) => void,
+  onMouseEnter: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onMouseLeave: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onMouseDown: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onMouseUp: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onFocus: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onBlur: (e: SyntheticEvent<HTMLInputElement>) => void,
+};
+
+export type StatelessStateT = {
+  textValue: string,
+  selectedOptions: Array<OptionT>,
+  isDropDownOpen: boolean,
+  filteredOptions?: ?Array<OptionT>,
+};
+
+export type StateT = {
+  textValue?: string,
+  selectedOptions?: Array<OptionT>,
+};
+
+export type StateReducerT = (
+  stateType: string,
+  nextState: StateT,
+  currentState: StateT,
+  event: SyntheticEvent<HTMLInputElement>,
+  params: ParamsT,
+) => StateT;
+
+export type DefaultStatefulPropsT = {
+  initialState: StateT,
+  children?: (*) => React$Node,
+  stateReducer: StateReducerT,
+  onChange: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onMouseEnter: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onMouseLeave: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onFocus: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onBlur: (e: SyntheticEvent<HTMLInputElement>) => void,
+};
+
+export type StatefulContainerPropsT = {
+  overrides?: OverridesT,
+  children?: (*) => React$Node,
+  initialState?: StateT,
+  stateReducer: StateReducerT,
+  onChange?: (e: SyntheticEvent<HTMLInputElement>, params: ParamsT) => void,
+  onMouseEnter?: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onMouseLeave?: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onFocus?: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onBlur?: (e: SyntheticEvent<HTMLInputElement>) => void,
+  autoFocus?: boolean,
+};
+
+export type StatefulSelectPropsT = {
+  overrides?: OverridesT,
+  options?: Array<OptionT>,
+  initialState?: StateT,
+  autoFocus?: boolean,
+  onChange?: (e: SyntheticEvent<HTMLInputElement>, params: ParamsT) => void,
+  onMouseEnter?: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onMouseLeave?: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onFocus?: (e: SyntheticEvent<HTMLInputElement>) => void,
+  onBlur?: (e: SyntheticEvent<HTMLInputElement>) => void,
+};
+
+export type DropDownPropsT = {
+  overrides?: OverridesDropDownT,
+  options: Array<OptionT>,
+  rows: number,
+  selectedOptions: Array<OptionT>,
+  isDropDownOpen: boolean,
+  type: string,
+  getOptionLabel: OptionT => React$Node,
+  onChange: (
+    e: SyntheticEvent<HTMLInputElement>,
+    type: ChangeActionT,
+    id: string,
+    label: LabelT,
+  ) => void,
+};


Test patch:
diff --git a/src/menu/__tests__/stateful-container.test.js b/src/menu/__tests__/stateful-container.test.js
index 9af316ad3..6b33a77a6 100644
--- a/src/menu/__tests__/stateful-container.test.js
+++ b/src/menu/__tests__/stateful-container.test.js
@@ -89,7 +89,7 @@ describe('Menu StatefulContainer', () => {
     const item = mockItems[0];
     const props = component.instance().getRequiredItemProps(item, 0);
     expect(props).toEqual({
-      key: 'item1-0',
+      key: '0',
       ref: React.createRef(),
       isHighlighted: false,
       onClick: props.onClick,
@@ -108,7 +108,7 @@ describe('Menu StatefulContainer', () => {
     const item = mockItems[0];
     const props = component.instance().getRequiredItemProps(item, 0);
     expect(props).toEqual({
-      key: 'item1-0',
+      key: '0',
       ref: React.createRef(),
       isHighlighted: true,
       onClick: props.onClick,
diff --git a/src/select/__tests__/__snapshots__/dropdown.test.js.snap b/src/select/__tests__/__snapshots__/dropdown.test.js.snap
new file mode 100644
index 000000000..69a5bae05
--- /dev/null
+++ b/src/select/__tests__/__snapshots__/dropdown.test.js.snap
@@ -0,0 +1,66 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Stateless Select Dropdown should render component with options  and drondown open is true: Component has correct render with options length 0 and dropdown open is %s true 1`] = `
+<ul
+  getOptionLabel={[MockFunction]}
+  isDropDownOpen={true}
+  options={Array []}
+  styled-component="true"
+/>
+`;
+
+exports[`Stateless Select Dropdown should render component with options [object Object],[object Object],[object Object],[object Object] and drondown open is false: Component has correct render with options length 4 and dropdown open is %s false 1`] = `
+<ul
+  getOptionLabel={[MockFunction]}
+  isDropDownOpen={false}
+  options={
+    Array [
+      Object {
+        "id": "1",
+        "label": "label for 1",
+      },
+      Object {
+        "id": "2",
+        "label": "label for 2",
+      },
+      Object {
+        "id": "3",
+        "label": "label for 3",
+      },
+      Object {
+        "id": "4",
+        "label": "label for 4",
+      },
+    ]
+  }
+  styled-component="true"
+/>
+`;
+
+exports[`Stateless Select Dropdown should render component with options [object Object],[object Object],[object Object],[object Object] and drondown open is true: Component has correct render with options length 4 and dropdown open is %s true 1`] = `
+<ul
+  getOptionLabel={[MockFunction]}
+  isDropDownOpen={true}
+  options={
+    Array [
+      Object {
+        "id": "1",
+        "label": "label for 1",
+      },
+      Object {
+        "id": "2",
+        "label": "label for 2",
+      },
+      Object {
+        "id": "3",
+        "label": "label for 3",
+      },
+      Object {
+        "id": "4",
+        "label": "label for 4",
+      },
+    ]
+  }
+  styled-component="true"
+/>
+`;
diff --git a/src/select/__tests__/__snapshots__/select.test.js.snap b/src/select/__tests__/__snapshots__/select.test.js.snap
new file mode 100644
index 000000000..52471f954
--- /dev/null
+++ b/src/select/__tests__/__snapshots__/select.test.js.snap
@@ -0,0 +1,767 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Stateless select should render component in search mode and false for multiple choice: Component has correct render in search mode and mutiple choice equal false 1`] = `
+<Select
+  autoFocus={false}
+  error=""
+  multiple={false}
+  onBlur={[MockFunction]}
+  onChange={[MockFunction]}
+  onFocus={[MockFunction]}
+  onMouseDown={[Function]}
+  onMouseEnter={[MockFunction]}
+  onMouseLeave={[MockFunction]}
+  onMouseUp={[Function]}
+  options={Array []}
+  overrides={Object {}}
+  selectedOptions={Array []}
+  type="search"
+>
+  <MockStyledComponent>
+    <div
+      styled-component="true"
+    >
+      <Input
+        autoFocus={false}
+        caption={null}
+        disabled={false}
+        endEnhancer={null}
+        error=""
+        id="bui-mock-id"
+        label={null}
+        onBlur={[Function]}
+        onChange={[Function]}
+        onFocus={[Function]}
+        overrides={
+          Object {
+            "After": [Function],
+            "Before": [Function],
+            "Input": Object {
+              "component": [Function],
+              "props": Object {
+                "onKeyUp": [Function],
+              },
+            },
+            "InputContainer": [Function],
+          }
+        }
+        required={false}
+        size="default"
+        startEnhancer={null}
+      >
+        <MockStyledComponent
+          $disabled={false}
+          $error=""
+          $isFocused={false}
+          $required={false}
+          $size="default"
+        >
+          <div
+            styled-component="true"
+          >
+            <BaseInput
+              adjoined="none"
+              autoFocus={false}
+              disabled={false}
+              error=""
+              id="bui-mock-id"
+              inputRef={
+                Object {
+                  "current": null,
+                }
+              }
+              onBlur={[Function]}
+              onChange={[Function]}
+              onFocus={[Function]}
+              overrides={
+                Object {
+                  "After": [Function],
+                  "Before": [Function],
+                  "Input": Object {
+                    "component": [Function],
+                    "props": Object {
+                      "onKeyUp": [Function],
+                    },
+                  },
+                  "InputContainer": [Function],
+                }
+              }
+              placeholder=""
+              required={false}
+              size="default"
+              type="text"
+              value=""
+            >
+              <MockStyledComponent
+                $adjoined="none"
+                $disabled={false}
+                $error=""
+                $isFocused={false}
+                $required={false}
+                $size="default"
+              >
+                <div
+                  styled-component="true"
+                >
+                  <Before
+                    $adjoined="none"
+                    $disabled={false}
+                    $error=""
+                    $isFocused={false}
+                    $required={false}
+                    $size="default"
+                  >
+                    <MockStyledComponent
+                      $type="loop"
+                      src="data:image/svg+xml;utf8,<svg width=\\"15\\" height=\\"15\\" viewBox=\\"0 0 15 15\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path d=\\"M9 9L13 13M10 5C10 7.76142 7.76142 10 5 10C2.23858 10 0 7.76142 0 5C0 2.23858 2.23858 0 5 0C7.76142 0 10 2.23858 10 5Z\\" transform=\\"translate(1 1)\\" stroke=\\"#1B6DE0\\" stroke-width=\\"2\\" stroke-linecap=\\"round\\"/></svg>"
+                    >
+                      <img
+                        src="data:image/svg+xml;utf8,<svg width=\\"15\\" height=\\"15\\" viewBox=\\"0 0 15 15\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path d=\\"M9 9L13 13M10 5C10 7.76142 7.76142 10 5 10C2.23858 10 0 7.76142 0 5C0 2.23858 2.23858 0 5 0C7.76142 0 10 2.23858 10 5Z\\" transform=\\"translate(1 1)\\" stroke=\\"#1B6DE0\\" stroke-width=\\"2\\" stroke-linecap=\\"round\\"/></svg>"
+                        styled-component="true"
+                      />
+                    </MockStyledComponent>
+                  </Before>
+                  <MockStyledComponent
+                    $adjoined="none"
+                    $disabled={false}
+                    $error=""
+                    $isFocused={false}
+                    $ref={
+                      Object {
+                        "current": null,
+                      }
+                    }
+                    $required={false}
+                    $size="default"
+                    aria-invalid={false}
+                    aria-required={false}
+                    disabled={false}
+                    id="bui-mock-id"
+                    onBlur={[Function]}
+                    onChange={[Function]}
+                    onFocus={[Function]}
+                    onKeyUp={[Function]}
+                    placeholder=""
+                    type="text"
+                    value=""
+                  >
+                    <input
+                      aria-invalid={false}
+                      aria-required={false}
+                      disabled={false}
+                      id="bui-mock-id"
+                      onBlur={[Function]}
+                      onChange={[Function]}
+                      onFocus={[Function]}
+                      onKeyUp={[Function]}
+                      placeholder=""
+                      styled-component="true"
+                      type="text"
+                      value=""
+                    />
+                  </MockStyledComponent>
+                  <After
+                    $adjoined="none"
+                    $disabled={false}
+                    $error=""
+                    $isFocused={false}
+                    $required={false}
+                    $size="default"
+                  >
+                    <MockStyledComponent
+                      $type="clear_all"
+                      onClick={[Function]}
+                      src="data:image/svg+xml;utf8,<svg width=\\"16\\" height=\\"16\\" viewBox=\\"0 0 16 16\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path fill-rule=\\"evenodd\\" clip-rule=\\"evenodd\\" d=\\"M8 16C12.4183 16 16 12.4183 16 8C16 3.58173 12.4183 0 8 0C3.58173 0 0 3.58173 0 8C0 12.4183 3.58173 16 8 16ZM6.03033 4.96967C5.73743 4.67679 5.26257 4.67679 4.96967 4.96967C4.67676 5.26257 4.67676 5.73743 4.96967 6.03033L6.93933 8L4.96967 9.96967C4.67676 10.2626 4.67676 10.7374 4.96967 11.0303C5.26257 11.3232 5.73743 11.3232 6.03033 11.0303L8 9.06067L9.96967 11.0303C10.2626 11.3232 10.7374 11.3232 11.0303 11.0303C11.3232 10.7374 11.3232 10.2626 11.0303 9.96967L9.06067 8L11.0303 6.03033C11.3232 5.73743 11.3232 5.26257 11.0303 4.96967C10.7374 4.67679 10.2626 4.67679 9.96967 4.96967L8 6.93933L6.03033 4.96967Z\\" fill=\\"#999999\\"/></svg>"
+                    >
+                      <img
+                        onClick={[Function]}
+                        src="data:image/svg+xml;utf8,<svg width=\\"16\\" height=\\"16\\" viewBox=\\"0 0 16 16\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path fill-rule=\\"evenodd\\" clip-rule=\\"evenodd\\" d=\\"M8 16C12.4183 16 16 12.4183 16 8C16 3.58173 12.4183 0 8 0C3.58173 0 0 3.58173 0 8C0 12.4183 3.58173 16 8 16ZM6.03033 4.96967C5.73743 4.67679 5.26257 4.67679 4.96967 4.96967C4.67676 5.26257 4.67676 5.73743 4.96967 6.03033L6.93933 8L4.96967 9.96967C4.67676 10.2626 4.67676 10.7374 4.96967 11.0303C5.26257 11.3232 5.73743 11.3232 6.03033 11.0303L8 9.06067L9.96967 11.0303C10.2626 11.3232 10.7374 11.3232 11.0303 11.0303C11.3232 10.7374 11.3232 10.2626 11.0303 9.96967L9.06067 8L11.0303 6.03033C11.3232 5.73743 11.3232 5.26257 11.0303 4.96967C10.7374 4.67679 10.2626 4.67679 9.96967 4.96967L8 6.93933L6.03033 4.96967Z\\" fill=\\"#999999\\"/></svg>"
+                        styled-component="true"
+                      />
+                    </MockStyledComponent>
+                  </After>
+                </div>
+              </MockStyledComponent>
+            </BaseInput>
+          </div>
+        </MockStyledComponent>
+      </Input>
+      <SelectDropDown
+        getOptionLabel={[Function]}
+        isDropDownOpen={true}
+        onChange={[Function]}
+        options={Array []}
+        overrides={Object {}}
+        selectedOptions={Array []}
+        type="search"
+      >
+        <div />
+      </SelectDropDown>
+    </div>
+  </MockStyledComponent>
+</Select>
+`;
+
+exports[`Stateless select should render component in search mode and true for multiple choice: Component has correct render in search mode and mutiple choice equal true 1`] = `
+<Select
+  autoFocus={false}
+  error=""
+  multiple={true}
+  onBlur={[MockFunction]}
+  onChange={[MockFunction]}
+  onFocus={[MockFunction]}
+  onMouseDown={[Function]}
+  onMouseEnter={[MockFunction]}
+  onMouseLeave={[MockFunction]}
+  onMouseUp={[Function]}
+  options={Array []}
+  overrides={Object {}}
+  selectedOptions={Array []}
+  type="search"
+>
+  <MockStyledComponent>
+    <div
+      styled-component="true"
+    >
+      <Input
+        autoFocus={false}
+        caption={null}
+        disabled={false}
+        endEnhancer={null}
+        error=""
+        id="bui-mock-id"
+        label={null}
+        onBlur={[Function]}
+        onChange={[Function]}
+        onFocus={[Function]}
+        overrides={
+          Object {
+            "After": [Function],
+            "Before": [Function],
+            "Input": Object {
+              "component": [Function],
+              "props": Object {
+                "onKeyUp": [Function],
+              },
+            },
+            "InputContainer": [Function],
+          }
+        }
+        required={false}
+        size="default"
+        startEnhancer={null}
+      >
+        <MockStyledComponent
+          $disabled={false}
+          $error=""
+          $isFocused={false}
+          $required={false}
+          $size="default"
+        >
+          <div
+            styled-component="true"
+          >
+            <BaseInput
+              adjoined="none"
+              autoFocus={false}
+              disabled={false}
+              error=""
+              id="bui-mock-id"
+              inputRef={
+                Object {
+                  "current": null,
+                }
+              }
+              onBlur={[Function]}
+              onChange={[Function]}
+              onFocus={[Function]}
+              overrides={
+                Object {
+                  "After": [Function],
+                  "Before": [Function],
+                  "Input": Object {
+                    "component": [Function],
+                    "props": Object {
+                      "onKeyUp": [Function],
+                    },
+                  },
+                  "InputContainer": [Function],
+                }
+              }
+              placeholder=""
+              required={false}
+              size="default"
+              type="text"
+              value=""
+            >
+              <MockStyledComponent
+                $adjoined="none"
+                $disabled={false}
+                $error=""
+                $isFocused={false}
+                $required={false}
+                $size="default"
+              >
+                <div
+                  styled-component="true"
+                >
+                  <Before
+                    $adjoined="none"
+                    $disabled={false}
+                    $error=""
+                    $isFocused={false}
+                    $required={false}
+                    $size="default"
+                  >
+                    <MockStyledComponent
+                      $type="loop"
+                      src="data:image/svg+xml;utf8,<svg width=\\"15\\" height=\\"15\\" viewBox=\\"0 0 15 15\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path d=\\"M9 9L13 13M10 5C10 7.76142 7.76142 10 5 10C2.23858 10 0 7.76142 0 5C0 2.23858 2.23858 0 5 0C7.76142 0 10 2.23858 10 5Z\\" transform=\\"translate(1 1)\\" stroke=\\"#1B6DE0\\" stroke-width=\\"2\\" stroke-linecap=\\"round\\"/></svg>"
+                    >
+                      <img
+                        src="data:image/svg+xml;utf8,<svg width=\\"15\\" height=\\"15\\" viewBox=\\"0 0 15 15\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path d=\\"M9 9L13 13M10 5C10 7.76142 7.76142 10 5 10C2.23858 10 0 7.76142 0 5C0 2.23858 2.23858 0 5 0C7.76142 0 10 2.23858 10 5Z\\" transform=\\"translate(1 1)\\" stroke=\\"#1B6DE0\\" stroke-width=\\"2\\" stroke-linecap=\\"round\\"/></svg>"
+                        styled-component="true"
+                      />
+                    </MockStyledComponent>
+                  </Before>
+                  <MockStyledComponent
+                    $adjoined="none"
+                    $disabled={false}
+                    $error=""
+                    $isFocused={false}
+                    $ref={
+                      Object {
+                        "current": null,
+                      }
+                    }
+                    $required={false}
+                    $size="default"
+                    aria-invalid={false}
+                    aria-required={false}
+                    disabled={false}
+                    id="bui-mock-id"
+                    onBlur={[Function]}
+                    onChange={[Function]}
+                    onFocus={[Function]}
+                    onKeyUp={[Function]}
+                    placeholder=""
+                    type="text"
+                    value=""
+                  >
+                    <input
+                      aria-invalid={false}
+                      aria-required={false}
+                      disabled={false}
+                      id="bui-mock-id"
+                      onBlur={[Function]}
+                      onChange={[Function]}
+                      onFocus={[Function]}
+                      onKeyUp={[Function]}
+                      placeholder=""
+                      styled-component="true"
+                      type="text"
+                      value=""
+                    />
+                  </MockStyledComponent>
+                  <After
+                    $adjoined="none"
+                    $disabled={false}
+                    $error=""
+                    $isFocused={false}
+                    $required={false}
+                    $size="default"
+                  >
+                    <MockStyledComponent
+                      $type="clear_all"
+                      onClick={[Function]}
+                      src="data:image/svg+xml;utf8,<svg width=\\"16\\" height=\\"16\\" viewBox=\\"0 0 16 16\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path fill-rule=\\"evenodd\\" clip-rule=\\"evenodd\\" d=\\"M8 16C12.4183 16 16 12.4183 16 8C16 3.58173 12.4183 0 8 0C3.58173 0 0 3.58173 0 8C0 12.4183 3.58173 16 8 16ZM6.03033 4.96967C5.73743 4.67679 5.26257 4.67679 4.96967 4.96967C4.67676 5.26257 4.67676 5.73743 4.96967 6.03033L6.93933 8L4.96967 9.96967C4.67676 10.2626 4.67676 10.7374 4.96967 11.0303C5.26257 11.3232 5.73743 11.3232 6.03033 11.0303L8 9.06067L9.96967 11.0303C10.2626 11.3232 10.7374 11.3232 11.0303 11.0303C11.3232 10.7374 11.3232 10.2626 11.0303 9.96967L9.06067 8L11.0303 6.03033C11.3232 5.73743 11.3232 5.26257 11.0303 4.96967C10.7374 4.67679 10.2626 4.67679 9.96967 4.96967L8 6.93933L6.03033 4.96967Z\\" fill=\\"#999999\\"/></svg>"
+                    >
+                      <img
+                        onClick={[Function]}
+                        src="data:image/svg+xml;utf8,<svg width=\\"16\\" height=\\"16\\" viewBox=\\"0 0 16 16\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path fill-rule=\\"evenodd\\" clip-rule=\\"evenodd\\" d=\\"M8 16C12.4183 16 16 12.4183 16 8C16 3.58173 12.4183 0 8 0C3.58173 0 0 3.58173 0 8C0 12.4183 3.58173 16 8 16ZM6.03033 4.96967C5.73743 4.67679 5.26257 4.67679 4.96967 4.96967C4.67676 5.26257 4.67676 5.73743 4.96967 6.03033L6.93933 8L4.96967 9.96967C4.67676 10.2626 4.67676 10.7374 4.96967 11.0303C5.26257 11.3232 5.73743 11.3232 6.03033 11.0303L8 9.06067L9.96967 11.0303C10.2626 11.3232 10.7374 11.3232 11.0303 11.0303C11.3232 10.7374 11.3232 10.2626 11.0303 9.96967L9.06067 8L11.0303 6.03033C11.3232 5.73743 11.3232 5.26257 11.0303 4.96967C10.7374 4.67679 10.2626 4.67679 9.96967 4.96967L8 6.93933L6.03033 4.96967Z\\" fill=\\"#999999\\"/></svg>"
+                        styled-component="true"
+                      />
+                    </MockStyledComponent>
+                  </After>
+                </div>
+              </MockStyledComponent>
+            </BaseInput>
+          </div>
+        </MockStyledComponent>
+      </Input>
+      <SelectDropDown
+        getOptionLabel={[Function]}
+        isDropDownOpen={true}
+        onChange={[Function]}
+        options={Array []}
+        overrides={Object {}}
+        selectedOptions={Array []}
+        type="search"
+      >
+        <div />
+      </SelectDropDown>
+    </div>
+  </MockStyledComponent>
+</Select>
+`;
+
+exports[`Stateless select should render component in select mode and false for multiple choice: Component has correct render in select mode and mutiple choice equal false 1`] = `
+<Select
+  autoFocus={false}
+  error=""
+  multiple={false}
+  onBlur={[MockFunction]}
+  onChange={[MockFunction]}
+  onFocus={[MockFunction]}
+  onMouseDown={[Function]}
+  onMouseEnter={[MockFunction]}
+  onMouseLeave={[MockFunction]}
+  onMouseUp={[Function]}
+  options={Array []}
+  overrides={Object {}}
+  selectedOptions={Array []}
+  type="select"
+>
+  <MockStyledComponent>
+    <div
+      styled-component="true"
+    >
+      <div
+        onClick={[Function]}
+      >
+        <Input
+          autoFocus={false}
+          caption={null}
+          disabled={true}
+          endEnhancer={null}
+          error={false}
+          id="bui-mock-id"
+          label={null}
+          onBlur={[Function]}
+          onFocus={[Function]}
+          overrides={
+            Object {
+              "After": [Function],
+              "Before": [Function],
+              "Input": [Function],
+              "Root": [Function],
+            }
+          }
+          required={false}
+          size="default"
+          startEnhancer={null}
+        >
+          <MockStyledComponent
+            $disabled={true}
+            $error={false}
+            $isFocused={false}
+            $required={false}
+            $size="default"
+          >
+            <div
+              styled-component="true"
+            >
+              <BaseInput
+                adjoined="none"
+                autoFocus={false}
+                disabled={true}
+                error={false}
+                id="bui-mock-id"
+                inputRef={
+                  Object {
+                    "current": null,
+                  }
+                }
+                onBlur={[Function]}
+                onChange={[Function]}
+                onFocus={[Function]}
+                overrides={
+                  Object {
+                    "After": [Function],
+                    "Before": [Function],
+                    "Input": [Function],
+                    "Root": [Function],
+                  }
+                }
+                placeholder=""
+                required={false}
+                size="default"
+                type="text"
+                value=""
+              >
+                <MockStyledComponent
+                  $adjoined="none"
+                  $disabled={true}
+                  $error={false}
+                  $isFocused={false}
+                  $required={false}
+                  $size="default"
+                >
+                  <div
+                    styled-component="true"
+                  >
+                    <Before
+                      $adjoined="none"
+                      $disabled={true}
+                      $error={false}
+                      $isFocused={false}
+                      $required={false}
+                      $size="default"
+                    />
+                    <MockStyledComponent
+                      $adjoined="none"
+                      $disabled={true}
+                      $error={false}
+                      $isFocused={false}
+                      $ref={
+                        Object {
+                          "current": null,
+                        }
+                      }
+                      $required={false}
+                      $size="default"
+                      aria-invalid={false}
+                      aria-required={false}
+                      disabled={true}
+                      id="bui-mock-id"
+                      onBlur={[Function]}
+                      onChange={[Function]}
+                      onFocus={[Function]}
+                      placeholder=""
+                      type="text"
+                      value=""
+                    >
+                      <input
+                        aria-invalid={false}
+                        aria-required={false}
+                        disabled={true}
+                        id="bui-mock-id"
+                        onBlur={[Function]}
+                        onChange={[Function]}
+                        onFocus={[Function]}
+                        placeholder=""
+                        styled-component="true"
+                        type="text"
+                        value=""
+                      />
+                    </MockStyledComponent>
+                    <After
+                      $adjoined="none"
+                      $disabled={true}
+                      $error={false}
+                      $isFocused={false}
+                      $required={false}
+                      $size="default"
+                    >
+                      <MockStyledComponent
+                        $type="select"
+                        src="data:image/svg+xml;utf8,<svg width=\\"12\\" height=\\"6\\" viewBox=\\"0 0 12 6\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path d=\\"M5.29289 5.29289L0.853553 0.853553C0.538571 0.538571 0.761654 0 1.20711 0H10.7929C11.2383 0 11.4614 0.538571 11.1464 0.853554L6.70711 5.29289C6.31658 5.68342 5.68342 5.68342 5.29289 5.29289Z\\" transform=\\"translate(12) scale(-1 1)\\" fill=\\"#666666\\"/></svg>"
+                      >
+                        <img
+                          src="data:image/svg+xml;utf8,<svg width=\\"12\\" height=\\"6\\" viewBox=\\"0 0 12 6\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path d=\\"M5.29289 5.29289L0.853553 0.853553C0.538571 0.538571 0.761654 0 1.20711 0H10.7929C11.2383 0 11.4614 0.538571 11.1464 0.853554L6.70711 5.29289C6.31658 5.68342 5.68342 5.68342 5.29289 5.29289Z\\" transform=\\"translate(12) scale(-1 1)\\" fill=\\"#666666\\"/></svg>"
+                          styled-component="true"
+                        />
+                      </MockStyledComponent>
+                    </After>
+                  </div>
+                </MockStyledComponent>
+              </BaseInput>
+            </div>
+          </MockStyledComponent>
+        </Input>
+      </div>
+      <SelectDropDown
+        getOptionLabel={[Function]}
+        isDropDownOpen={false}
+        onChange={[Function]}
+        options={Array []}
+        overrides={Object {}}
+        selectedOptions={Array []}
+        type="select"
+      >
+        <div />
+      </SelectDropDown>
+    </div>
+  </MockStyledComponent>
+</Select>
+`;
+
+exports[`Stateless select should render component in select mode and true for multiple choice: Component has correct render in select mode and mutiple choice equal true 1`] = `
+<Select
+  autoFocus={false}
+  error=""
+  multiple={true}
+  onBlur={[MockFunction]}
+  onChange={[MockFunction]}
+  onFocus={[MockFunction]}
+  onMouseDown={[Function]}
+  onMouseEnter={[MockFunction]}
+  onMouseLeave={[MockFunction]}
+  onMouseUp={[Function]}
+  options={Array []}
+  overrides={Object {}}
+  selectedOptions={Array []}
+  type="select"
+>
+  <MockStyledComponent>
+    <div
+      styled-component="true"
+    >
+      <div
+        onClick={[Function]}
+      >
+        <Input
+          autoFocus={false}
+          caption={null}
+          disabled={true}
+          endEnhancer={null}
+          error={false}
+          id="bui-mock-id"
+          label={null}
+          onBlur={[Function]}
+          onFocus={[Function]}
+          overrides={
+            Object {
+              "After": [Function],
+              "Before": [Function],
+              "Input": [Function],
+              "Root": [Function],
+            }
+          }
+          required={false}
+          size="default"
+          startEnhancer={null}
+        >
+          <MockStyledComponent
+            $disabled={true}
+            $error={false}
+            $isFocused={false}
+            $required={false}
+            $size="default"
+          >
+            <div
+              styled-component="true"
+            >
+              <BaseInput
+                adjoined="none"
+                autoFocus={false}
+                disabled={true}
+                error={false}
+                id="bui-mock-id"
+                inputRef={
+                  Object {
+                    "current": null,
+                  }
+                }
+                onBlur={[Function]}
+                onChange={[Function]}
+                onFocus={[Function]}
+                overrides={
+                  Object {
+                    "After": [Function],
+                    "Before": [Function],
+                    "Input": [Function],
+                    "Root": [Function],
+                  }
+                }
+                placeholder=""
+                required={false}
+                size="default"
+                type="text"
+                value=""
+              >
+                <MockStyledComponent
+                  $adjoined="none"
+                  $disabled={true}
+                  $error={false}
+                  $isFocused={false}
+                  $required={false}
+                  $size="default"
+                >
+                  <div
+                    styled-component="true"
+                  >
+                    <Before
+                      $adjoined="none"
+                      $disabled={true}
+                      $error={false}
+                      $isFocused={false}
+                      $required={false}
+                      $size="default"
+                    />
+                    <MockStyledComponent
+                      $adjoined="none"
+                      $disabled={true}
+                      $error={false}
+                      $isFocused={false}
+                      $ref={
+                        Object {
+                          "current": null,
+                        }
+                      }
+                      $required={false}
+                      $size="default"
+                      aria-invalid={false}
+                      aria-required={false}
+                      disabled={true}
+                      id="bui-mock-id"
+                      onBlur={[Function]}
+                      onChange={[Function]}
+                      onFocus={[Function]}
+                      placeholder=""
+                      type="text"
+                      value=""
+                    >
+                      <input
+                        aria-invalid={false}
+                        aria-required={false}
+                        disabled={true}
+                        id="bui-mock-id"
+                        onBlur={[Function]}
+                        onChange={[Function]}
+                        onFocus={[Function]}
+                        placeholder=""
+                        styled-component="true"
+                        type="text"
+                        value=""
+                      />
+                    </MockStyledComponent>
+                    <After
+                      $adjoined="none"
+                      $disabled={true}
+                      $error={false}
+                      $isFocused={false}
+                      $required={false}
+                      $size="default"
+                    >
+                      <MockStyledComponent
+                        $type="select"
+                        src="data:image/svg+xml;utf8,<svg width=\\"12\\" height=\\"6\\" viewBox=\\"0 0 12 6\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path d=\\"M5.29289 5.29289L0.853553 0.853553C0.538571 0.538571 0.761654 0 1.20711 0H10.7929C11.2383 0 11.4614 0.538571 11.1464 0.853554L6.70711 5.29289C6.31658 5.68342 5.68342 5.68342 5.29289 5.29289Z\\" transform=\\"translate(12) scale(-1 1)\\" fill=\\"#666666\\"/></svg>"
+                      >
+                        <img
+                          src="data:image/svg+xml;utf8,<svg width=\\"12\\" height=\\"6\\" viewBox=\\"0 0 12 6\\" fill=\\"none\\" xmlns=\\"http://www.w3.org/2000/svg\\"><path d=\\"M5.29289 5.29289L0.853553 0.853553C0.538571 0.538571 0.761654 0 1.20711 0H10.7929C11.2383 0 11.4614 0.538571 11.1464 0.853554L6.70711 5.29289C6.31658 5.68342 5.68342 5.68342 5.29289 5.29289Z\\" transform=\\"translate(12) scale(-1 1)\\" fill=\\"#666666\\"/></svg>"
+                          styled-component="true"
+                        />
+                      </MockStyledComponent>
+                    </After>
+                  </div>
+                </MockStyledComponent>
+              </BaseInput>
+            </div>
+          </MockStyledComponent>
+        </Input>
+      </div>
+      <SelectDropDown
+        getOptionLabel={[Function]}
+        isDropDownOpen={false}
+        onChange={[Function]}
+        options={Array []}
+        overrides={Object {}}
+        selectedOptions={Array []}
+        type="select"
+      >
+        <div />
+      </SelectDropDown>
+    </div>
+  </MockStyledComponent>
+</Select>
+`;
diff --git a/src/select/__tests__/__snapshots__/stateful-select-container.test.js.snap b/src/select/__tests__/__snapshots__/stateful-select-container.test.js.snap
new file mode 100644
index 000000000..87957c714
--- /dev/null
+++ b/src/select/__tests__/__snapshots__/stateful-select-container.test.js.snap
@@ -0,0 +1,18 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Stateful Select Container should correctly render: Component has correct render  1`] = `
+<StatefulSelectContainer
+  initialState={Object {}}
+  onBlur={[Function]}
+  onChange={[Function]}
+  onFocus={[Function]}
+  onMouseEnter={[Function]}
+  onMouseLeave={[Function]}
+  prop1="some other propq"
+  stateReducer={[Function]}
+>
+  <div>
+    test
+  </div>
+</StatefulSelectContainer>
+`;
diff --git a/src/select/__tests__/__snapshots__/styled-components.test.js.snap b/src/select/__tests__/__snapshots__/styled-components.test.js.snap
new file mode 100644
index 000000000..a3473014a
--- /dev/null
+++ b/src/select/__tests__/__snapshots__/styled-components.test.js.snap
@@ -0,0 +1,286 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Select styled components StyledDropDown : StyledDropDown has correct styles when  is  1`] = `
+Object {
+  "background": "$theme.colors.white",
+  "borderRadius": "$theme.sizing.scale300",
+  "boxShadow": "$theme.lighting.shadow600",
+  "display": "none",
+  "flexDirection": "column",
+  "height": null,
+  "listStyle": "none",
+  "margin": 0,
+  "overflowY": null,
+  "padding": "$theme.sizing.scale600",
+  "paddingBottom": "$theme.sizing.scale200",
+  "paddingLeft": "$theme.sizing.scale400",
+  "paddingRight": "$theme.sizing.scale400",
+  "paddingTop": "$theme.sizing.scale200",
+  "position": "absolute",
+  "top": null,
+  "width": "96%",
+}
+`;
+
+exports[`Select styled components StyledDropDown : StyledDropDown has correct styles when $isOpen is true 1`] = `
+Object {
+  "background": "$theme.colors.white",
+  "borderRadius": "$theme.sizing.scale300",
+  "boxShadow": "$theme.lighting.shadow600",
+  "display": null,
+  "flexDirection": "column",
+  "height": null,
+  "listStyle": "none",
+  "margin": 0,
+  "overflowY": null,
+  "padding": "$theme.sizing.scale600",
+  "paddingBottom": "$theme.sizing.scale200",
+  "paddingLeft": "$theme.sizing.scale400",
+  "paddingRight": "$theme.sizing.scale400",
+  "paddingTop": "$theme.sizing.scale200",
+  "position": "absolute",
+  "top": null,
+  "width": "96%",
+}
+`;
+
+exports[`Select styled components StyledDropDown : StyledDropDown has correct styles when $rows is 3 1`] = `
+Object {
+  "background": "$theme.colors.white",
+  "borderRadius": "$theme.sizing.scale300",
+  "boxShadow": "$theme.lighting.shadow600",
+  "display": "none",
+  "flexDirection": "column",
+  "height": "NaNpx",
+  "listStyle": "none",
+  "margin": 0,
+  "overflowY": "scroll",
+  "padding": "$theme.sizing.scale600",
+  "paddingBottom": "$theme.sizing.scale200",
+  "paddingLeft": "$theme.sizing.scale400",
+  "paddingRight": "$theme.sizing.scale400",
+  "paddingTop": "$theme.sizing.scale200",
+  "position": "absolute",
+  "top": null,
+  "width": "96%",
+}
+`;
+
+exports[`Select styled components StyledDropDown : StyledDropDown has correct styles when $type is select 1`] = `
+Object {
+  "background": "$theme.colors.white",
+  "borderRadius": "$theme.sizing.scale300",
+  "boxShadow": "$theme.lighting.shadow600",
+  "display": "none",
+  "flexDirection": "column",
+  "height": null,
+  "listStyle": "none",
+  "margin": 0,
+  "overflowY": null,
+  "padding": "$theme.sizing.scale600",
+  "paddingBottom": "$theme.sizing.scale200",
+  "paddingLeft": "$theme.sizing.scale400",
+  "paddingRight": "$theme.sizing.scale400",
+  "paddingTop": "$theme.sizing.scale200",
+  "position": "absolute",
+  "top": "$theme.sizing.scale1000",
+  "width": "96%",
+}
+`;
+
+exports[`Select styled components StyledDropDownItem DropDownItem: StyledDropDownItem has correct styles 1`] = `
+Object {
+  ":hover": Object {
+    "color": "$theme.colors.primary400",
+  },
+  "color": "$theme.colors.black",
+  "cursor": "pointer",
+  "display": "block",
+  "fontFamily": "$theme.typography.font400.fontFamily",
+  "fontSize": "$theme.typography.font400.fontSize",
+  "fontWeight": "$theme.typography.font400.fontWeight",
+  "lineHeight": "$theme.sizing.scale600",
+  "margin": 0,
+  "position": "relative",
+  "transitionDuration": "$theme.animation.timing100",
+  "transitionProperty": "color",
+  "transitionTimingFunction": "$theme.animation.easeOutCurve",
+}
+`;
+
+exports[`Select styled components StyledInput Input: StyledInput has correct styles 1`] = `
+Object {
+  "::placeholder": Object {
+    "color": "$theme.colors.mono700",
+  },
+  ":hover": Object {
+    "cursor": "text",
+  },
+  "backgroundColor": "transparent",
+  "borderStyle": "none",
+  "borderWidth": "0",
+  "boxSizing": "border-box",
+  "caretColor": "$theme.colors.primary",
+  "color": "$theme.colors.mono1000",
+  "cursor": "pointer",
+  "flexGrow": "1",
+  "fontFamily": "$theme.typography.font300.fontFamily",
+  "fontSize": "$theme.typography.font300.fontSize",
+  "fontWeight": "$theme.typography.font300.fontWeight",
+  "lineHeight": "$theme.typography.font300.lineHeight",
+  "maxWidth": "100%",
+  "outline": "none",
+  "paddingBottom": "$theme.sizing.scale400",
+  "paddingLeft": "$theme.sizing.scale500",
+  "paddingRight": "$theme.sizing.scale500",
+  "paddingTop": "$theme.sizing.scale400",
+  "width": "auto",
+}
+`;
+
+exports[`Select styled components StyledInputContainer InputContainer: StyledInputContainer has correct styles 1`] = `
+Object {
+  "alignItems": "center",
+  "backgroundColor": "$theme.colors.mono200",
+  "borderColor": "$theme.colors.mono700",
+  "borderRadius": undefined,
+  "borderStyle": "solid",
+  "borderWidth": "1px",
+  "boxShadow": "0 2px 6px transparent",
+  "boxSizing": "border-box",
+  "color": "$theme.colors.mono1000",
+  "display": "flex",
+  "flexWrap": "wrap",
+  "fontFamily": "$theme.typography.font300.fontFamily",
+  "fontSize": "$theme.typography.font300.fontSize",
+  "fontWeight": "$theme.typography.font300.fontWeight",
+  "height": "$theme.sizing.scale1000",
+  "lineHeight": "$theme.typography.font300.lineHeight",
+  "padding": "$theme.sizing.scale300",
+  "position": "relative",
+  "transitionDuration": "$theme.animation.timing100",
+  "transitionProperty": "border, boxShadow, backgroundColor",
+  "transitionTimingFunction": "$theme.animation.easeOutCurve",
+  "width": "100%",
+}
+`;
+
+exports[`Select styled components StyledOption : StyledOption has correct styles when  1`] = `
+Object {
+  ":hover": Object {
+    "cursor": "pointer",
+  },
+  "color": null,
+  "paddingBottom": "$theme.sizing.scale300",
+  "paddingLeft": "18px",
+  "paddingRight": "18px",
+  "paddingTop": "$theme.sizing.scale300",
+}
+`;
+
+exports[`Select styled components StyledOption : StyledOption has correct styles when $selected 1`] = `
+Object {
+  ":hover": Object {
+    "cursor": "pointer",
+  },
+  "color": "$theme.colors.primary400",
+  "paddingBottom": "$theme.sizing.scale300",
+  "paddingLeft": "0px",
+  "paddingRight": "0px",
+  "paddingTop": "$theme.sizing.scale300",
+}
+`;
+
+exports[`Select styled components StyledOption : StyledOption has correct styles when disabled 1`] = `
+Object {
+  ":hover": Object {
+    "cursor": "not-allowed",
+  },
+  "color": "$theme.colors.mono700",
+  "paddingBottom": "$theme.sizing.scale300",
+  "paddingLeft": "18px",
+  "paddingRight": "18px",
+  "paddingTop": "$theme.sizing.scale300",
+}
+`;
+
+exports[`Select styled components StyledSearchIcon : StyledSearchIcon has correct styles when type is clear_all 1`] = `
+Object {
+  "cursor": "pointer",
+  "marginLeft": "auto",
+  "position": "absolute",
+  "right": "$theme.sizing.scale600",
+}
+`;
+
+exports[`Select styled components StyledSearchIcon : StyledSearchIcon has correct styles when type is clear_tag 1`] = `Object {}`;
+
+exports[`Select styled components StyledSearchIcon : StyledSearchIcon has correct styles when type is loop 1`] = `
+Object {
+  "paddingLeft": "$theme.sizing.scale300",
+}
+`;
+
+exports[`Select styled components StyledSearchIcon : StyledSearchIcon has correct styles when type is select 1`] = `
+Object {
+  "marginRight": "$theme.sizing.scale500",
+}
+`;
+
+exports[`Select styled components StyledSearchIcon : StyledSearchIcon has correct styles when type is selected 1`] = `
+Object {
+  "paddingRight": "$theme.sizing.scale300",
+}
+`;
+
+exports[`Select styled components StyledTag : StyledTag has correct styles when  1`] = `
+Object {
+  "::placeholder": Object {
+    "color": "$theme.colors.mono600",
+  },
+  ":hover": Object {
+    "cursor": "pointer",
+  },
+  "backgroundColor": "transparent",
+  "borderStyle": "none",
+  "borderWidth": "0",
+  "boxSizing": "border-box",
+  "caretColor": "$theme.colors.primary",
+  "color": "$theme.colors.mono600",
+  "cursor": "pointer",
+  "flexGrow": "1",
+  "fontFamily": "$theme.typography.font300.fontFamily",
+  "fontSize": "$theme.typography.font300.fontSize",
+  "fontWeight": "$theme.typography.font300.fontWeight",
+  "lineHeight": "$theme.typography.font300.lineHeight",
+  "maxWidth": "100%",
+  "outline": "none",
+  "paddingBottom": "$theme.sizing.scale400",
+  "paddingLeft": "$theme.sizing.scale500",
+  "paddingRight": "$theme.sizing.scale500",
+  "paddingTop": "$theme.sizing.scale400",
+  "width": "auto",
+}
+`;
+
+exports[`Select styled components StyledTag : StyledTag has correct styles when $multiple 1`] = `
+Object {
+  "alignItems": "center",
+  "backgroundColor": "$theme.colors.mono400",
+  "borderColor": "$theme.colors.primary400",
+  "borderRadius": "7px",
+  "borderWidth": "1px",
+  "color": "$theme.colors.primary400",
+  "display": "flex",
+  "fontFamily": "$theme.typography.font200.fontFamily",
+  "fontSize": "$theme.typography.font200.fontSize",
+  "fontWeight": "bold",
+  "height": "$theme.sizing.scale800",
+  "lineHeight": "$theme.typography.font200.lineHeight",
+  "margin": "5px",
+  "paddingBottom": "$theme.sizing.scale0",
+  "paddingLeft": "5px",
+  "paddingRight": "5px",
+  "paddingTop": "$theme.sizing.scale0",
+}
+`;
diff --git a/src/select/__tests__/dropdown.test.js b/src/select/__tests__/dropdown.test.js
new file mode 100644
index 000000000..d43418ca3
--- /dev/null
+++ b/src/select/__tests__/dropdown.test.js
@@ -0,0 +1,94 @@
+/*
+MIT License
+
+Copyright (c) 2018 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+// @flow
+import React from 'react';
+import {shallow} from 'enzyme';
+import {StyledDropDown} from '../index';
+
+describe('Stateless Select Dropdown', function() {
+  let wrapper,
+    events = {};
+  let allProps: any = {},
+    mockFn;
+  let options = [
+      {
+        id: '1',
+        label: 'label for 1',
+      },
+      {
+        id: '2',
+        label: 'label for 2',
+      },
+      {
+        id: '3',
+        label: 'label for 3',
+      },
+      {
+        id: '4',
+        label: 'label for 4',
+      },
+    ],
+    getOptionLabel = jest.fn(() => <div>test label</div>),
+    isDropDownOpen = true,
+    selectedOptions,
+    onChange,
+    type,
+    rows;
+
+  beforeEach(function() {
+    mockFn = jest.fn();
+    events = {
+      onChange: mockFn,
+    };
+    allProps = {
+      ...events,
+      options,
+      getOptionLabel,
+      isDropDownOpen,
+      selectedOptions,
+      onChange,
+      type,
+      rows,
+    };
+  });
+
+  afterEach(function() {
+    jest.restoreAllMocks();
+    wrapper && wrapper.unmount();
+  });
+
+  test.each([[options, true], [[], true], [options, false]])(
+    'should render component with options %s and drondown open is %s',
+    (options, isDropDownOpen) => {
+      allProps = Object.assign({}, allProps, {options, isDropDownOpen});
+      wrapper = shallow(<StyledDropDown {...allProps} />);
+      expect(wrapper).toMatchSnapshot(
+        'Component has correct render with options length ' +
+          options.length +
+          ' and dropdown open is %s ' +
+          isDropDownOpen,
+      );
+    },
+  );
+});
diff --git a/src/select/__tests__/select.test.js b/src/select/__tests__/select.test.js
new file mode 100644
index 000000000..e654177dc
--- /dev/null
+++ b/src/select/__tests__/select.test.js
@@ -0,0 +1,285 @@
+/*
+MIT License
+
+Copyright (c) 2018 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+// @flow
+import React from 'react';
+import {mount, shallow} from 'enzyme';
+import {Select, StyledSearchIcon, StyledInput} from '../index';
+import {STATE_CHANGE_TYPE, TYPE, ICON} from '../constants';
+
+describe('Stateless select', function() {
+  let wrapper,
+    events = {};
+  let allProps: any = {},
+    mockFn;
+
+  beforeEach(function() {
+    mockFn = jest.fn();
+    events = {
+      onChange: mockFn,
+      onMouseEnter: mockFn,
+      onMouseLeave: mockFn,
+      onFocus: mockFn,
+      onBlur: mockFn,
+    };
+    allProps = {
+      ...events,
+    };
+  });
+
+  afterEach(function() {
+    jest.restoreAllMocks();
+    wrapper && wrapper.unmount();
+  });
+
+  test.each([
+    [TYPE.search, false],
+    [TYPE.select, false],
+    [TYPE.search, true],
+    [TYPE.select, true],
+  ])(
+    'should render component in %s mode and %s for multiple choice',
+    (type, multiple) => {
+      allProps.type = type;
+      allProps.multiple = multiple;
+      wrapper = mount(<Select {...allProps} />);
+      expect(wrapper).toMatchSnapshot(
+        'Component has correct render in ' +
+          type +
+          ' mode and mutiple choice equal ' +
+          multiple,
+      );
+    },
+  );
+
+  test('should apply initial select options if they are missing in options array', function() {
+    const initialSelectedOptions = [
+      {
+        id: '123',
+        label: 'label for 123',
+      },
+    ];
+    allProps.selectedOptions = initialSelectedOptions;
+    allProps.type = TYPE.select;
+    wrapper = shallow(<Select {...allProps} />);
+    const instance = wrapper.instance();
+    expect(instance.state.selectedOptions).toEqual(initialSelectedOptions);
+  });
+
+  describe('events', function() {
+    let documentMock,
+      instance,
+      originalAddEventListener,
+      originalRemoveEventListener;
+    beforeEach(function() {
+      /* eslint-disable-next-line cup/no-undef  */
+      documentMock = document;
+      originalAddEventListener = documentMock.addEventListener;
+      originalRemoveEventListener = documentMock.removeEventListener;
+      //$FlowFixMe
+      documentMock.addEventListener = jest.fn();
+      //$FlowFixMe
+      documentMock.removeEventListener = jest.fn();
+      wrapper = shallow(<Select {...allProps} />);
+      instance = wrapper.instance();
+    });
+    afterEach(function() {
+      //$FlowFixMe
+      documentMock.addEventListener = originalAddEventListener;
+      //$FlowFixMe
+      documentMock.removeEventListener = originalRemoveEventListener;
+    });
+
+    test('should subscribe on document click to handle toggle of dropdown when component is rendered', function() {
+      expect(documentMock.addEventListener).toHaveBeenCalledWith(
+        'click',
+        instance.handleClickEvent,
+        {
+          capture: true,
+        },
+      );
+    });
+
+    test('should unsubscribe from document click to handle toggle of dropdown when component is removed from DOM', function() {
+      wrapper.unmount();
+      expect(documentMock.removeEventListener).toHaveBeenCalledWith(
+        'click',
+        instance.handleClickEvent,
+        {
+          capture: true,
+        },
+      );
+    });
+    test('should close dropdown if clicked outside of select component', function() {
+      wrapper.unmount();
+      wrapper = mount(<Select {...allProps} />);
+      instance = wrapper.instance();
+      instance.setState({isDropDownOpen: true});
+      instance.handleClickEvent({
+        target: null,
+      });
+      expect(instance.state.isDropDownOpen).toBeFalsy();
+    });
+
+    describe('On change action', function() {
+      let id = '2';
+      let label = 'label for 2';
+      let textValue = 'some value';
+      beforeEach(function() {
+        const options = [
+          {
+            id: '1',
+            label: 'label for 1',
+          },
+          {
+            id,
+            label,
+          },
+          {
+            id: '3',
+            label: 'label for 3',
+          },
+          {
+            id: '4',
+            label: 'label for 4',
+          },
+        ];
+        allProps.type = TYPE.search;
+        allProps.options = options;
+        allProps.onChange = jest.fn();
+        wrapper = mount(<Select {...allProps} />);
+      });
+
+      test.each([
+        [STATE_CHANGE_TYPE.select, {id, label, selectedOptions: [{id, label}]}],
+        [STATE_CHANGE_TYPE.clearAll, {selectedOptions: []}],
+        [STATE_CHANGE_TYPE.keyUp, {textValue}],
+      ])(
+        'should set update of state if change action is %s',
+        (type, expectedResult) => {
+          const e = {
+            target: {
+              value: textValue,
+            },
+          };
+          const event = [e, type, id, label];
+          wrapper.instance().onChange(...event);
+          expect(allProps.onChange).toHaveBeenCalledWith(
+            e,
+            Object.assign({}, expectedResult, {type}),
+          );
+        },
+      );
+    });
+  });
+
+  describe('Selected Tags', function() {
+    let clearIcon;
+    let selectedOptions;
+    let options;
+    beforeEach(function() {
+      selectedOptions = [
+        {
+          id: '2',
+          label: 'label for 2',
+        },
+      ];
+      options = [
+        {
+          id: '1',
+          label: 'label for 1',
+        },
+        {
+          id: '2',
+          label: 'label for 2',
+        },
+      ];
+      allProps = Object.assign({}, allProps, {
+        options,
+        selectedOptions,
+        type: TYPE.search,
+        multiple: true,
+      });
+      wrapper = mount(<Select {...allProps} />);
+    });
+
+    test('should remove selected tag from selected options if X button is clicked', function() {
+      clearIcon = wrapper.find(StyledSearchIcon);
+      clearIcon = clearIcon.filterWhere(
+        comp => comp.props().$type === ICON.clearTag,
+      );
+      expect(wrapper.instance().state.selectedOptions).toEqual(selectedOptions);
+      clearIcon.first().simulate('click');
+      expect(wrapper.instance().state.selectedOptions).toEqual([]);
+    });
+
+    test('should should clear all selected tags when Clear All clicked', function() {
+      wrapper.unmount();
+      allProps.selectedOptions = options;
+      wrapper = mount(<Select {...allProps} />);
+      clearIcon = wrapper.find(StyledSearchIcon);
+      clearIcon = clearIcon.filterWhere(
+        comp => comp.props().$type === ICON.clearAll,
+      );
+      expect(wrapper.instance().state.selectedOptions).toEqual(options);
+      clearIcon.first().simulate('click');
+      expect(wrapper.instance().state.selectedOptions).toEqual([]);
+    });
+  });
+  describe('Search mode', function() {
+    test('should call change method when key up is hit', function() {
+      allProps = Object.assign({}, allProps, {
+        type: TYPE.search,
+        multiple: true,
+      });
+      wrapper = mount(<Select {...allProps} />);
+      const value = 'some input value';
+      let input = wrapper
+        .find(StyledInput)
+        .first()
+        .find('input');
+      const event = {
+        target: {value},
+      };
+      input.props().onKeyUp(event);
+      expect(wrapper.instance().props.onChange).toHaveBeenCalledWith(event, {
+        type: STATE_CHANGE_TYPE.keyUp,
+        textValue: value,
+      });
+    });
+  });
+  describe('Select mode', function() {
+    test('should toggle dropdown if input is clicked', function() {
+      allProps = Object.assign({}, allProps, {
+        type: TYPE.select,
+      });
+      wrapper = mount(<Select {...allProps} />);
+      expect(wrapper.instance().state.isDropDownOpen).toBeFalsy();
+      wrapper
+        .find(StyledInput)
+        .first()
+        .simulate('click');
+      expect(wrapper.instance().state.isDropDownOpen).toBeTruthy();
+    });
+  });
+});
diff --git a/src/select/__tests__/stateful-select-container.test.js b/src/select/__tests__/stateful-select-container.test.js
new file mode 100644
index 000000000..7e03e8cdb
--- /dev/null
+++ b/src/select/__tests__/stateful-select-container.test.js
@@ -0,0 +1,127 @@
+/*
+MIT License
+
+Copyright (c) 2018 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+// @flow
+import React from 'react';
+import {mount} from 'enzyme';
+import {StatefulSelectContainer} from '../index';
+import {STATE_CHANGE_TYPE} from '../constants';
+import type {StateReducerT} from '../types';
+
+describe('Stateful Select Container', function() {
+  let allProps: any, childFn;
+  let wrapper;
+
+  beforeEach(function() {
+    const stateReducer: StateReducerT = (type, nextState) => nextState;
+    childFn = jest.fn(() => <div>test</div>);
+    allProps = {
+      children: childFn,
+      initialState: {},
+      stateReducer: stateReducer,
+      prop1: 'some other propq',
+    };
+  });
+
+  afterEach(function() {
+    jest.restoreAllMocks();
+    wrapper && wrapper.unmount();
+  });
+
+  test('should correctly render', function() {
+    wrapper = mount(<StatefulSelectContainer {...allProps} />);
+    expect(wrapper).toMatchSnapshot('Component has correct render ');
+  });
+
+  test('should provide all needed props to children render func', function() {
+    wrapper = mount(<StatefulSelectContainer {...allProps} />);
+    const actualProps = childFn.mock.calls[0][0];
+    expect(actualProps).toMatchObject({
+      prop1: allProps.prop1,
+    });
+  });
+
+  test('should provide initial state as part of state', function() {
+    allProps.initialState = {prop3: 'some initial state'};
+    wrapper = mount(<StatefulSelectContainer {...allProps} />);
+    const actualProps = childFn.mock.calls[0][0];
+    expect(actualProps).toMatchObject(allProps.initialState);
+  });
+
+  describe('Events', function() {
+    let selectedOptions = [
+      {
+        id: '123',
+        label: 'label for 123',
+      },
+    ];
+    let textValue = 'some good value';
+    let events, stateReducerMock, instance, event;
+    event = {target: {checked: true}};
+    const handlers = [
+      ['onChange', STATE_CHANGE_TYPE.select, {selectedOptions}],
+      ['onChange', STATE_CHANGE_TYPE.clearAll, {selectedOptions: []}],
+      ['onChange', STATE_CHANGE_TYPE.keyUp, {textValue}],
+    ];
+    beforeEach(function() {
+      events = {
+        onChange: jest.fn(),
+        onMouseEnter: jest.fn(),
+        onMouseLeave: jest.fn(),
+        onFocus: jest.fn(),
+        onBlur: jest.fn(),
+      };
+      allProps = {...allProps, ...events};
+      stateReducerMock = jest.fn();
+      allProps.stateReducer = stateReducerMock;
+      wrapper = mount(<StatefulSelectContainer {...allProps} />);
+      instance = wrapper.instance();
+    });
+
+    test.each(handlers)(
+      'should call state reducer to apply new state for %s event with %s type',
+      (eventHandler, type, newState) => {
+        const handler = instance[eventHandler];
+        const params = Object.assign({}, newState, {type});
+        handler(event, params);
+        expect(stateReducerMock).toHaveBeenCalledWith(
+          type,
+          newState,
+          {},
+          event,
+          params,
+        );
+        expect(events[eventHandler]).toHaveBeenCalledWith(event, params);
+      },
+    );
+
+    test.each([['onMouseEnter'], ['onMouseLeave'], ['onFocus'], ['onBlur']])(
+      'should call handler for %s event if it is present',
+      eventHandler => {
+        const handler = instance[eventHandler];
+        handler(event);
+        expect(events[eventHandler]).toHaveBeenCalledWith(event);
+      },
+    );
+  });
+});
diff --git a/src/select/__tests__/stateful-select.test.js b/src/select/__tests__/stateful-select.test.js
new file mode 100644
index 000000000..65ab690ee
--- /dev/null
+++ b/src/select/__tests__/stateful-select.test.js
@@ -0,0 +1,95 @@
+/*
+MIT License
+
+Copyright (c) 2018 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+// @flow
+import React from 'react';
+import {mount} from 'enzyme';
+
+describe('Stateful checkbox', function() {
+  let allProps: any, wrapper;
+
+  beforeEach(function() {
+    allProps = {};
+    jest.mock('../select', () => jest.fn(() => <div>test</div>));
+  });
+
+  afterEach(function() {
+    jest.restoreAllMocks();
+    wrapper && wrapper.unmount();
+  });
+
+  test('should provide default styled components to render', function() {
+    const {
+      StyledRoot,
+      StyledInput,
+      StyledInputContainer,
+      StyledTag,
+      StyledSearchIcon,
+      StyledDropDown,
+      StyledOption,
+      StyledDropDownItem,
+      StatefulSelect,
+    } = require('../index');
+    allProps.overrides = {
+      Root: StyledRoot,
+      Input: StyledInput,
+      InputContainer: StyledInputContainer,
+      Tag: StyledTag,
+      SearchIcon: StyledSearchIcon,
+      DropDown: StyledDropDown,
+      Option: StyledOption,
+      DropDownItem: StyledDropDownItem,
+    };
+    const checkbox: any = require('../select');
+    wrapper = mount(<StatefulSelect {...allProps} />);
+    const {overrides} = checkbox.mock.calls[0][0];
+    expect(overrides).toEqual({
+      Root: StyledRoot,
+      Input: StyledInput,
+      InputContainer: StyledInputContainer,
+      Tag: StyledTag,
+      SearchIcon: StyledSearchIcon,
+      DropDown: StyledDropDown,
+      Option: StyledOption,
+      DropDownItem: StyledDropDownItem,
+    });
+  });
+
+  test('should pass all the other props to stateless select', function() {
+    const otherProps = {
+      someProp: 'some other props',
+      autoFocus: false,
+    };
+    allProps = {...allProps, ...otherProps};
+    const {StatefulSelect} = require('../index');
+    const select: any = require('../select');
+    wrapper = mount(<StatefulSelect {...allProps} />);
+    // eslint-disable-next-line no-unused-vars
+    const {overrides, ...rest} = select.mock.calls[1][0];
+    expect(rest).toMatchObject({
+      someProp: 'some other props',
+      selectedOptions: [],
+      autoFocus: false,
+    });
+  });
+});
diff --git a/src/select/__tests__/styled-components.test.js b/src/select/__tests__/styled-components.test.js
new file mode 100644
index 000000000..bb23913d3
--- /dev/null
+++ b/src/select/__tests__/styled-components.test.js
@@ -0,0 +1,146 @@
+/*
+MIT License
+
+Copyright (c) 2018 Uber Technologies, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+*/
+// @flow
+import React from 'react';
+import {shallow} from 'enzyme';
+import {
+  StyledInput,
+  StyledInputContainer,
+  StyledTag,
+  StyledSearchIcon,
+  StyledDropDown,
+  StyledDropDownItem,
+  StyledOption,
+} from '../index';
+import {ICON, TYPE} from '../constants';
+
+describe('Select styled components', () => {
+  describe('StyledInput', function() {
+    test('Input', () => {
+      const component = shallow(
+        <StyledInput>
+          <div />
+        </StyledInput>,
+      );
+      expect(component.instance().getStyles()).toMatchSnapshot(
+        'StyledInput has correct styles',
+      );
+    });
+  });
+  describe('StyledInputContainer', function() {
+    test('InputContainer', () => {
+      const component = shallow(
+        <StyledInputContainer>
+          <div />
+        </StyledInputContainer>,
+      );
+      expect(component.instance().getStyles()).toMatchSnapshot(
+        'StyledInputContainer has correct styles',
+      );
+    });
+  });
+
+  describe('StyledTag', () => {
+    test.each([[''], ['$multiple']])('', prop => {
+      const props = {};
+      props[prop] = true;
+      const component = shallow(
+        <StyledTag {...props}>
+          <div />
+        </StyledTag>,
+      );
+      expect(component.instance().getStyles()).toMatchSnapshot(
+        'StyledTag has correct styles when ' + prop,
+      );
+    });
+  });
+
+  describe('StyledSearchIcon', () => {
+    test.each([
+      [ICON.clearAll],
+      [ICON.select],
+      [ICON.loop],
+      [ICON.clearTag],
+      [ICON.selected],
+    ])('', $type => {
+      const props = {$type};
+      const component = shallow(
+        <StyledSearchIcon {...props}>
+          <div />
+        </StyledSearchIcon>,
+      );
+      expect(component.instance().getStyles()).toMatchSnapshot(
+        'StyledSearchIcon has correct styles when type is ' + $type,
+      );
+    });
+  });
+
+  describe('StyledDropDown', () => {
+    test.each([
+      ['', ''],
+      ['$rows', 3],
+      ['$type', TYPE.select],
+      ['$isOpen', true],
+    ])('', (prop, value) => {
+      const props = {};
+      props[prop] = value;
+      const component = shallow(
+        <StyledDropDown {...props}>
+          <div />
+        </StyledDropDown>,
+      );
+      expect(component.instance().getStyles()).toMatchSnapshot(
+        'StyledDropDown has correct styles when ' + prop + ' is ' + value,
+      );
+    });
+  });
+
+  describe('StyledDropDownItem', function() {
+    test('DropDownItem', () => {
+      const component = shallow(
+        <StyledDropDownItem>
+          <div />
+        </StyledDropDownItem>,
+      );
+      expect(component.instance().getStyles()).toMatchSnapshot(
+        'StyledDropDownItem has correct styles',
+      );
+    });
+  });
+
+  describe('StyledOption', () => {
+    test.each([[''], ['disabled'], ['$selected']])('', prop => {
+      const props = {};
+      props[prop] = true;
+      const component = shallow(
+        <StyledOption {...props}>
+          <div />
+        </StyledOption>,
+      );
+      expect(component.instance().getStyles()).toMatchSnapshot(
+        'StyledOption has correct styles when ' + prop,
+      );
+    });
+  });
+});