Skip to content

Commit

Permalink
Merge branch 'main' into js-283-frontend-updates
Browse files Browse the repository at this point in the history
Conflicts:
	.circleci/config.yml
	frontend/src/pages/ActivityReport/Pages/topicsResources.js
  • Loading branch information
jasalisbury committed Mar 4, 2021
2 parents 0cf6743 + 92a929b commit 9438d3f
Show file tree
Hide file tree
Showing 36 changed files with 652 additions and 301 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ parameters:
default: "main"
type: string
sandbox_git_branch: # change to feature branch to test deployment
default: "js-283-frontend-updates"
default: "grantee-import-bugfix"
type: string
jobs:
build_and_lint:
Expand Down
1 change: 1 addition & 0 deletions deployment_config/dev_vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ web_instances: 1
web_memory: 512M
worker_instances: 1
worker_memory: 512M
LOG_LEVEL: info
AUTH_BASE: https://uat.hsesinfo.org
# This env variable should go away soon in favor of TTA_SMART_HUB_URI
REDIRECT_URI_HOST: https://tta-smarthub-dev.app.cloud.gov
Expand Down
1 change: 1 addition & 0 deletions deployment_config/prod_vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ web_instances: 2
web_memory: 512M
worker_instances: 1
worker_memory: 512M
LOG_LEVEL: info
AUTH_BASE: TKTK
# This env variable should go away soon in favor of TTA_SMART_HUB_URI
REDIRECT_URI_HOST: https://ttahub.ohs.acf.hhs.gov
Expand Down
1 change: 1 addition & 0 deletions deployment_config/sandbox_vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ web_instances: 1
web_memory: 512M
worker_instances: 1
worker_memory: 512M
LOG_LEVEL: info
AUTH_BASE: https://uat.hsesinfo.org
# This env variable should go away soon in favor of TTA_SMART_HUB_URI
REDIRECT_URI_HOST: https://tta-smarthub-sandbox.app.cloud.gov
Expand Down
1 change: 1 addition & 0 deletions deployment_config/staging_vars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ web_instances: 1
web_memory: 512M
worker_instances: 1
worker_memory: 512M
LOG_LEVEL: info
AUTH_BASE: https://uat.hsesinfo.org
# This env variable should go away soon in favor of TTA_SMART_HUB_URI
REDIRECT_URI_HOST: https://tta-smarthub-staging.app.cloud.gov
Expand Down
4 changes: 3 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ services:
aws-cli:
image: amazon/aws-cli
env_file: .env
command: ["--endpoint-url", "$S3_ENDPOINT", "s3api", "create-bucket", "--bucket", "$S3_BUCKET"]
command: ["--endpoint-url", "http://minio:9000", "s3api", "create-bucket", "--bucket", "$S3_BUCKET"]
depends_on:
- minio
clamav-rest:
image: ajilaag/clamav-rest
ports:
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ body {
}

