From 4fbe998cfe69b3c78f16b250a4f2b7191a9f377d Mon Sep 17 00:00:00 2001 From: Tomasz Knapik <hi@tmkn.org> Date: Tue, 15 Jan 2019 14:40:54 +0000 Subject: [PATCH] Refactor listing --- .../src/app/src/components/Listing/index.js | 67 +++++++++++-------- .../src/app/src/components/ListingGroup.js | 21 ++++++ .../src/app/src/containers/ByStatusListing.js | 31 +++++---- .../src/app/src/redux/reducers/submissions.js | 2 +- .../app/src/redux/selectors/submissions.js | 23 ------- 5 files changed, 77 insertions(+), 67 deletions(-) create mode 100644 opentech/static_src/src/app/src/components/ListingGroup.js diff --git a/opentech/static_src/src/app/src/components/Listing/index.js b/opentech/static_src/src/app/src/components/Listing/index.js index b04e54070..421625e1f 100644 --- a/opentech/static_src/src/app/src/components/Listing/index.js +++ b/opentech/static_src/src/app/src/components/Listing/index.js @@ -2,40 +2,54 @@ import React from 'react'; import PropTypes from 'prop-types'; import ListingHeading from '@components/ListingHeading'; +import ListingGroup from '@components/ListingGroup'; import ListingItem from '@components/ListingItem'; import './style.scss'; export default class Listing extends React.Component { renderListItems() { - if (this.props.isLoading) { + const { isLoading, isError, items } = this.props; + + if (isLoading) { return <ListingItem title={"Loading..."} />; - } else if (this.props.isError) { + } else if (isError) { return <ListingItem title={"Something went wrong. Please try again later."} />; - } else if (this.props.items.length === 0) { + } else if (items.length === 0) { return <ListingItem title={"No results found."} />; } - const listItems = []; - for (const item of this.props.items) { - listItems.push( - <ListingHeading key={`item-${item.id}`} title={item.title} count={item.subitems.length} /> - ); + return this.getOrderedItems().filter(v => v.items.length !== 0).map(v => + <ListingGroup key={`listing-group-${v.group}`} item={v.group}> + {v.items.map(item => + <ListingItem key={`listing-item-${item.id}`} title={item.title}/> + )} + </ListingGroup> + ); + } - const subitems = []; - for (const subitem of item.subitems) { + getGroupedItems() { + const { groupBy, items } = this.props; - subitems.push( - <ListingItem key={`subitem-${subitem.id}`} title={subitem.title} /> - ); + return items.reduce((tmpItems, v) => { + const groupByValue = v[groupBy]; + if (!(groupByValue in tmpItems)) { + tmpItems[groupByValue] = []; } - listItems.push( - <ul key={`subitems-listing-${item.id}`}> - {subitems} - </ul> - ); - } - return listItems; + tmpItems[groupByValue].push({...v}); + return tmpItems; + }, {}); + } + + getOrderedItems() { + const groupedItems = this.getGroupedItems(); + const { order = [] } = this.props; + const orderedItems = []; + const leftOverKeys = Object.keys(groupedItems).filter(v => !order.includes(v)); + return order.concat(leftOverKeys).map(key => ({ + group: key, + items: groupedItems[key] || [] + })); } render() { @@ -52,7 +66,7 @@ export default class Listing extends React.Component { </form> </div> <ul className="listing__list"> - {this.renderListItems()} + {this.renderListItems()} </ul> </div> ); @@ -60,14 +74,9 @@ export default class Listing extends React.Component { } Listing.propTypes = { - items: PropTypes.arrayOf(PropTypes.shape({ - title: PropTypes.string, - id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - subitems: PropTypes.arrayOf(PropTypes.shape({ - title: PropTypes.string, - id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), - })), - })), + items: PropTypes.array, isLoading: PropTypes.bool, isError: PropTypes.bool, + groupBy: PropTypes.string, + order: PropTypes.arrayOf(PropTypes.string), }; diff --git a/opentech/static_src/src/app/src/components/ListingGroup.js b/opentech/static_src/src/app/src/components/ListingGroup.js new file mode 100644 index 000000000..0e072445d --- /dev/null +++ b/opentech/static_src/src/app/src/components/ListingGroup.js @@ -0,0 +1,21 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import ListingHeading from '@components/ListingHeading'; + +export default class ListingGroup extends React.Component { + render() { + return ( + <> + <ListingHeading title={this.props.item} count={this.props.children.length} /> + <ul> + {this.props.children} + </ul> + </> + ); + } +} + +ListingGroup.propTypes = { + item: PropTypes.object, +}; diff --git a/opentech/static_src/src/app/src/containers/ByStatusListing.js b/opentech/static_src/src/app/src/containers/ByStatusListing.js index 58aaf392d..c5fd7a7a2 100644 --- a/opentech/static_src/src/app/src/containers/ByStatusListing.js +++ b/opentech/static_src/src/app/src/containers/ByStatusListing.js @@ -5,7 +5,7 @@ import { connect } from 'react-redux' import Listing from '@components/Listing'; import { getCurrentRound, - getCurrentRoundSubmissionsByStatus, + getCurrentRoundSubmissions, getSubmissionsByRoundErrorState, getSubmissionsByRoundLoadingState, } from '@selectors/submissions'; @@ -31,23 +31,26 @@ class ByStatusListing extends React.Component { render() { const { isLoading, isError } = this.props; - return <Listing isLoading={isLoading} isError={isError} items={this.getListingItems()} />; - } - - getListingItems() { - return this.props.items.map(v => ({ - id: v.id, - title: v.title, - subitems: v.submissions.map(v => ({ - title: v.title, - id: v.id, - })), - })); + return <Listing + isLoading={isLoading} + isError={isError} + items={this.props.items} + groupBy={'status'} + order={[ + 'in_discussion', + 'more_info', + 'internal_review', + 'post_review_discussion', + 'post_review_more_info', + 'accepted', + 'rejected', + ]} + />; } } const mapStateToProps = state => ({ - items: getCurrentRoundSubmissionsByStatus(state), + items: getCurrentRoundSubmissions(state), roundId: getCurrentRound(state), isError: getSubmissionsByRoundErrorState(state), isLoading: getSubmissionsByRoundLoadingState(state), diff --git a/opentech/static_src/src/app/src/redux/reducers/submissions.js b/opentech/static_src/src/app/src/redux/reducers/submissions.js index 12f8869ae..c866758b7 100644 --- a/opentech/static_src/src/app/src/redux/reducers/submissions.js +++ b/opentech/static_src/src/app/src/redux/reducers/submissions.js @@ -26,7 +26,7 @@ export default function submissions(state = initialState, action) { items: { ...state.items, ...action.data.results.reduce((newItems, v) => { - newItems[v.id] = v; + newItems[v.id] = { ...v }; return newItems; }, {}), }, diff --git a/opentech/static_src/src/app/src/redux/selectors/submissions.js b/opentech/static_src/src/app/src/redux/selectors/submissions.js index 892da24e1..6b29a2fb9 100644 --- a/opentech/static_src/src/app/src/redux/selectors/submissions.js +++ b/opentech/static_src/src/app/src/redux/selectors/submissions.js @@ -14,28 +14,6 @@ const getCurrentRoundSubmissions = createSelector( } ); -const getCurrentRoundSubmissionsByStatus = createSelector( - [getCurrentRoundSubmissions] , - currentRoundSubmissions => { - const submissionsByStatus = {}; - for (const submission of currentRoundSubmissions) { - if (!(submission.status in submissionsByStatus)) { - submissionsByStatus[submission.status] = []; - } - submissionsByStatus[submission.status].push({ ...submission }); - } - const formattedSubmissionsByStatus = []; - for (const [submissionStatus, statusSubmissions] of Object.entries(submissionsByStatus)) { - formattedSubmissionsByStatus.push({ - id: submissionStatus, - title: submissionStatus, - submissions: statusSubmissions, - }); - } - return formattedSubmissionsByStatus; - } -); - const getSubmissionsByRoundErrorState = state => state.submissions.itemsByRoundLoadingError; const getSubmissionsByRoundLoadingState = state => state.submissions.itemsByRoundLoading; @@ -43,7 +21,6 @@ const getSubmissionsByRoundLoadingState = state => state.submissions.itemsByRoun export { getCurrentRound, getCurrentRoundSubmissions, - getCurrentRoundSubmissionsByStatus, getSubmissionsByRoundErrorState, getSubmissionsByRoundLoadingState, }; -- GitLab