Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Move routing here #21

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .babelrc
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
{ "presets": ["react", "es2015"] }
{
"presets": ["react", "es2015"],
"plugins": ["transform-class-properties"]
}
8 changes: 4 additions & 4 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ const browserHistory = useRouterHistory(createHistory)({
});

ReactDOM.render(
(
<Provider store={store}>
(
<Provider store={store}>
<Router history={browserHistory}>
{route}
{route}
</Router>
</Provider>
), document.getElementById('root')
), document.getElementById('root')
);
4 changes: 2 additions & 2 deletions js/components/Form.jsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React from 'react';

import FormApp from 'us-forms-system/lib/js/containers/FormApp';
import FormApp from './FormApp';
import formConfig from '../config/form';

export default function Form({ location, children }) {
return (
<FormApp formConfig={formConfig} currentLocation={location}>
{children}
{children}
</FormApp>
);
}
87 changes: 87 additions & 0 deletions js/components/FormApp.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import React from 'react';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The motivation for moving the <FormApp/> component from USFS into the starter app is that with pulling out routing we are also going to pull out navigation elements, footer, header, nav, basically anything that wraps around a section of questions and has opinions about what a form page should look like. We think that should be the responsibility of the starter app so that users can decide their own navigation elements or headers and footers if needed.

import { connect } from 'react-redux';
import Scroll from 'react-scroll';

import FormNav from 'us-forms-system/lib/js/components/FormNav';
import FormTitle from 'us-forms-system/lib/js/components/FormTitle';
import { isInProgress } from 'us-forms-system/lib/js/helpers';
// import { setGlobalScroll } from 'us-forms-system/lib/js/utilities/ui';

const Element = Scroll.Element;

/*
* Primary component for a schema generated form app.
*/
class FormApp extends React.Component {
componentWillMount() {
// setGlobalScroll();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still need to figure out where the global scroll should be set. My inclination is within the starter app, but there are many lower level components in USFS that need knowledge of a global scroll. In general I think this is a bad pattern, but it's what we have now. We should come up with a better solution for setting scroll in components that need it.


// if (window.History) {
// window.History.scrollRestoration = 'manual';
// }
}

render() {
const { currentLocation, formConfig, children, formData } = this.props;
console.log('FormApp');
console.log(formData);
const trimmedPathname = currentLocation.pathname.replace(/\/$/, '');
const isIntroductionPage = trimmedPathname.endsWith('introduction');
const Footer = formConfig.footerContent;

let formTitle;
let formNav;
let renderedChildren = children;
if (!isIntroductionPage) {
// Show title only if we're not on the intro page and if there is a title
// specified in the form config
if (formConfig.title) {
formTitle = <FormTitle title={formConfig.title} subTitle={formConfig.subTitle}/>;
}
}

// Show nav only if we're not on the intro, form-saved, error, or confirmation page
// Also add form classes only if on an actual form page
if (isInProgress(trimmedPathname)) {
formNav = <FormNav formData={formData} formConfig={formConfig} currentPath={trimmedPathname}/>;

renderedChildren = (
<div className="progress-box progress-box-schemaform">
{children}
</div>
);
}

let footer;
if (Footer) {
footer = (
<Footer
formConfig={formConfig}
currentLocation={currentLocation}/>
);
}

return (
<div>
<div className="row">
<div className="usa-width-two-thirds medium-8 columns">
<Element name="topScrollElement"/>
{formTitle}
{formNav}
{renderedChildren}
</div>
</div>
{footer}
<span className="js-test-location hidden" data-location={trimmedPathname} hidden></span>
</div>
);
}
}

const mapStateToProps = (state) => ({
formData: state.form.data
});

export default connect(mapStateToProps)(FormApp);

export { FormApp };
164 changes: 164 additions & 0 deletions js/components/PageWithNavigation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import { withRouter } from 'react-router';
import Scroll from 'react-scroll';
import _ from 'lodash/fp';
import classNames from 'classnames';