@media print {
.smart-hub-header {
position: relative;
}

* {
color: #000 !important;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/FileUploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const upload = async (file, reportId, attachmentType, setErrorMessage) =>
}
setErrorMessage(null);
return {
id: res.id, originalFileName: file.name, fileSize: file.size, status: 'UPLOADED',
id: res.id, originalFileName: file.name, fileSize: file.size, status: 'UPLOADED', url: res.url,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
import { REPORT_STATUSES } from '../../../../Constants';

describe('SideNav', () => {
const renderNav = (state, onNavigation = () => {}, current = false) => {
const renderNav = (state, onNavigation = () => {}, current = false, errorMessage = null) => {
const pages = [
{
label: 'test',
Expand All @@ -34,6 +34,7 @@ describe('SideNav', () => {
pages={pages}
skipTo="skip"
skipToMessage="message"
errorMessage={errorMessage}
/>,
);
};
Expand All @@ -60,6 +61,20 @@ describe('SideNav', () => {
expect(complete).toBeVisible();
});

it('approved', () => {
renderNav(REPORT_STATUSES.APPROVED);
const complete = screen.getByText('Approved');
expect(complete).toHaveClass('smart-hub--tag-submitted');
expect(complete).toBeVisible();
});

it('needs action', () => {
renderNav(REPORT_STATUSES.NEEDS_ACTION);
const complete = screen.getByText('Needs Action');
expect(complete).toHaveClass('smart-hub--tag-needs-action');
expect(complete).toBeVisible();
});

it('submitted', () => {
renderNav(REPORT_STATUSES.SUBMITTED);
const submitted = screen.getByText('Submitted');
Expand All @@ -68,6 +83,12 @@ describe('SideNav', () => {
});
});

it('displays error message', async () => {
renderNav(REPORT_STATUSES.SUBMITTED, () => {}, false, 'error');
const alert = await screen.findByTestId('alert');
expect(alert).toBeVisible();
});

it('clicking a nav item calls onNavigation', () => {
const onNav = jest.fn();
renderNav(NOT_STARTED, onNav);
Expand Down
1 change: 1 addition & 0 deletions frontend/src/components/Navigator/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ function Navigator({
const hookForm = useForm({
mode: 'onChange',
defaultValues: formData,
shouldUnregister: false,
});

const {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/__tests__/FileUploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ describe('upload tests', () => {
const mockFile = { name: 'MockFile', size: 2000 };
const mockSetErrorMessage = jest.fn();
it('can upload a file and return the correct information', async () => {
const mockFileUpload = jest.spyOn(fileFetcher, 'uploadFile').mockImplementation(async () => ({ id: 1 }));
const mockFileUpload = jest.spyOn(fileFetcher, 'uploadFile').mockImplementation(async () => ({ id: 1, url: 'url' }));
const got = await upload(mockFile, 1, 'fakeAttachment', mockSetErrorMessage);
expect(got).toStrictEqual({
id: 1, originalFileName: mockFile.name, fileSize: mockFile.size, status: 'UPLOADED',
id: 1, originalFileName: mockFile.name, fileSize: mockFile.size, status: 'UPLOADED', url: 'url',
});
expect(mockFileUpload).toHaveBeenCalled();
expect(mockSetErrorMessage).toHaveBeenCalledWith(null);
Expand Down
46 changes: 46 additions & 0 deletions frontend/src/fetchers/__tests__/activityReports.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import join from 'url-join';
import fetchMock from 'fetch-mock';

import {
submitReport, saveReport, reviewReport, resetToDraft,
} from '../activityReports';

describe('activityReports fetcher', () => {
afterEach(() => fetchMock.restore());

describe('submitReport', () => {
it('returns the report', async () => {
const report = { id: 1 };
fetchMock.post(join('api', 'activity-reports', '1', 'submit'), report);
const savedReport = await submitReport(1, report);
expect(savedReport).toEqual(report);
});
});

describe('saveReport', () => {
it('returns the report', async () => {
const report = { id: 1 };
fetchMock.put(join('api', 'activity-reports', '1'), report);
const savedReport = await saveReport(1, report);
expect(savedReport).toEqual(report);
});
});

describe('resetToDraft', () => {
it('calls reset and returns the result', async () => {
const report = { id: 1 };
fetchMock.put(join('api', 'activity-reports', '1', 'reset'), report);
const savedReport = await resetToDraft(1);
expect(savedReport).toEqual(report);
});
});

describe('reviewReport', () => {
it('returns the report', async () => {
const report = { id: 1 };
fetchMock.put(join('api', 'activity-reports', '1', 'review'), report);
const savedReport = await reviewReport(1, report);
expect(savedReport).toEqual(report);
});
});
});
32 changes: 32 additions & 0 deletions frontend/src/pages/ActivityReport/Pages/Review/FileReviewItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Grid } from '@trussworks/react-uswds';

const APPROVED = 'APPROVED';

const FileReviewItem = ({ filename, url, status }) => {
const approved = status === APPROVED;
return (
<Grid row className="margin-top-1 ">
<Grid col={6}>
{filename}
</Grid>
<Grid col={6}>
<div className="flex-align-end display-flex flex-column no-print">
{approved
&& <a href={url}>Download</a>}
{!approved
&& <div>Not Approved</div>}
</div>
</Grid>
</Grid>
);
};

FileReviewItem.propTypes = {
filename: PropTypes.string.isRequired,
status: PropTypes.string.isRequired,
url: PropTypes.string.isRequired,
};

export default FileReviewItem;
48 changes: 48 additions & 0 deletions frontend/src/pages/ActivityReport/Pages/Review/ReviewItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React from 'react';
import PropTypes from 'prop-types';
import _ from 'lodash';
import { useFormContext } from 'react-hook-form';

const ReviewItem = ({ label, name, path }) => {
const { watch } = useFormContext();
const value = watch(name);
let values = value;

if (!Array.isArray(value)) {
values = [value];
}

if (path) {
values = values.map((v) => _.get(v, path));
}

const emptySelector = value && value.length > 0 ? '' : 'smart-hub-review-item--empty';
const classes = ['margin-top-1', emptySelector].filter((x) => x !== '').join(' ');

return (
<div className={`grid-row ${classes} margin-bottom-3 desktop:margin-bottom-0`}>
<div className="grid-col-12 desktop:grid-col-6 font-sans-2xs desktop:font-sans-sm text-bold desktop:text-normal">
{label}
</div>
<div className="grid-col-12 desktop:grid-col-6">
{values.map((v, index) => (
<div aria-label={`${label} ${index + 1}`} key={`${label}${v}`} col={12} className="desktop:flex-align-end display-flex flex-column flex-justify-center">
{Number.isNaN(v) ? '' : v}
</div>
))}
</div>
</div>
);
};

ReviewItem.propTypes = {
label: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
path: PropTypes.string,
};

ReviewItem.defaultProps = {
path: '',
};

export default ReviewItem;
53 changes: 53 additions & 0 deletions frontend/src/pages/ActivityReport/Pages/Review/ReviewPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React from 'react';
import PropTypes from 'prop-types';
import { some } from 'lodash';
import { useFormContext } from 'react-hook-form';

import Section from './ReviewSection';
import ReviewItem from './ReviewItem';

const ReviewPage = ({ sections, path }) => {
const { getValues } = useFormContext();
return (
<>
{sections.map((section) => {
const names = section.items.map((item) => item.name);
const values = getValues(names);
const isEmpty = !some(values, (value) => value && value.length);
return (
<Section
hidePrint={isEmpty}
key={section.title}
basePath={path}
anchor={section.anchor}
title={section.title}
>
{section.items.map((item) => (
<ReviewItem
key={item.label}
label={item.label}
path={item.path}
name={item.name}
/>
))}
</Section>
);
})}
</>
);
};

ReviewPage.propTypes = {
path: PropTypes.string.isRequired,
sections: PropTypes.arrayOf(PropTypes.shape({
title: PropTypes.string,
anchor: PropTypes.string,
items: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.string,
path: PropTypes.string,
name: PropTypes.string,
})),
})).isRequired,
};

export default ReviewPage;
44 changes: 44 additions & 0 deletions frontend/src/pages/ActivityReport/Pages/Review/ReviewSection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import { HashLink } from 'react-router-hash-link';

const Section = ({
title, children, basePath, anchor, hidePrint,
}) => {
const classes = [
'smart-hub-review-section',
'margin-top-2 desktop:margin-top-0',
hidePrint ? 'smart-hub-review-section--empty no-print' : '',
'margin-bottom-3',
].filter((x) => x).join(' ');

return (
<div className={classes}>
<div className="grid-row border-bottom padding-bottom-1 margin-bottom-105">
<div className="grid-col-12 desktop:grid-col-6">
<b className="margin-y-1">{title}</b>
</div>
<div className="grid-col-12 desktop:grid-col-6 display-flex flex-align-end flex-column flex-justify-center">
<HashLink
aria-label={`Edit form section "${title}"`}
to={`${basePath}#${anchor}`}
className="smart-hub-edit-link pull-right no-print"
>
Edit
</HashLink>
</div>
</div>
{children}
</div>
);
};

Section.propTypes = {
hidePrint: PropTypes.bool.isRequired,
title: PropTypes.string.isRequired,
anchor: PropTypes.string.isRequired,
children: PropTypes.node.isRequired,
basePath: PropTypes.string.isRequired,
};

export default Section;

0 comments on commit 9438d3f

Please sign in to comment.