From 0c76babf010925911b851be349d78c85abf05f1e Mon Sep 17 00:00:00 2001 From: Anthony Iacono Date: Thu, 4 Oct 2018 15:18:25 -0700 Subject: [PATCH 01/10] :art: Refactor getPersonList to reduce nesting --- index.html | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/index.html b/index.html index 508ebb3d..7d2a9679 100644 --- a/index.html +++ b/index.html @@ -101,19 +101,16 @@ * ... * ] */ - function getPersonList() { - return new Promise((resolve, reject) => { - fetch('https://willowtreeapps.com/api/v1.0/profiles') - .then(response => { - if (response.status !== 200) { - reject(new Error("Error!")); - } + async function getPersonList() { + let response = await fetch('https://willowtreeapps.com/api/v1.0/profiles'); - response.json().then(imageList => { - resolve(imageList); - }); - }); - }); + if(response.status !== 200) { + throw new Error("Error!"); + } + + let data = await response.json(); + + return data; } From 674c3e698b31872e7a01db5529288a0da16edb71 Mon Sep 17 00:00:00 2001 From: Anthony Iacono Date: Thu, 4 Oct 2018 15:20:03 -0700 Subject: [PATCH 02/10] :art: Trim first and last name --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 7d2a9679..dd0484ca 100644 --- a/index.html +++ b/index.html @@ -122,11 +122,11 @@ function getLastName(person) { - return person.lastName; + return person.lastName.trim(); } const getFirstName = (person) => { - return person.firstName; + return person.firstName.trim(); }; // headshot URLs are scheme relative // From bad2f4918d29640fa7636eff87bd2a0028f18eeb Mon Sep 17 00:00:00 2001 From: Anthony Iacono Date: Thu, 4 Oct 2018 15:21:38 -0700 Subject: [PATCH 03/10] :tada: Add "not pictured" text label for people with headshots --- index.html | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index dd0484ca..cd8a124c 100644 --- a/index.html +++ b/index.html @@ -131,7 +131,12 @@ // headshot URLs are scheme relative // // prepend http: to prevent invalid schemes like file:// or uri:// + // instead return null if the person does not have a headshot url defined const getImageUrl = (person) => { + if(person.headshot.url === undefined) { + return null; + } + return `http:${person.headshot.url}`; }; @@ -224,11 +229,21 @@ src: props.src }); - const ListRow = (props) => React.DOM.tr({ key: `${props.person.firstName} ${props.person.lastName}` }, [ - React.DOM.td({ key: 'thumb' }, React.createElement(Thumbnail, { src: getImageUrl(props.person) })), - React.DOM.td({ key: 'first' }, null, getFirstName(props.person)), - React.DOM.td({ key: 'last' }, null, getLastName(props.person)), - ]); + const NotPictured = (props) => React.DOM.span({ + className: 'not-pictured', + children: 'Not Pictured' + }); + + const ListRow = (props) => { + const imageUrl = getImageUrl(props.person); + const imageElement = imageUrl === null ? NotPictured : Thumbnail; + + return React.DOM.tr({ key: `${props.person.firstName} ${props.person.lastName}` }, [ + React.DOM.td({ key: 'thumb' }, React.createElement(imageElement, { src: imageUrl })), + React.DOM.td({ key: 'first' }, null, getFirstName(props.person)), + React.DOM.td({ key: 'last' }, null, getLastName(props.person)), + ]); + }; const ListContainer = (props) => React.DOM.table({ className: 'list-container' }, [ React.DOM.thead({ key: 'thead' }, React.DOM.tr({}, [ From 6bef48dbdc9f5ac32ea69b10b10f789a7568c7b1 Mon Sep 17 00:00:00 2001 From: Anthony Iacono Date: Thu, 4 Oct 2018 15:23:07 -0700 Subject: [PATCH 04/10] :bug: Prevent list from shuffling in place and chopping off first item --- index.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/index.html b/index.html index cd8a124c..9f2af6b3 100644 --- a/index.html +++ b/index.html @@ -145,15 +145,15 @@ */ function shuffleList(list) { // Make a copy & don't mutate the passed in list - let result = list.slice(1); + let result = list.slice(); - let tmp, j, i = list.length - 1 + let tmp, j, i = result.length - 1 for (; i > 0; i -= 1) { j = Math.floor(Math.random() * (i + 1)); - tmp = list[i]; - list[i] = list[j]; - list[j] = tmp; + tmp = result[i]; + result[i] = result[j]; + result[j] = tmp; } return result; From dd9b5a4ee3f66a8e5aaa5de485c0882671934273 Mon Sep 17 00:00:00 2001 From: Anthony Iacono Date: Thu, 4 Oct 2018 15:24:36 -0700 Subject: [PATCH 05/10] :tada: Add partial search matching --- index.html | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 9f2af6b3..70eb6047 100644 --- a/index.html +++ b/index.html @@ -161,12 +161,44 @@ /** - * Remove any people that do not have the name we are - * searching for. + * Get all the words from a phrase string. + */ + function getWords(phrase) { + let words = []; + let cleanedPhrase = phrase.replace(/[\s]+/g, ' ').trim(); + + if(phrase.length === 0 || cleanedPhrase === '') { + return words; + } + + words = cleanedPhrase.split(' '); + return words; + } + + /** + * Remove any people that do not have a name that + * contains one or more words from our search string. */ function filterByName(searchForName, personList) { + // Get each word from the search query. + const searchTerms = getWords(searchForName); + + // Filter our list of people... return personList.filter((person) => { - return person.firstName === searchForName || person.lastName === searchForName; + // If there are no search terms, return everyone in our list. + if(searchTerms.length === 0) { + return true; + } + + // Make sure that one or more search term is found inside + return searchTerms.some((searchTerm) => { + // Transform our search term into lowercase. + searchTerm = searchTerm.trim().toLowerCase(); + + // Check if the first name or last name contains our search term. + return getFirstName(person).toLowerCase().indexOf(searchTerm) !== -1 || + getLastName(person).toLowerCase().indexOf(searchTerm) !== -1; + }); }); } From 26d63628bc6eee3d551923f67e244e36c0666d05 Mon Sep 17 00:00:00 2001 From: Anthony Iacono Date: Thu, 4 Oct 2018 15:25:06 -0700 Subject: [PATCH 06/10] :bug: Fix sorting by last name --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 70eb6047..5b7ccf0a 100644 --- a/index.html +++ b/index.html @@ -242,7 +242,7 @@ const sortByFirstName = sortObjListByProp('firstName'); - const sortByLastName = (personList) => sortByFirstName(personList).reverse(); + const sortByLastName = sortObjListByProp('lastName'); /*================================================== From cc5c178f7006a9579aac7051a7707936dcb6c22e Mon Sep 17 00:00:00 2001 From: Anthony Iacono Date: Thu, 4 Oct 2018 15:26:26 -0700 Subject: [PATCH 07/10] :bug: Prevent removing first item from array copy in sortObjListByProp --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 5b7ccf0a..f884f5e8 100644 --- a/index.html +++ b/index.html @@ -222,7 +222,7 @@ function sortObjListByProp(prop) { return function(objList) { // Make a copy & don't mutate the passed in list - let result = objList.slice(1); + let result = objList.slice(); result.sort((a, b) => { if (a[prop] < b[prop]) { From 031f9f2d7ed60c0805a07202e234e8c97edadc79 Mon Sep 17 00:00:00 2001 From: Anthony Iacono Date: Thu, 4 Oct 2018 15:28:20 -0700 Subject: [PATCH 08/10] :art: Inline reference to state variable --- index.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/index.html b/index.html index f884f5e8..e30680bb 100644 --- a/index.html +++ b/index.html @@ -328,14 +328,12 @@ }, render() { - const { visiblePersonList } = this.state; - return React.DOM.div({ className: 'app-container' }, [ React.createElement(Search, { key: 'search', onChange: this._onSearch }), React.DOM.button({ key: 'shuffle', onClick: this._shuffleList }, null, 'Shuffle'), React.DOM.button({ key: 'sort-first', onClick: this._sortByFirst }, null, 'Sort (First Name)'), React.DOM.button({ key: 'sort-last', onClick: this._sortByLast }, null, 'Sort (Last Name)'), - React.createElement(ListContainer, { key: 'list', personList: visiblePersonList }) + React.createElement(ListContainer, { key: 'list', personList: this.state.visiblePersonList }) ]); } }); From 740a60cb2935efc8b18768c1e83f6e015c78853d Mon Sep 17 00:00:00 2001 From: Anthony Iacono Date: Thu, 4 Oct 2018 15:29:16 -0700 Subject: [PATCH 09/10] :tada: Preserve search results when sorting and allow reversing sort order --- index.html | 51 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index e30680bb..382c710b 100644 --- a/index.html +++ b/index.html @@ -202,6 +202,14 @@ }); } + /** + * Toggle the value passed in using the following rules: + * 1. If the current value is null, switch the value to true. + * 2. Otherwise, switch the value to !value. + */ + function toggleBool(currentBool) { + return currentBool === null ? true : !currentBool; + } /** * Takes in a property of an object list, e.g. "name" below @@ -211,26 +219,35 @@ * And returns a function that will sort that list, e.g. * * const sortPeopleByName = sortObjListByProp('name'); - * const sortedPeople = sortPeopleByName(people); + * const sortedPeople = sortPeopleByName(people, true); * * We now have: * * console.log(sortedPeople) * > [{ name: 'Jon' }, { name: 'Kevin' }, { name: 'Sam' }] * + * You may also sort the list backwards, e.g. + * + * const sortPeopleByName = sortObjListByProp('name'); + * const sortedPeopleBackwards = sortPeopleByName(people, false); + * + * We now have: + * + * console.log(sortedPeopleBackwards) + * > [{ name: 'Sam' }, { name: 'Kevin' }, { name: 'Jon' }] */ function sortObjListByProp(prop) { - return function(objList) { + return function(objList, forward) { // Make a copy & don't mutate the passed in list let result = objList.slice(); result.sort((a, b) => { if (a[prop] < b[prop]) { - return -1; + return forward ? -1 : 1; } if (a[prop] > b[prop]) { - return 1; + return forward ? 1 : -1; } return 1; @@ -291,7 +308,10 @@ getInitialState() { return { personList: [], - visiblePersonList: [] + visiblePersonList: [], + sortFirstForward: null, + sortLastForward: null, + searchQuery: '' }; }, @@ -304,25 +324,40 @@ }, _shuffleList() { + const searchResults = filterByName(this.state.searchQuery, this.state.personList); + this.setState({ - visiblePersonList: shuffleList(this.state.personList) + visiblePersonList: shuffleList(searchResults) }); }, _sortByFirst() { + const searchResults = filterByName(this.state.searchQuery, this.state.personList); + + const newSortFirstForward = toggleBool(this.state.sortFirstForward); + this.setState({ - visiblePersonList: sortByFirstName(this.state.personList) + sortFirstForward: newSortFirstForward, + sortLastForward: null, + visiblePersonList: sortByFirstName(searchResults, newSortFirstForward) }); }, _sortByLast() { + const searchResults = filterByName(this.state.searchQuery, this.state.personList); + + const newSortLastForward = toggleBool(this.state.sortLastForward); + this.setState({ - visiblePersonList: sortByLastName(this.state.personList) + sortFirstForward: null, + sortLastForward: newSortLastForward, + visiblePersonList: sortByLastName(searchResults, newSortLastForward) }); }, _onSearch(e) { this.setState({ + searchQuery: e.target.value, visiblePersonList: filterByName(e.target.value, this.state.personList) }); }, From 0063f123287ac6c36d24fc3a4fe5d0f7eff1b8cd Mon Sep 17 00:00:00 2001 From: Anthony Iacono Date: Thu, 4 Oct 2018 15:49:07 -0700 Subject: [PATCH 10/10] :bug: Fix sort callback --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 382c710b..16820611 100644 --- a/index.html +++ b/index.html @@ -250,7 +250,7 @@ return forward ? 1 : -1; } - return 1; + return 0; }); return result;