import FormPage from 'us-forms-system/lib/js/containers/FormPage';
import ProgressButton from 'us-forms-system/lib/js/components/ProgressButton';
// import SchemaForm from 'us-forms-system/lib/js/components/SchemaForm';
// import { setData, uploadFile } from '../actions';
import { getNextPagePath, getPreviousPagePath } from 'us-forms-system/lib/js/routing';
import { focusElement } from 'us-forms-system/lib/js/utilities/ui';

function focusForm() {
focusElement('.nav-header');
}

const scroller = Scroll.scroller;
const scrollToTop = () => {
scroller.scrollTo('topScrollElement', window.Forms.scroll || {
duration: 500,
delay: 0,
smooth: true,
});
};

class PageWithNavigation extends React.Component {
componentDidMount() {
if (!this.props.blockScrollOnMount) {
scrollToTop();
focusForm();
}
}

componentDidUpdate(prevProps) {
if (prevProps.route.pageConfig.pageKey !== this.props.route.pageConfig.pageKey ||
_.get('params.index', prevProps) !== _.get('params.index', this.props)) {
scrollToTop();
focusForm();
}
}

getUpdatedFormData = (formData) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the new hook for getting the updated formData after setData has been called within <FormPage/>'s function onChange in USFS. Not sure if this is the best pattern :/

return formData;
}

onSubmit = () => {
const { params, route, location } = this.props;
const formData = this.getUpdatedFormData();
// This makes sure defaulted data on a page with no changes is saved
// Probably safe to do this for regular pages, too, but it hasn’t been necessary
// if (route.pageConfig.showPagePerItem) {
// const newData = _.set([route.pageConfig.arrayPath, params.index], formData, form.data);
// this.props.setData(newData);
// }

const path = getNextPagePath(route.pageList, formData, location.pathname);

this.props.router.push(path);
}

goBack = () => {
const { route: { pageList }, location } = this.props;
const formData = this.getUpdatedFormData();

const path = getPreviousPagePath(pageList, formData, location.pathname);

this.props.router.push(path);
}

render() {
const {
route,
params,
// form,
contentAfterButtons,
formContext
} = this.props;

// let {
// schema,
// uiSchema
// } = form.pages[route.pageConfig.pageKey];

const pageClasses = classNames('form-panel', route.pageConfig.pageClass);
// let data = form.data;

// if (route.pageConfig.showPagePerItem) {
// // Instead of passing through the schema/uiSchema to SchemaForm, the
// // current item schema for the array at arrayPath is pulled out of the page state and passed
// schema = schema.properties[route.pageConfig.arrayPath].items[params.index];
// // Similarly, the items uiSchema and the data for just that particular item are passed
// uiSchema = uiSchema[route.pageConfig.arrayPath].items;
// // And the data should be for just the item in the array
// data = _.get([route.pageConfig.arrayPath, params.index], data);
// }
// It should be "safe" to check that this is the first page because it is
// always eligible and enabled, no need to call getPreviousPagePath.
const isFirstRoutePage = route.pageList[0].path === this.props.location.pathname;

return (
<div className={pageClasses}>
<FormPage
route={route}
location={this.props.location}
onSubmit={this.onSubmit}
onPageChange={this.getUpdatedFormData}>
</FormPage>
<div className="row form-progress-buttons schemaform-buttons">
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One of the problems we have in regards to navigation buttons comes from a component called <Form/>, which comes from rjsf and lives further down the component hierarchy tree as a child of <SchemaForm/>. There's logic in <Form/> that automatically renders a submit button if there aren't other children elements to render. Before we moved the navigation elements out of USFS, they were nested under <SchemaForm/> in <FormPage/>, which meant they were passed as children to <Form/>. Because they are no longer passed as children, <Form/> is rendering a separate submit button in addition to these two. Not sure of the best way to resolve this problem other than changing the logic in <Form/>.

screen shot 2018-09-20 at 3 48 56 pm

<div className="small-6 medium-5 columns">
{!isFirstRoutePage &&
<ProgressButton
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These buttons aren't actually firing anything when clicked. Need to figure out why.

onButtonClick={this.goBack}
buttonText="Back"
buttonClass="usa-button-secondary"
beforeText="«"/> }
</div>
<div className="small-6 medium-5 end columns">
<ProgressButton
submitButton
buttonText="Continue"
buttonClass="usa-button-primary"
afterText="»"/>
</div>
{contentAfterButtons}
</div>
</div>
);
}
}

