Skip to content

Commit

Permalink
feat: dropdown in project search to order projects
Browse files Browse the repository at this point in the history
  • Loading branch information
vfried committed Jul 2, 2019
1 parent b7a403d commit 3e6a7d9
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 21 deletions.
59 changes: 47 additions & 12 deletions src/project/list/ProjectList.container.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,28 @@ class List extends Component {
this.handlers = {
onSearchQueryChange: this.onSearchQueryChange.bind(this),
onSearchSubmit: this.onSearchSubmit.bind(this),
onPaginationPageChange: this.onPaginationPageChange.bind(this)
onPaginationPageChange: this.onPaginationPageChange.bind(this),
onOrderByDropdownToogle: this.onOrderByDropdownToogle.bind(this),
changeSearchDropdownOrder: this.changeSearchDropdownOrder.bind(this),
toogleSearchSorting: this.toogleSearchSorting.bind(this)
};
}

componentDidMount() {
this.model.set('perPage', this.perPage);
const {query, pageNumber, pathName} = this.getUrlSearchParameters(this.props.location);
const {query, pageNumber, pathName, orderBy, orderSearchAsc} = this.getUrlSearchParameters(this.props.location);
this.model.setQuery(query);
this.model.setPathName(pathName);
this.model.setPage(pageNumber);

this.model.setOrderDropdownOpen(false);
this.model.setOrderBy(orderBy);
this.model.setOrderSearchAsc(orderSearchAsc);
// save listener to remove it when unmounting the component
// TODO: this could be removed if onPaginationPageChange/this.props.history.push worked
// also when only the search part changed
const listener = this.props.history.listen(location => {
const {query, pageNumber, pathName} = this.getUrlSearchParameters(location);
this.onUrlParametersChange(query, pageNumber, pathName);
const {query, pageNumber, pathName, orderBy, orderSearchAsc} = this.getUrlSearchParameters(location);
this.onUrlParametersChange(query, pageNumber, pathName, orderBy, orderSearchAsc);
});
this.setState({listener});
}
Expand All @@ -70,50 +75,79 @@ class List extends Component {
}
}

