From e6e63f3b2a05bf7c2c0c18b9d28860b3645666bc Mon Sep 17 00:00:00 2001 From: Onuwa Nnachi Isaac Date: Wed, 11 Sep 2019 11:39:22 +0100 Subject: [PATCH] Add search box --- src/common/assets/images/search.svg | 3 + .../components/Search/Search.container.js | 8 +++ src/common/components/Search/Search.jsx | 67 +++++++++++++++++++ .../components/Search/Search.module.scss | 29 ++++++++ .../SearchResultItem/SearchResultItem.jsx | 51 ++++++++++++++ .../SearchResultItem.module.scss | 59 ++++++++++++++++ .../Search/SearchResultItem/index.js | 3 + .../SearchResultList/SearchResultList.jsx | 16 +++++ .../Search/SearchResultList/index.js | 3 + src/common/components/Search/index.js | 3 + .../CategorySelector/CategorySelector.jsx | 64 ++++++++++++------ .../CategorySelector.module.scss | 13 ++++ src/modules/Home/Home.jsx | 18 ++++- src/modules/Home/Home.module.scss | 22 ++++++ 14 files changed, 338 insertions(+), 21 deletions(-) create mode 100644 src/common/assets/images/search.svg create mode 100644 src/common/components/Search/Search.container.js create mode 100644 src/common/components/Search/Search.jsx create mode 100644 src/common/components/Search/Search.module.scss create mode 100644 src/common/components/Search/SearchResultItem/SearchResultItem.jsx create mode 100644 src/common/components/Search/SearchResultItem/SearchResultItem.module.scss create mode 100644 src/common/components/Search/SearchResultItem/index.js create mode 100644 src/common/components/Search/SearchResultList/SearchResultList.jsx create mode 100644 src/common/components/Search/SearchResultList/index.js create mode 100644 src/common/components/Search/index.js diff --git a/src/common/assets/images/search.svg b/src/common/assets/images/search.svg new file mode 100644 index 0000000..7e2b291 --- /dev/null +++ b/src/common/assets/images/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/common/components/Search/Search.container.js b/src/common/components/Search/Search.container.js new file mode 100644 index 0000000..0882eaa --- /dev/null +++ b/src/common/components/Search/Search.container.js @@ -0,0 +1,8 @@ +import { connect } from 'react-redux' +import Search from './Search' + +const mapStateToProps = state => ({ + dappState: state.dapps, +}) + +export default connect(mapStateToProps)(Search) diff --git a/src/common/components/Search/Search.jsx b/src/common/components/Search/Search.jsx new file mode 100644 index 0000000..7c14e43 --- /dev/null +++ b/src/common/components/Search/Search.jsx @@ -0,0 +1,67 @@ +import React from 'react' +import PropTypes from 'prop-types' +import searchIcon from '../../assets/images/search.svg' +import SearchResultItem from './SearchResultList' +import { DappState } from '../../data/dapp' +import styles from './Search.module.scss' + +class Search extends React.Component { + constructor(props) { + super(props) + this.state = { + dapps: [], + isSearching: false, + } + this.handleChange = this.handleChange.bind(this) + } + + handleChange(e) { + const { value } = e.target + const { dappState } = this.props + if (value.length > 1) { + const dapps = dappState.dapps.filter(dapp => dapp.name.startsWith(value)) + this.setState({ dapps, isSearching: true }) + } else if (value === '') { + this.setState({ + isSearching: false, + dapps: [], + }) + } + } + + render() { + const { searchStyle, onSearch, searchResultStyle } = this.props + const { dapps, isSearching } = this.state + return ( + <> +
+ Search Icon + this.handleChange(e)} + className={[styles.search, searchStyle].join(' ')} + placeholder="Search Dapps" + onSearch={onSearch(isSearching)} + /> +
+
+ +
+ + ) + } +} + +Search.propTypes = { + searchStyle: PropTypes.string, + searchResultStyle: PropTypes.string, + dappState: PropTypes.instanceOf(DappState).isRequired, + onSearch: PropTypes.func.isRequired, +} + +Search.defaultProps = { + searchStyle: null, + searchResultStyle: null, +} + +export default Search diff --git a/src/common/components/Search/Search.module.scss b/src/common/components/Search/Search.module.scss new file mode 100644 index 0000000..8de0fc9 --- /dev/null +++ b/src/common/components/Search/Search.module.scss @@ -0,0 +1,29 @@ +@import '../../styles/_variables.scss'; + +.container { + display: flex; + flex: 1; + border: 1px solid #eef2f5; + border-radius: calculateRem(4); + overflow: hidden; + margin: calculateRem(16); + img { + padding: calculateRem(5); + align-self: center; + } +} + +.search { + font-family: 'Inter'; + font-size: calculateRem(15); + flex: 1; + border: none; + outline: none; + &::placeholder { + color: #939ba1; + } +} + +.searchResults { + z-index: 99; +} diff --git a/src/common/components/Search/SearchResultItem/SearchResultItem.jsx b/src/common/components/Search/SearchResultItem/SearchResultItem.jsx new file mode 100644 index 0000000..83d9364 --- /dev/null +++ b/src/common/components/Search/SearchResultItem/SearchResultItem.jsx @@ -0,0 +1,51 @@ +import React from 'react' +import PropTypes from 'prop-types' +import ReactImageFallback from 'react-image-fallback' +import { DappModel } from '../../../utils/models' +import styles from './SearchResultItem.module.scss' +import icon from '../../../assets/images/icon.svg' + +const SearchResultItem = props => { + const { dapp } = props + + const { name, description, image, url } = dapp + + return ( +
+
+ +
+
+ <> +

{name}

+

+ {description} +

+ + {url} +  → + + +
+
+ ) +} + +SearchResultItem.defaultProps = { + visible: true, +} + +SearchResultItem.propTypes = { + dapp: PropTypes.shape(DappModel).isRequired, + visible: PropTypes.bool, +} + +export default SearchResultItem diff --git a/src/common/components/Search/SearchResultItem/SearchResultItem.module.scss b/src/common/components/Search/SearchResultItem/SearchResultItem.module.scss new file mode 100644 index 0000000..e72b799 --- /dev/null +++ b/src/common/components/Search/SearchResultItem/SearchResultItem.module.scss @@ -0,0 +1,59 @@ +@import '../../../styles/variables'; + +.container { + display: flex; + margin: calculateRem(10); + font-family: 'Inter'; +} + +.header { + color: $headline-color !important; + font-size: calculateRem(15); + line-height: calculateRem(22); + margin-bottom: calculateRem(2); + margin-top: calculateRem(12); + font-weight: 600; +} + +.description { + color: #939ba1 !important; + font-size: calculateRem(13); +} + +.column { + align-self: flex-end; + overflow-wrap: break-word; +} + +.image { + max-width: calculateRem(40); + max-height: calculateRem(40); + margin-top: calculateRem(15); + margin-right: calculateRem(16); + border-radius: 50%; +} + +p { + margin: 0; +} + +.link { + margin-top: calculateRem(2); + font-size: calculateRem(12); +} + +.imgWrapper { + flex: 0 0 auto; +} + +@media (max-width: $desktop) { + .column { + width: 78vw; + } +} + +@media (min-width: $desktop) { + .column { + width: 24vw; + } +} diff --git a/src/common/components/Search/SearchResultItem/index.js b/src/common/components/Search/SearchResultItem/index.js new file mode 100644 index 0000000..6e4d23b --- /dev/null +++ b/src/common/components/Search/SearchResultItem/index.js @@ -0,0 +1,3 @@ +import SearchResultItem from './SearchResultItem' + +export default SearchResultItem diff --git a/src/common/components/Search/SearchResultList/SearchResultList.jsx b/src/common/components/Search/SearchResultList/SearchResultList.jsx new file mode 100644 index 0000000..23f17d3 --- /dev/null +++ b/src/common/components/Search/SearchResultList/SearchResultList.jsx @@ -0,0 +1,16 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { DappListModel } from '../../../utils/models' +import SearchResultItem from '../SearchResultItem' + +const SearchResultList = ({ dapps }) => + dapps && + dapps.map((dapp, i) => ( + + )) + +SearchResultList.propTypes = { + dapps: DappListModel.isRequired, +} + +export default SearchResultList diff --git a/src/common/components/Search/SearchResultList/index.js b/src/common/components/Search/SearchResultList/index.js new file mode 100644 index 0000000..d8fd7b0 --- /dev/null +++ b/src/common/components/Search/SearchResultList/index.js @@ -0,0 +1,3 @@ +import SearchResultList from './SearchResultList' + +export default SearchResultList diff --git a/src/common/components/Search/index.js b/src/common/components/Search/index.js new file mode 100644 index 0000000..0f8d5b9 --- /dev/null +++ b/src/common/components/Search/index.js @@ -0,0 +1,3 @@ +import Search from './Search.container' + +export default Search diff --git a/src/modules/CategorySelector/CategorySelector.jsx b/src/modules/CategorySelector/CategorySelector.jsx index f3b503a..4950884 100644 --- a/src/modules/CategorySelector/CategorySelector.jsx +++ b/src/modules/CategorySelector/CategorySelector.jsx @@ -2,6 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import CategoryIcon from '../../common/components/CategoryIcon' import ViewAll from '../../common/components/ViewAll' +import Search from '../../common/components/Search' import categories from '../../common/utils/categories' import humanise from '../../common/utils/humanise' import dropdownArrows from '../../common/assets/images/dropdown-arrows.svg' @@ -10,13 +11,14 @@ import styles from './CategorySelector.module.scss' class CategorySelector extends React.Component { constructor(props) { super(props) - this.state = { open: false } + this.state = { open: false, isSearching: false } this.toggle = this.toggle.bind(this) this.updateCategory = this.updateCategory.bind(this) this.container = React.createRef() this.onClickSubmit = this.onClickSubmit.bind(this) this.onClickHighestRanked = this.onClickHighestRanked.bind(this) this.onClickRecentlyAdded = this.onClickRecentlyAdded.bind(this) + this.onSearch = this.onSearch.bind(this) } componentDidMount() { @@ -43,6 +45,10 @@ class CategorySelector extends React.Component { this.setState({ open: false }) } + onSearch(isSearching) { + this.setState({ isSearching }) + } + onClickHighestRanked(e) { const { onClickCloseDesktopMenu } = this.props onClickCloseDesktopMenu() @@ -77,6 +83,7 @@ class CategorySelector extends React.Component { showSubmitDApp, } = this.props let { open } = this.state + const { isSearching } = this.state if (alwaysOpen === true) open = true return ( @@ -85,29 +92,45 @@ class CategorySelector extends React.Component { style={open ? { visible: 'block' } : { display: 'none' }} className={styles.open} > -
+ +

Categories

- {categories.map(c => ( - - ))} +
+ {categories.map(c => ( + + ))} +
{showLists && ( - <> +

Lists

@@ -151,7 +174,7 @@ class CategorySelector extends React.Component { {'Recently added'} - +
)} {showSubmitDApp && ( @@ -159,6 +182,7 @@ class CategorySelector extends React.Component { className={`${styles.openButton} ${styles.submitDapp}`} type="button" onClick={this.onClickSubmit} + style={isSearching ? { display: 'none' } : { display: 'flex' }} > Discover
+
+ +
diff --git a/src/modules/Home/Home.module.scss b/src/modules/Home/Home.module.scss index 2de1e4c..8126985 100644 --- a/src/modules/Home/Home.module.scss +++ b/src/modules/Home/Home.module.scss @@ -9,3 +9,25 @@ font-size: calculateRem(17); margin: 0; } + +.mobileSearch { + display: none; +} + +.search { + display: flex; + width: 100%; + align-self: center; + padding: calculateRem(9); +} + +@media (max-width: $desktop) { + .mobileSearch { + display: block; + } + .searchResult { + width: 98vw; + position: absolute; + background-color: #ffffff; + } +}