diff --git a/README.md b/README.md
index ece398f..f14daf6 100644
--- a/README.md
+++ b/README.md
@@ -318,6 +318,95 @@ Default themes: **primary**, **success**, **warning**, **alert**


+### ``
+Expand component
+
+#### Expand Props
+| Name | Desc | Type | Default
+| --- | --- | --- | --- |
+| `title` | Always visible. | String | `-`
+| `description` | In close state cropped to one line. | String | `-`
+| `defaultExpanded` | Default state of component. If it true component will be rendered in open state | Bool | `false`
+| `icon` | Disclosure indicator for close state \n `name` - icon name for [FontAwesome](http://fontawesome.io/icons/) | Object | `{ name: 'chevron-down', color: ThemeConstants.LIGHT_GRAY, size: 12 }`
+| `expandedIcon` | Disclosure indicator for close state \n `name` - icon name for [FontAwesome](http://fontawesome.io/icons/) | Object | `{ name: 'chevron-up', color: ThemeConstants.LIGHT_GRAY, size: 12 }`
+| `children` | Child element will be shown only in open state | Node | `-`
+
+#### Example
+```js
+import { Expand } from 'tipsi-ui-kit'
+
+
+```
+Theme structure:
+
+```js
+{
+ container: ,
+ titleWrapper: ,
+ descriptionWrapper: ,
+ childrenWrapper: ,
+ titleText: ,
+ descriptionText: ,
+}
+```
+#### Preview
+
+
+
+
+### ``
+Search component
+
+#### Search Props
+| Name | Desc | Type | Default
+| --- | --- | --- | --- |
+| `value`| Text input value | String | - |
+| `suggestions`| Array of suggestions. If not empty will be render as list | Array | [] |
+| `beforeSuggestionsRenderer`| Child element will be shown before suggestions | Node | - |
+| `afterSuggestionsRenderer`| Child element will be shown after suggestions | Node | - |
+| `onClear`| Call when tap on clear button | Function | - |
+| `onRowClick`| Call when select suggestion | Function | - |
+| `endEditing`| Call when end editing TextInput | Function | - |
+| `clearButtonMode`| When show clean button. Can be one of `'never', 'while-editing', 'unless-editing', 'always'` | String | `always` |
+| `highlightColor`| Suggestion row highlighting color | String | `ThemeConstants.LIGHT_GRAY` |
+| `clearButtonStyle`| Style for clean button, should contain size and color | Object | `{ size: 20, color: ThemeConstants.LIGHT_GRAY }`|
+| `...rest` | All other props for `TextInput` component | - | - | - |
+
+
+#### Example
+```js
+import { Search } from 'tipsi-ui-kit'
+
+ {setState({ value: text })}}
+ onClear={() => {setState({ value: '' })}}
+ clearButtonMode='while-editing'
+/>
+```
+Theme structure:
+
+```js
+{
+ container: ,
+ textIputWrapper: ,
+ textInput: ,
+ clearButtonWrapper: ,
+ separator: ,
+ scrollView: ,
+ row: ,
+ suggestionText: ,
+}
+```
+#### Preview
+
+
+
+
## Utils
### ThemeRegister
diff --git a/__tests__/e2e/09_Expand.test.js b/__tests__/e2e/09_Expand.test.js
new file mode 100644
index 0000000..1078b3b
--- /dev/null
+++ b/__tests__/e2e/09_Expand.test.js
@@ -0,0 +1,47 @@
+import test from 'tape-async'
+import helper from 'tipsi-appium-helper'
+
+const { driver, select, idFromXPath } = helper
+
+test('', async (t) => {
+ const expandGroupId = select({
+ ios: idFromXPath(`//
+ XCUIElementTypeApplication/XCUIElementTypeWindow/XCUIElementTypeOther/
+ XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther
+ /XCUIElementTypeOther/XCUIElementTypeOther[2]/XCUIElementTypeOther
+ /XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeScrollView/XCUIElementTypeOther
+ `),
+ android: idFromXPath(`//
+ android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/
+ android.widget.FrameLayout[1]/android.view.ViewGroup[1]/
+ android.view.ViewGroup[2]/android.view.ViewGroup[1]/android.widget.ScrollView[1]
+ `),
+ })
+ const expandItemId = select({
+ ios: idFromXPath(`${expandGroupId}/XCUIElementTypeOther[2]/XCUIElementTypeOther`),
+ android: idFromXPath(`
+ ${expandGroupId}/android.view.ViewGroup[1]/android.view.ViewGroup[1]/android.view.ViewGroup[2]
+ `),
+ })
+
+ try {
+ await helper.openExampleFor('', 60000)
+ await driver.waitForVisible(expandItemId, 10000)
+ t.pass(' example should be visible')
+
+ const height = await driver.getElementSize(expandItemId, 'height')
+ await driver.click(expandItemId)
+ t.pass('Can click on first expand item')
+ const expandedHeight = await driver.getElementSize(expandItemId, 'height')
+ t.notEqual(
+ height,
+ expandedHeight,
+ 'Height of the element should be changed after the click'
+ )
+ } catch (error) {
+ await helper.screenshot()
+ await helper.source()
+
+ throw error
+ }
+})
diff --git a/__tests__/e2e/12_Search.test.js b/__tests__/e2e/12_Search.test.js
new file mode 100644
index 0000000..39ae852
--- /dev/null
+++ b/__tests__/e2e/12_Search.test.js
@@ -0,0 +1,57 @@
+import test from 'tape-async'
+import helper from 'tipsi-appium-helper'
+
+const { driver, select, idFromXPath } = helper
+
+test('', async (t) => {
+ const textInputId = select({
+ ios: idFromXPath(`//
+ XCUIElementTypeApplication/XCUIElementTypeWindow/XCUIElementTypeOther
+ /XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther
+ /XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther[2]
+ /XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther
+ /XCUIElementTypeScrollView/XCUIElementTypeOther
+ /XCUIElementTypeOther/XCUIElementTypeOther[2]
+ /XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeTextField
+ `),
+ android: idFromXPath(`//
+ android.widget.LinearLayout[1]/android.widget.FrameLayout[1]
+ /android.widget.FrameLayout[1]/android.view.ViewGroup[1]
+ /android.view.ViewGroup[2]/android.view.ViewGroup[1]
+ /android.widget.ScrollView[1]/android.view.ViewGroup[1]
+ /android.view.ViewGroup[1]/android.widget.EditText[1]
+ `),
+ })
+ const suggestionsId = select({
+ ios: idFromXPath(`//
+ XCUIElementTypeApplication/XCUIElementTypeWindow/XCUIElementTypeOther
+ /XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther
+ /XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther[2]
+ /XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther
+ /XCUIElementTypeScrollView/XCUIElementTypeOther/XCUIElementTypeOther
+ /XCUIElementTypeOther[2]/XCUIElementTypeOther/XCUIElementTypeOther[3]`),
+ android: idFromXPath(`//
+ android.widget.LinearLayout[1]/android.widget.FrameLayout[1]
+ /android.widget.FrameLayout[1]/android.view.ViewGroup[1]
+ /android.view.ViewGroup[2]/android.view.ViewGroup[1]
+ /android.widget.ScrollView[1]/android.view.ViewGroup[1]
+ /android.view.ViewGroup[1]/android.view.ViewGroup[7]/android.widget.ScrollView[1]
+ `),
+ })
+
+ try {
+ await helper.openExampleFor('', 60000)
+ await driver.waitForVisible(textInputId, 30000)
+ t.pass(' example should be visible')
+ await driver.click(textInputId)
+ t.pass('Search field should be editable')
+ await driver.keys('Bl')
+ await driver.waitForVisible(suggestionsId, 30000)
+ t.pass('User should see suggestions')
+ } catch (error) {
+ await helper.screenshot()
+ await helper.source()
+
+ throw error
+ }
+})
diff --git a/__tests__/unit/__snapshots__/Storyshots.test.js.snap b/__tests__/unit/__snapshots__/Storyshots.test.js.snap
index 31f8c0b..21bd0a6 100644
--- a/__tests__/unit/__snapshots__/Storyshots.test.js.snap
+++ b/__tests__/unit/__snapshots__/Storyshots.test.js.snap
@@ -3684,6 +3684,413 @@ exports[`UIExplorer components Thickness of each dash - Prop: dashThick
style={undefined} />
`;
+exports[`UIExplorer components Children - Also you can pass your own component as children. 1`] = `
+
+
+
+
+ WHAT TYPE OF WINES DO YOU PREFER?
+
+
+
+
+
+
+
+`;
+
+exports[`UIExplorer components Default Expanded - Prop: defaultExpanded (Bool) 1`] = `
+
+
+
+
+ Winemakers Notes:
+
+
+
+
+
+
+
+ The 2012 vintage in Napa Valley was about as close to ‘normal’ as it gets! Average rainfall ended with a nearly ideal budbreak, flowering and fruit set. The frost season was mild and while the harvest was a bit slow to start, it was beautifully abundant. We carefully harvest our Muscat block twice to get the best for both wines that we made this vintage. In September, we harvested clusters inside the canopy. These early clusters retained their bright acidity and classic Muscat character in a slightly sweet, low alcohol wine perfect for interesting food pairings.
+
+
+
+
+`;
+
+exports[`UIExplorer components Other - View, Text and Image componants as children. 1`] = `
+
+
+
+
+ Region:
+
+
+
+
+
+
+
+ Napa Valley
+
+
+
+
+`;
+
+exports[`UIExplorer components Title and Description - Prop: title (String), description (String) 1`] = `
+
+
+
+
+ Winemakers Notes:
+
+
+
+
+
+
+
+ The 2012 vintage in Napa Valley was about as close to ‘normal’ as it gets! Average rainfall ended with a nearly ideal budbreak, flowering and fruit set. The frost season was mild and while the harvest was a bit slow to start, it was beautifully abundant. We carefully harvest our Muscat block twice to get the best for both wines that we made this vintage. In September, we harvested clusters inside the canopy. These early clusters retained their bright acidity and classic Muscat character in a slightly sweet, low alcohol wine perfect for interesting food pairings.
+
+
+
+
+`;
+
exports[`UIExplorer components Custom Text and Custom Background - Label with custom text and style props 1`] = `
Step - Use step prop to specify v
`;
+exports[`UIExplorer components Search - Prop: value(text), suggestions(array), beforeSuggestionsRenderer(node) 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ALL
+
+
+
+
+
+
+
+
+
+
+ $20-$30
+
+
+
+
+
+
+
+
+
+
+ $30-$100
+
+
+
+
+
+
+
+
+`;
+
exports[`UIExplorer components Count - Prop: count (Number) 1`] = `
{
+ this.setState({ expanded: !this.state.expanded })
+ }
+
+ render() {
+ const { title, description, children, styles, icon, expandedIcon } = this.props
+ const { expanded } = this.state
+ const disclosureIndicator = expanded ? :
+ return (
+
+
+
+
+ {title}
+
+ {disclosureIndicator}
+
+ {Boolean(description) &&
+
+
+ {description}
+
+
+ }
+ {expanded && children}
+
+
+ )
+ }
+}
+
+const baseStyles = StyleSheet.create({
+ container: {
+ flex: 1,
+ marginLeft: 3,
+ marginRight: 3,
+ },
+ titleWrapper: {
+ marginBottom: 5,
+ flex: 1,
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ },
+ titleText: {
+ fontSize: 14,
+ },
+ descriptionText: {
+ fontSize: 14,
+ },
+ descriptionWrapper: {
+ marginBottom: 5,
+ },
+})
+
+export default themeable(
+ 'Expand',
+ baseStyles,
+)(Expand)
diff --git a/src/Expand/index.js b/src/Expand/index.js
new file mode 100644
index 0000000..204a146
--- /dev/null
+++ b/src/Expand/index.js
@@ -0,0 +1 @@
+export { default } from './Expand.js'
diff --git a/src/Search/Search.js b/src/Search/Search.js
new file mode 100644
index 0000000..acbbc7c
--- /dev/null
+++ b/src/Search/Search.js
@@ -0,0 +1,179 @@
+import React, { Component, PropTypes } from 'react'
+import {
+ Text,
+ View,
+ TextInput,
+ StyleSheet,
+ ScrollView,
+ TouchableOpacity,
+ TouchableHighlight,
+ KeyboardAvoidingView,
+} from 'react-native'
+import Icon from 'react-native-vector-icons/FontAwesome'
+import shortid from 'shortid'
+import themeable from '../utils/themeable'
+import ThemeConstants from '../utils/ThemeConstants'
+
+class Search extends Component {
+
+ static propTypes = {
+ value: PropTypes.string,
+ suggestions: PropTypes.array,
+ beforeSuggestionsRenderer: PropTypes.node,
+ afterSuggestionsRenderer: PropTypes.node,
+ onClear: PropTypes.func,
+ onRowClick: PropTypes.func,
+ endEditing: PropTypes.func,
+ clearButtonMode: PropTypes.oneOf([
+ 'never',
+ 'while-editing',
+ 'unless-editing',
+ 'always',
+ ]),
+ highlightColor: PropTypes.string,
+ clearButtonStyle: PropTypes.object,
+ styles: PropTypes.object,
+ }
+
+ static defaultProps = {
+ value: '',
+ placeholder: 'Search...',
+ suggestions: [],
+ beforeSuggestionsRenderer: null,
+ afterSuggestionsRenderer: null,
+ onClear: () => {},
+ onRowClick: () => {},
+ endEditing: () => {},
+ highlightColor: ThemeConstants.LIGHT_GRAY,
+ clearButtonMode: 'always',
+ styles: {},
+ clearButtonStyle: { size: 20, color: ThemeConstants.LIGHT_GRAY },
+ }
+
+ state = { isEditing: false }
+
+ onFocus = () => {
+ this.setState({
+ isEditing: true,
+ })
+ }
+
+ onEndEditing = () => {
+ this.setState({
+ isEditing: false,
+ })
+ this.props.endEditing()
+ }
+
+ render() {
+ const {
+ beforeSuggestionsRenderer,
+ afterSuggestionsRenderer,
+ clearButtonMode,
+ clearButtonStyle,
+ styles,
+ highlightColor,
+ onClear,
+ onRowClick,
+ suggestions,
+ ...rest
+ } = this.props
+ let clearButton = (
+
+
+
+ )
+ switch (clearButtonMode) {
+ case 'never':
+ clearButton = false
+ break
+ case 'while-editing':
+ if (!this.state.isEditing || this.props.value.length === 0) {
+ clearButton = false
+ }
+ break
+ case 'unless-editing':
+ if (this.state.isEditing) {
+ clearButton = false
+ }
+ break
+ default:
+ break
+ }
+
+ const items = suggestions.map(renderItem =>
+ { onRowClick(renderItem) }}
+ underlayColor={highlightColor}>
+
+ {renderItem}
+
+ )
+
+ return (
+
+
+
+ { clearButton }
+
+ { beforeSuggestionsRenderer }
+ {items.length > 0 &&
+
+
+
+ {items}
+
+
+ }
+ { afterSuggestionsRenderer }
+
+ )
+ }
+}
+
+const baseStyles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ textIputWrapper: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
+ textInput: {
+ flex: 1,
+ height: 40,
+ },
+ clearButtonWrapper: {
+ marginLeft: 5,
+ marginRight: 5,
+ },
+ separator: {
+ height: 1,
+ marginTop: 5,
+ marginBottom: 5,
+ backgroundColor: ThemeConstants.LIGHT_GRAY,
+ },
+ scrollView: {
+ flex: 1,
+ },
+ row: {
+ flex: 1,
+ flexDirection: 'row',
+ padding: 10,
+ },
+ suggestionText: {
+ fontSize: 14,
+ },
+})
+
+export default themeable(
+ 'Search',
+ baseStyles,
+)(Search)
diff --git a/src/Search/index.js b/src/Search/index.js
new file mode 100644
index 0000000..78c5e48
--- /dev/null
+++ b/src/Search/index.js
@@ -0,0 +1 @@
+export { default } from './Search'
diff --git a/src/index.js b/src/index.js
index cb4c54f..ed8c40f 100644
--- a/src/index.js
+++ b/src/index.js
@@ -10,7 +10,8 @@ export { default as Label } from './Label'
export { default as LabelRating } from './LabelRating'
export { default as RangeSlider } from './RangeSlider'
export { default as Counter } from './Counter'
-
+export { default as Expand } from './Expand'
+export { default as Search } from './Search'
// Utils
export { default as ThemeRegister } from './utils/ThemeRegister'
export { default as ThemeConstants } from './utils/ThemeConstants'
diff --git a/uiexplorer/examples/Expand.js b/uiexplorer/examples/Expand.js
new file mode 100644
index 0000000..ddec0cf
--- /dev/null
+++ b/uiexplorer/examples/Expand.js
@@ -0,0 +1,79 @@
+import React from 'react'
+import { Text, Image, View } from 'react-native'
+import register from '../core/utils/register'
+import { Expand, Tags } from '../../src'
+
+register.addExample({
+ type: 'components',
+ title: '',
+ description: 'Expand component',
+ examples: [{
+ title: 'Title and Description',
+ description: 'Prop: title (String), description (String)',
+ render: () => (
+
+ ),
+ }, {
+ title: 'Default Expanded',
+ description: 'Prop: defaultExpanded (Bool)',
+ render: () => (
+
+ ),
+ }, {
+ title: 'Children',
+ description: 'Also you can pass your own component as children.',
+ render: () => (
+
+
+
+
+
+
+
+
+
+
+ ),
+ }, {
+ title: 'Other',
+ description: 'View, Text and Image componants as children.',
+ render: () => (
+
+
+
+ California’s Napa Valley is the best-known,
+ most prestigious wine region in America.
+ And yet only about four percent of
+ California’s wine comes from the vineyard lands of Napa Valley!
+ Most of Napa Valley’s wineries are small operations,
+ although a few large wineries, such as Robert Mondavi Winery,
+ Beringer, and Sutter Home, are based in Napa County.
+ Many Napa Valley wineries own large vineyards,
+ which surround their properties like gorgeous manicured lawns.
+ Other wineries don’t own vineyards but instead buy their grapes
+ from independent grape growers or buy juice or bulk wine from other
+ wine producers. And some Napa Valley wine producers don’t have
+ their own wineries; they bring their grapes to custom-crush wine
+ facilities, which they rent — all for the distinction of making “Napa Valley wine.”
+
+
+
+
+ ),
+ }],
+})
diff --git a/uiexplorer/examples/Search.js b/uiexplorer/examples/Search.js
new file mode 100644
index 0000000..d08cbb7
--- /dev/null
+++ b/uiexplorer/examples/Search.js
@@ -0,0 +1,79 @@
+import React from 'react'
+import { View } from 'react-native'
+import register from '../core/utils/register'
+import { Search, Breadcrumbs } from '../../src'
+/* eslint react/prop-types: 0 */
+
+register.addExample({
+ type: 'components',
+ title: '',
+ description: 'Search component',
+ examples: [{
+ title: 'Search',
+ description: 'Prop: value(text), suggestions(array), beforeSuggestionsRenderer(node)',
+ state: {
+ value: '',
+ suggestions: [
+ 'Il Roccolo Montepulciano d\'Abruzzo',
+ 'Chateau Branaire-Ducru Saint-Julien Bordeaux Blend',
+ 'Cabernet Sauvignon',
+ 'Merlot',
+ 'Petit Verdot',
+ 'Château d\'Yquem Sauternes Semillon',
+ 'Sauvignon Blanc',
+ 'Château Guiraud 1er Grand Cru Classe Sauternes Semillon',
+ 'Choroy Cabernet Sauvignon',
+ 'Château La Vieille Cure Fronsac Bordeaux Blend',
+ 'Cabernet Franc',
+ 'Crow Canyon Chardonnay',
+ 'Finca La Celia La Finca Oak Aged Tempranillo',
+ 'Château Lamothe Bergeron Haut-Medoc Bordeaux Blend',
+ ],
+ filteredSuggestions: [],
+ },
+ render: ({ state, setState }) => {
+ const beforeSuggestionsRenderer = (
+
+
+
+
+
+
+ )
+ const onClear = () => {
+ setState({
+ value: '',
+ filteredSuggestions: [],
+ })
+ }
+ const onRowClick = (rowData) => {
+ setState({
+ value: rowData,
+ filteredSuggestions: [],
+ })
+ }
+ const onChangeText = (text) => {
+ let results = []
+ if (text.length > 0) {
+ results = state.suggestions.filter(
+ elem => elem.toLowerCase().indexOf(text.toLowerCase()) > -1,
+ )
+ }
+ setState({
+ value: text,
+ filteredSuggestions: results,
+ })
+ }
+ return (
+
+ )
+ },
+ }],
+})
diff --git a/uiexplorer/examples/index.js b/uiexplorer/examples/index.js
index ac8df32..d2f5508 100644
--- a/uiexplorer/examples/index.js
+++ b/uiexplorer/examples/index.js
@@ -8,6 +8,7 @@ import './LabelRating'
import './Label'
import './RangeSlider'
import './Counter'
-
+import './Expand'
+import './Search'
// Utils
import './ThemeConstants'
diff --git a/uiexplorer/img/Napa-Wine-Map-wine-folly.jpg b/uiexplorer/img/Napa-Wine-Map-wine-folly.jpg
new file mode 100644
index 0000000..e0a5a3a
Binary files /dev/null and b/uiexplorer/img/Napa-Wine-Map-wine-folly.jpg differ