urlFromQueryAndPageNumber(query, pageNumber , pathName) {
return `${pathName}?q=${query}&page=${pageNumber}`
orderByValuesMap(){
return {
NAME: 'name',
CREATIONDATE: 'created_at',
UPDATEDDATE: 'last_activity_at'
}
}

urlFromQueryAndPageNumber(query, pageNumber , pathName, orderBy, orderSearchAsc) {
return `${pathName}?q=${query}&page=${pageNumber}&orderBy=${orderBy}&orderSearchAsc=${orderSearchAsc}`
}

getUrlSearchParameters(location) {
const pageNumber = parseInt(qs.parse(location.search).page, 10) || 1
const query = qs.parse(location.search).q || '';
const orderBy = qs.parse(location.search).orderBy || this.orderByValuesMap().UPDATEDDATE;
const orderSearchAsc = qs.parse(location.search).orderSearchAsc === "true" ? true : false;
const pathName = location.pathname.endsWith('/') ?
location.pathname.substring(0,location.pathname.length-1) :
location.pathname;
return {query, pageNumber,pathName};
return {query, pageNumber, pathName, orderBy, orderSearchAsc};
}

onUrlParametersChange(query, pageNumber, pathName) {
onUrlParametersChange(query, pageNumber, pathName, orderBy, orderSearchAsc) {
// workaround to prevent the listener of "this.props.history.listen" to trigger in the wrong path
// INFO: check if the path matches [/projects$, /projects/$, /projects?*, /projects/\D*]
const regExp = /\/projects($|\/$|(\/|\?)\D+.*)$/;
if (!regExp.test(pathName)) {
return;
}
this.model.setQueryPageNumberAndPath(query, pageNumber,pathName);
this.model.setQueryPageNumberAndPath(query, pageNumber,pathName, orderBy, orderSearchAsc);
}

onPaginationPageChange(newPageNumber) {
const query = this.model.get('query');
const pathName = this.model.get('pathName');
const newUrl = this.urlFromQueryAndPageNumber(query, newPageNumber, pathName);
const orderBy= this.model.get('orderBy');
const orderSearchAsc= this.model.get('orderSearchAsc');
const newUrl = this.urlFromQueryAndPageNumber(query, newPageNumber, pathName, orderBy, orderSearchAsc);
this.props.history.push(newUrl);
}

onOrderByDropdownToogle() {
this.model.set('orderByDropdownOpen', !this.model.get('orderByDropdownOpen'));
}

changeSearchDropdownOrder(e){
this.model.set('orderBy', e.target.value);
this.props.history.push(this.urlFromQueryAndPageNumber(this.model.get('query'), 1 , this.model.get('pathName'), this.model.get('orderBy'), this.model.get('orderSearchAsc') ))
}

toogleSearchSorting(){
this.model.set('orderSearchAsc', !this.model.get('orderSearchAsc'));
this.props.history.push(this.urlFromQueryAndPageNumber(this.model.get('query'), 1 , this.model.get('pathName'), this.model.get('orderBy'), this.model.get('orderSearchAsc') ))
}

onSearchQueryChange(e) {
this.model.setQuery(e.target.value);
}

onSearchSubmit(e) {
e.preventDefault();
this.props.history.push(this.urlFromQueryAndPageNumber(this.model.get('query'), 1 , this.model.get('pathName') ))
this.props.history.push(this.urlFromQueryAndPageNumber(this.model.get('query'), 1 , this.model.get('pathName'), this.model.get('orderBy'), this.model.get('orderSearchAsc') ))
}

mapStateToProps(state, ownProps) {
const currentPage = this.model.get('currentPage');
return {
user: ownProps.user,
searchQuery: this.model.get('query'),
orderBy: this.model.get('orderBy'),
orderByDropdownOpen: this.model.get('orderByDropdownOpen'),
orderSearchAsc: this.model.get('orderSearchAsc'),
loading: this.model.get('loading'),
page: this.model.get('pages')[currentPage] || {projects: []},
currentPage: this.model.get('currentPage'),
Expand All @@ -133,6 +167,7 @@ class List extends Component {
user={this.props.user}
handlers={this.handlers}
urlMap={this.urlMap()}
orderByValuesMap={this.orderByValuesMap()}
/>
}
}
Expand Down
41 changes: 35 additions & 6 deletions src/project/list/ProjectList.present.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,15 @@
import React, { Component } from 'react';
import { Link, Route, Switch } from 'react-router-dom';
import { Row, Col } from 'reactstrap';
import { Button, Form, FormGroup, FormText, Input, Label } from 'reactstrap';
import { Nav, NavItem } from 'reactstrap';
import { Button, Form, InputGroup, FormText, Input, Label } from 'reactstrap';
import { Nav, NavItem, InputGroupButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem} from 'reactstrap';

import { Avatar, Loader, Pagination, TimeCaption , RenkuNavLink } from '../../utils/UIComponents';
import { ProjectTagList } from '../shared';
import faCheck from '@fortawesome/fontawesome-free-solid/faCheck';
import faSortAmountUp from '@fortawesome/fontawesome-free-solid/faSortAmountUp';
import faSortAmountDown from '@fortawesome/fontawesome-free-solid/faSortAmountDown';
import FontAwesomeIcon from '@fortawesome/react-fontawesome'

class ProjectListRow extends Component {
render() {
Expand All @@ -49,11 +53,30 @@ class ProjectSearchForm extends Component {

render() {
return [<Form key="form" onSubmit={this.props.handlers.onSearchSubmit} inline>
<FormGroup>
<Label for="searchQuery" hidden>Query</Label>
<InputGroup>
<Input name="searchQuery" id="searchQuery" placeholder="Search Text" style={{minWidth: "300px"}}
value={this.props.searchQuery} onChange={this.props.handlers.onSearchQueryChange} />
</FormGroup>
<Label for="searchQuery" hidden>Query</Label>
<InputGroupButtonDropdown addonType="append" toggle={this.props.handlers.onOrderByDropdownToogle} isOpen={this.props.orderByDropdownOpen} >
<Button outline color="primary" onClick={this.props.handlers.toogleSearchSorting}>
{ this.props.orderSearchAsc ? <FontAwesomeIcon icon={faSortAmountUp}/> : <FontAwesomeIcon icon={faSortAmountDown}/> }
</Button>
<DropdownToggle outline caret color="primary" >
Order By
</DropdownToggle>
<DropdownMenu>
<DropdownItem value={this.props.orderByValuesMap.NAME} onClick={this.props.handlers.changeSearchDropdownOrder}>
{this.props.orderBy === this.props.orderByValuesMap.NAME ? <FontAwesomeIcon icon={faCheck} /> :null} Name
</DropdownItem>
<DropdownItem value={this.props.orderByValuesMap.CREATIONDATE} onClick={this.props.handlers.changeSearchDropdownOrder}>
{this.props.orderBy === this.props.orderByValuesMap.CREATIONDATE ? <FontAwesomeIcon icon={faCheck} /> :null} Creation Date
</DropdownItem>
<DropdownItem value={this.props.orderByValuesMap.UPDATEDDATE} onClick={this.props.handlers.changeSearchDropdownOrder}>
{this.props.orderBy === this.props.orderByValuesMap.UPDATEDDATE ? <FontAwesomeIcon icon={faCheck} /> :null} Updated Date
</DropdownItem>
</DropdownMenu>
</InputGroupButtonDropdown>
</InputGroup>
&nbsp;
<Button color="primary" onClick={this.props.handlers.onSearchSubmit}>
Search
Expand Down Expand Up @@ -160,7 +183,13 @@ class ProjectsSearch extends Component {
<span></span>
}
<Col md={8}>
<ProjectSearchForm searchQuery={this.props.searchQuery} handlers={this.props.handlers} />
<ProjectSearchForm
orderByValuesMap={this.props.orderByValuesMap}
orderBy={this.props.orderBy}
orderByDropdownOpen={this.props.orderByDropdownOpen}
orderSearchAsc={this.props.orderSearchAsc}
searchQuery={this.props.searchQuery}
handlers={this.props.handlers} />
</Col>
</Row>,
<Row key="spacer2"><Col md={8}>&nbsp;</Col></Row>,
Expand Down
21 changes: 19 additions & 2 deletions src/project/list/ProjectList.state.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const projectListSchema = new Schema({
totalItems: {mandatory: true},
currentPage: {mandatory: true},
perPage: {mandatory: true},
orderBy: {initial:'last_activity_at', mandatory:true},
pages: {initial: [], schema: [{projectsPageSchema}]}
});

Expand Down Expand Up @@ -70,9 +71,23 @@ class ProjectListModel extends StateModel {
this.set('selected',selected);
}

setQueryPageNumberAndPath(query, pageNumber, pathName) {
setOrderDropdownOpen(value){
this.set('orderByDropdownOpen', value);
}

setOrderBy(orderBy){
this.set('orderBy', orderBy);
}

setOrderSearchAsc(orderSearchAsc){
this.set('orderSearchAsc', orderSearchAsc);
}

setQueryPageNumberAndPath(query, pageNumber, pathName, orderBy, orderSearchAsc) {
this.setQuery(query)
this.setPathName(pathName)
this.setOrderBy(orderBy)
this.setOrderSearchAsc(orderSearchAsc);
return this.setPage(pageNumber)
}

Expand All @@ -81,7 +96,9 @@ class ProjectListModel extends StateModel {
const pageNumber = this.get('currentPage');
const perPage = this.get('perPage');
const query = this.get('query');
return this.client.getProjects({search: query, page: pageNumber, per_page: perPage})
const orderBy = this.get('orderBy');
const sort = this.get('orderSearchAsc') === true ? 'asc' : 'desc';
return this.client.getProjects({search: query, page: pageNumber, per_page: perPage, order_by: orderBy, sort:sort})
.then(response => {
const pagination = response.pagination;
this.set('currentPage', pagination.currentPage);
Expand Down
2 changes: 1 addition & 1 deletion src/project/list/ProjectList.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('new project actions', () => {
expect(model.get('currentPage')).toEqual(undefined);
});
it('does search with a query', () => {
return model.setQueryPageNumberAndPath("", 1,fakeHistory.pathName).then(() => {
return model.setQueryPageNumberAndPath("", 1,fakeHistory.pathName, 'last_activity_at').then(() => {
expect(model.get('currentPage')).toEqual(1);
expect(model.get('totalItems')).toEqual(1);
expect(model.get('pathName')).toEqual(fakeHistory.pathName);
Expand Down

0 comments on commit 3e6a7d9

Please sign in to comment.