Skip to content

Commit

Permalink
* wip *
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-USDS-damman committed Feb 22, 2017
1 parent f13a6b2 commit 6cb0c46
Show file tree
Hide file tree
Showing 45 changed files with 5,585 additions and 1,715 deletions.
4,606 changes: 3,218 additions & 1,388 deletions npm-shrinkwrap.json

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -177,5 +177,9 @@
"node": "4.4.7",
"npm": "3.8.9"
},
"private": true
"private": true,
"dependencies": {
"react-autosuggest": "^8.0.0",
"reselect": "^2.5.4"
}
}
49 changes: 42 additions & 7 deletions src/js/common/components/Pagination.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Pagination extends React.Component {
super(props);
this.next = this.next.bind(this);
this.prev = this.prev.bind(this);
this.last = this.last.bind(this);
this.pageNumbers = this.pageNumbers.bind(this);
}

Expand Down Expand Up @@ -34,16 +35,39 @@ class Pagination extends React.Component {
return prevPage;
}

last() {
let lastPage;
if (this.props.showLastPage && this.props.page < this.props.pages - this.props.maxPageListLength + 2) {
lastPage = (
<span>
<a aria-label="...">
...
</a>
<a aria-label="Last page" onClick={() => {this.props.onPageSelect(this.props.pages);}}>
{this.props.pages}
</a>
</span>
);
}
return lastPage;
}

