src/platform/forms-system/src/js/containers/FormApp.jsx
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import environment from '@department-of-veterans-affairs/platform-utilities/environment';
import { Element } from 'platform/utilities/scroll';
import { isLoggedIn } from 'platform/user/selectors';
import BackLink from '../components/BackLink';
import FormNav from '../components/FormNav';
import FormTitle from '../components/FormTitle';
import { isInProgress, hideFormTitle } from '../helpers';
import { setGlobalScroll } from '../utilities/ui';
/*
* Primary component for a schema generated form app.
*/
const FormApp = props => {
const { currentLocation, formConfig, children, formData } = props;
const trimmedPathname = currentLocation.pathname.replace(/\/$/, '');
const lastPathComponent = currentLocation.pathname.split('/').pop();
const { additionalRoutes, CustomTopContent } = formConfig;
useEffect(() => {
setGlobalScroll();
if (window.History) {
window.History.scrollRestoration = 'manual';
}
}, []);
useEffect(
() => {
// Set current location in data-location for custom CSS
document.body.dataset.location = currentLocation.pathname.slice(1);
},
[currentLocation.pathname],
);
const nonFormPages = additionalRoutes
? additionalRoutes.map(route => route.path)
: [];
const isIntroductionPage = trimmedPathname.endsWith('introduction');
const isNonFormPage = nonFormPages.includes(lastPathComponent);
const Footer = formConfig.footerContent;
const title =
typeof formConfig.title === 'function'
? formConfig.title(props)
: formConfig.title;
const subTitle =
typeof formConfig.subTitle === 'function'
? formConfig.subTitle(props)
: formConfig.subTitle;
const { noTitle, noTopNav, fullWidth } = formConfig?.formOptions || {};
const notProd = !environment.isProduction();
const hasHiddenFormTitle = hideFormTitle(formConfig, trimmedPathname);
let useTopBackLink = false;
let formTitle;
let formNav;
let renderedChildren = children;
// Show title only if:
// 1. we're not on the intro page *or* one of the additionalRoutes
// specified in the form config
// 2. there is a title specified in the form config
if (!isIntroductionPage && !isNonFormPage && !hasHiddenFormTitle && title) {
formTitle = <FormTitle title={title} subTitle={subTitle} />;
}
if (!isNonFormPage) {
useTopBackLink = formConfig.useTopBackLink;
}
// Show nav only if we're not on the intro, form-saved, error, confirmation
// page or one of the additionalRoutes specified in the form config
// Also add form classes only if on an actual form page
if (!isNonFormPage && isInProgress(trimmedPathname)) {
formNav = (
<FormNav
formData={formData}
formConfig={formConfig}
currentPath={trimmedPathname}
isLoggedIn={props.isLoggedIn}
inProgressFormId={props.inProgressFormId}
/>
);
renderedChildren = (
<div className="progress-box progress-box-schemaform">{children}</div>
);
}
let footer;
if (Footer && !isNonFormPage) {
footer = (
<Footer formConfig={formConfig} currentLocation={currentLocation} />
);
}
const wrapperClass =
notProd && fullWidth
? ''
: 'usa-width-two-thirds medium-8 columns print-full-width';
return (
<div className="form-app">
<div className={notProd && fullWidth ? '' : 'row'}>
<div className={wrapperClass}>
<Element name="topScrollElement" />
{CustomTopContent && (
<CustomTopContent currentLocation={currentLocation} />
)}
{useTopBackLink && <BackLink />}
{notProd && noTitle ? null : formTitle}
{notProd && noTopNav ? null : formNav}
<Element name="topContentElement" />
{renderedChildren}
</div>
</div>
{footer}
<span
className="js-test-location hidden"
data-location={trimmedPathname}
hidden
/>
</div>
);
};
FormApp.propTypes = {
additionalRoutes: PropTypes.array,
children: PropTypes.any,
currentLocation: PropTypes.shape({
pathname: PropTypes.string,
}),
formConfig: PropTypes.shape({
additionalRoutes: PropTypes.array,
footerContent: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
formOptions: PropTypes.shape({}),
subTitle: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
title: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
useTopBackLink: PropTypes.bool,
CustomTopContent: PropTypes.oneOfType([PropTypes.element, PropTypes.func]),
}),
formData: PropTypes.shape({}),
inProgressFormId: PropTypes.number,
isLoggedIn: PropTypes.bool,
};
const mapStateToProps = state => ({
formData: state.form.data,
isLoggedIn: isLoggedIn(state),
inProgressFormId: state?.form?.inProgressFormId,
});
export default connect(mapStateToProps)(FormApp);
export { FormApp };