// function mapStateToProps(state) {
// return {
// form: state.form,
// user: state.user
// };
// }

// const mapDispatchToProps = {
// setData,
// uploadFile
// };

PageWithNavigation.propTypes = {
// form: PropTypes.object.isRequired,
route: PropTypes.shape({
pageConfig: PropTypes.shape({
pageKey: PropTypes.string.isRequired,
schema: PropTypes.object.isRequired,
uiSchema: PropTypes.object.isRequired
}),
pageList: PropTypes.arrayOf(PropTypes.shape({
path: PropTypes.string.isRequired
}))
}),
contentAfterButtons: PropTypes.element,
// setData: PropTypes.func
};

export default withRouter(PageWithNavigation);

export { PageWithNavigation };
16 changes: 13 additions & 3 deletions js/config/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,16 @@ const formConfig = {
firstPage: {
path: 'first-chapter/first-page',
title: 'First Page',
uiSchema: {},
uiSchema: {

},
schema: {
type: 'object',
properties: {}
properties: {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stuff is added for testing purposes, I'll revert these changes once the PR is working.

name: {
type: 'string'
}
}
}
},
secondPage: {
Expand All @@ -30,7 +36,11 @@ const formConfig = {
uiSchema: {},
schema: {
type: 'object',
properties: {}
properties: {
favoriteAnimal: {
type: 'string'
}
}
}
}
}
Expand Down
62 changes: 58 additions & 4 deletions js/routes.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,71 @@
import { createRoutes } from 'us-forms-system/lib/js/helpers';
import React from 'react';
// import { createRoutes } from 'us-forms-system/lib/js/helpers';
import FormPage from 'us-forms-system/lib/js/containers/FormPage';
import ReviewPage from 'us-forms-system/lib/js/review/ReviewPage';
import { createFormPageList, createPageList } from 'us-forms-system/lib/js/helpers';

import formConfig from './config/form';
import Form from './components/Form.jsx';
import PageWithNavigation from './components/PageWithNavigation';

const routes = createRoutes(formConfig);
// const routes = createRoutes(formConfig);

/*
* Create the routes based on a form config. This goes through each chapter in a form
* config, pulls out the config for each page, then generates a list of Route components with the
* config as props
*/
function createRoutes(formConfig) {
const formPages = createFormPageList(formConfig);
const pageList = createPageList(formConfig, formPages);
let routes = formPages
.map(page => {
return {
path: page.path,
component: page.component || PageWithNavigation,
pageConfig: page,
pageList,
urlPrefix: formConfig.urlPrefix
};
});
if (formConfig.introduction) {
routes = [
{
path: 'introduction',
component: formConfig.introduction,
formConfig,
pageList
}
].concat(routes);
}

return routes.concat([
{
path: 'review-and-submit',
formConfig,
component: ReviewPage,
pageList
},
{
path: 'confirmation',
component: formConfig.confirmation
},
{
path: '*',
onEnter: (nextState, replace) => replace(formConfig.urlPrefix || '/')
}
]);
}

const formConfigRoutes = createRoutes(formConfig);

const route = {
path: '/',
component: Form,
indexRoute: {
onEnter: (nextState, replace) => replace(formConfig.urlPrefix+routes[0].path)
onEnter: (nextState, replace) => replace(formConfig.urlPrefix+formConfigRoutes[0].path)
},
childRoutes: routes,
childRoutes: formConfigRoutes,
};

export default route;
Loading