pageNumbers(limit) {
const totalPages = this.props.pages;
const currentPage = this.props.page;
let end;
let start;

// If there are more pages returned than the limit to show
// cap the upper range at limit + the page number.
if (totalPages > limit) {
// If there are more pages returned than the limit to show
// cap the upper range at limit + the page number.
start = this.props.page;
end = limit + this.props.page;
start = currentPage;
end = limit + currentPage;
// treat the last pages specially
if (start >= totalPages - limit) {
start = totalPages - limit;
end = totalPages + 1;
}
} else {
start = 1;
end = totalPages + 1;
Expand All @@ -57,7 +81,12 @@ class Pagination extends React.Component {
return <div/>;
}

const pageList = this.pageNumbers(10).map((pageNumber) => {
let pageListMax = this.props.maxPageListLength;
if (this.props.showLastPage) {
pageListMax -= 2;
}

const pageList = this.pageNumbers(pageListMax).map((pageNumber) => {
const pageClass = classNames({
'va-pagination-active': this.props.page === pageNumber
});
Expand All @@ -77,7 +106,7 @@ class Pagination extends React.Component {
<div className="va-pagination">
<span className="va-pagination-prev">{this.prev()}</span>
<div className="va-pagination-inner">
{pageList}
{pageList} {this.last()}
</div>
<span className="va-pagination-next">{this.next()}</span>
</div>
Expand All @@ -88,7 +117,13 @@ class Pagination extends React.Component {
Pagination.propTypes = {
onPageSelect: React.PropTypes.func.isRequired,
page: React.PropTypes.number.isRequired,
pages: React.PropTypes.number.isRequired
pages: React.PropTypes.number.isRequired,
maxPageListLength: React.PropTypes.number.isRequired,
showLastPage: React.PropTypes.bool,
};

Pagination.defaultProps = {
maxPageListLength: 10,
};

export default Pagination;
161 changes: 160 additions & 1 deletion src/js/gi/actions/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,25 @@
// import { api } from '../config';
import { api } from '../config';

export const DISPLAY_MODAL = 'DISPLAY_MODAL';
export const SET_PAGE_TITLE = 'SET_PAGE_TITLE';
export const ENTER_PREVIEW_MODE = 'ENTER_PREVIEW_MODE';
export const EXIT_PREVIEW_MODE = 'EXIT_PREVIEW_MODE';
export const FETCH_CONSTANTS_STARTED = 'FETCH_CONSTANTS_STARTED';
export const FETCH_CONSTANTS_FAILED = 'FETCH_CONSTANTS_FAILED';
export const FETCH_CONSTANTS_SUCCEEDED = 'FETCH_CONSTANTS_SUCCEEDED';
export const AUTOCOMPLETE_STARTED = 'AUTOCOMPLETE_STARTED';
export const AUTOCOMPLETE_FAILED = 'AUTOCOMPLETE_FAILED';
export const AUTOCOMPLETE_SUCCEEDED = 'AUTOCOMPLETE_SUCCEEDED';
export const AUTOCOMPLETE_CLEARED = 'AUTOCOMPLETE_CLEARED';
export const AUTOCOMPLETE_TERM_CHANGED = 'AUTOCOMPLETE_TERM_CHANGED';
export const ELIGIBILITY_CHANGED = 'ELIGIBILITY_CHANGED';
export const SEARCH_STARTED = 'SEARCH_STARTED';
export const SEARCH_FAILED = 'SEARCH_FAILED';
export const SEARCH_SUCCEEDED = 'SEARCH_SUCCEEDED';
export const FETCH_PROFILE_STARTED = 'FETCH_PROFILE_STARTED';
export const FETCH_PROFILE_FAILED = 'FETCH_PROFILE_FAILED';
export const FETCH_PROFILE_SUCCEEDED = 'FETCH_PROFILE_SUCCEEDED';
export const INSTITUTION_FILTER_CHANGED = 'INSTITUTION_FILTER_CHANGED';

export function displayModal(modal) {
return {
Expand All @@ -15,3 +34,143 @@ export function closeModal() {
modal: null
};
}

export function setPageTitle(title) {
return {
type: SET_PAGE_TITLE,
title
};
}

export function enterPreviewMode(version) {
return {
type: ENTER_PREVIEW_MODE,
version
};
}

export function exitPreviewMode() {
return {
type: EXIT_PREVIEW_MODE
};
}

export function fetchConstants() {
const previewVersion = ''; // TODO
const url = `${api.url}/calculator/constants?preview=${previewVersion}`;

return dispatch => {
dispatch({ type: FETCH_CONSTANTS_STARTED });

return fetch(url, api.settings)
.then(res => res.json())
.then(
payload => dispatch({ type: FETCH_CONSTANTS_SUCCEEDED, payload }),
err => dispatch({ type: FETCH_CONSTANTS_FAILED, err })
);
};
}

export function updateAutocompleteSearchTerm(search_term) {
return {
type: AUTOCOMPLETE_TERM_CHANGED,
search_term,
};
}

export function fetchAutocompleteSuggestions(text) {
const previewVersion = ''; // TODO
const url = [
`${api.url}/institutions/autocomplete?preview=${previewVersion}`,
`term=${text}`
].join('&');

return dispatch => {
dispatch({ type: AUTOCOMPLETE_STARTED });

return fetch(url, api.settings)
.then(res => res.json())
.then(
payload => dispatch({ type: AUTOCOMPLETE_SUCCEEDED, payload }),
err => dispatch({ type: AUTOCOMPLETE_FAILED, err })
);
};
}

export function clearAutocompleteSuggestions() {
return { type: AUTOCOMPLETE_CLEARED };
}

export function eligibilityChange(e) {
const field = e.target.name;
const value = e.target.value;
return {
type: ELIGIBILITY_CHANGED,
field,
value
};
}

export function institutionFilterChange(e) {
const field = e.target.name;
const value = e.target.value;
return {
type: INSTITUTION_FILTER_CHANGED,
field,
value
};
}

export function institutionProgramFilterChange(e) {
const field = e.target.name;
const value = e.target.checked;
return {
type: INSTITUTION_FILTER_CHANGED,
field,
value
};
}

function withPreview(dispatch, action) {
const version = action.payload.meta.version;
if (version.preview) {
dispatch({
type: ENTER_PREVIEW_MODE,
version
});
}
dispatch(action);
}

export function fetchSearchResults(page = 1, previewVersionNumber = null) {
const url = `${api.url}/institutions?page=${page}&version=1`;
// TODO: pagination and other params
const preview = !!previewVersionNumber;

return dispatch => {
dispatch({ type: SEARCH_STARTED });

return fetch(url, api.settings)
.then(res => res.json())
.then(
payload => withPreview(dispatch, { type: SEARCH_SUCCEEDED, payload }),
err => dispatch({ type: SEARCH_FAILED, err })
);
};
}

export function fetchProfile(facility_code, previewVersionNumber = null) {
const url = `${api.url}/institutions/${facility_code}?version=1`;
const preview = !!previewVersionNumber;

return dispatch => {
dispatch({ type: FETCH_PROFILE_STARTED });

return fetch(url, api.settings)
.then(res => res.json())
.then(
payload => withPreview(dispatch, { type: FETCH_PROFILE_SUCCEEDED, payload }),
err => dispatch({ type: FETCH_PROFILE_FAILED, err })
);
};
}
102 changes: 102 additions & 0 deletions src/js/gi/components/Checkbox.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React from 'react';
import _ from 'lodash';

import ToolTip from '../../common/components/form-elements/ToolTip';


/**
* A form checkbox with a label that can display error messages.
*
* Validation has the following props.
* `checked` - Boolean. Whether or not the checkbox is checked.
* `errorMessage` - Error string to display in the component.
* When defined, indicates checkbox has a validation error.
* `label` - String for the checkbox label.
* `name` - String for name attribute.
* `toolTipText` - String with help text for user.
* `tabIndex` - Number for keyboard tab order.
* `onValueChange` - a function with this prototype: (newValue)
* `required` - boolean. Render marker indicating field is required.
*/
class Checkbox extends React.Component {
constructor() {
super();
this.handleChange = this.handleChange.bind(this);
}

componentWillMount() {
this.inputId = _.uniqueId('errorable-checkbox-');
}

handleChange(domEvent) {
this.props.onChange(domEvent);
}

render() {
// TODO: extract error logic into a utility function
// Calculate error state.
let errorSpan = '';
let errorSpanId = undefined;
if (this.props.errorMessage) {
errorSpanId = `${this.inputId}-error-message`;
errorSpan = <span className="usa-input-error-message" id={`${errorSpanId}`}>{this.props.errorMessage}</span>;
}

// Addes ToolTip if text is provided.
let toolTip;
if (this.props.toolTipText) {
toolTip = (
<ToolTip
tabIndex={this.props.tabIndex}
toolTipText={this.props.toolTipText}/>
);
}

// Calculate required.
let requiredSpan = undefined;
if (this.props.required) {
requiredSpan = <span className="form-required-span">*</span>;
}

let className = `form-checkbox${this.props.errorMessage ? ' usa-input-error' : ''}`;
if (!_.isUndefined(this.props.className)) {
className = `${className} ${this.props.className}`;
}

return (
<div className={className}>
<input
autoComplete="false"
aria-describedby={errorSpanId}
checked={this.props.checked}
id={this.inputId}
name={this.props.name}
type="checkbox"
onChange={this.handleChange}/>
<label
className={this.props.errorMessage ? 'usa-input-error-label' : undefined}
name={`${this.props.name}-label`}
htmlFor={this.inputId}>
{this.props.label}
{requiredSpan}
</label>
{errorSpan}
{toolTip}
</div>
);
}
}

Checkbox.propTypes = {
checked: React.PropTypes.bool,
errorMessage: React.PropTypes.string,
name: React.PropTypes.string,
label: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.object
]).isRequired,
onChange: React.PropTypes.func.isRequired,
required: React.PropTypes.bool,
};

export default Checkbox;
Loading

0 comments on commit 6cb0c46

Please sign in to comment.