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

[Prod] TR dashboard, resource dashboard updates, validation fixes #2097

Merged
merged 96 commits into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
5d7ec81
wip
AdamAdHocTeam Mar 19, 2024
e9fd200
wip2
AdamAdHocTeam Mar 21, 2024
69ba509
Merge branch 'main' into al/ttahub-2570/flat-resource-sql
AdamAdHocTeam Mar 22, 2024
4893e96
wip
AdamAdHocTeam Mar 25, 2024
c8dc05a
first pass
nvms Mar 25, 2024
8fb316a
maybe make the e2e test happy
nvms Mar 25, 2024
6e9f5dc
completed tests
AdamAdHocTeam Mar 25, 2024
925725f
deploy to sandbox
nvms Mar 25, 2024
7fb8c1d
handle all resource use clacs in sql
AdamAdHocTeam Mar 26, 2024
15ff44e
update topic use to be all sql
AdamAdHocTeam Mar 26, 2024
a9e8e0f
create header table for now, unless we save a flat file in the future
AdamAdHocTeam Mar 26, 2024
3757d89
rollup topics and resource use
AdamAdHocTeam Mar 26, 2024
d5788ad
sort by date
AdamAdHocTeam Mar 26, 2024
228f7bf
Merge branch 'main' into al/ttahub-2570/flat-resource-sql
AdamAdHocTeam Mar 27, 2024
f2c4ea8
get test passing
AdamAdHocTeam Mar 27, 2024
a747c16
for dev testing
AdamAdHocTeam Mar 27, 2024
b595d62
comment and fix tests
AdamAdHocTeam Mar 27, 2024
5bb81c4
hook up cache to flat resource data set
AdamAdHocTeam Mar 28, 2024
95d59e3
clean up and add test
AdamAdHocTeam Mar 28, 2024
ab3715f
rollback a change to the old resource data func
AdamAdHocTeam Mar 28, 2024
b53bcc5
update sorting
AdamAdHocTeam Mar 29, 2024
18e0943
match sorting on resource use for now
AdamAdHocTeam Mar 29, 2024
e530e71
fix topics cound per AR
AdamAdHocTeam Mar 29, 2024
f3e3786
clean up
AdamAdHocTeam Apr 1, 2024
8e9ca95
restructure the new flat dataset to match what we currently expect on…
AdamAdHocTeam Apr 1, 2024
4bb7dfb
hook up new flat resources to FE
AdamAdHocTeam Apr 1, 2024
3f7e673
hook up FE
AdamAdHocTeam Apr 1, 2024
610ab3e
lint
AdamAdHocTeam Apr 1, 2024
affd713
add both routes for now
AdamAdHocTeam Apr 2, 2024
7f83e79
hook up both paths from UI
AdamAdHocTeam Apr 2, 2024
3cc9c24
set timeout for cache to 10 sec for testing
AdamAdHocTeam Apr 2, 2024
55b3261
}
AdamAdHocTeam Apr 3, 2024
07d1dda
fix lint
AdamAdHocTeam Apr 3, 2024
fd72bd1
take of resource limiter
AdamAdHocTeam Apr 3, 2024
89bcabc
Merge branch 'main' into jp/1185/validate-on-click
nvms Apr 4, 2024
e279126
possibly better keyboard interactions
nvms Apr 4, 2024
e937d8e
onClick
nvms Apr 4, 2024
11ad181
use aria-hidden
nvms Apr 4, 2024
ede534f
switch to new resource db for now
AdamAdHocTeam Apr 4, 2024
653f5f6
don't prevent default here
nvms Apr 5, 2024
112a6dc
fix test
nvms Apr 5, 2024
8965a1b
first pass at Garretts suggestions
AdamAdHocTeam Apr 5, 2024
40934a6
Create backend for reason list
thewatermethod Apr 8, 2024
b349358
more fixes per Garrett
AdamAdHocTeam Apr 8, 2024
0323f83
Work in progress
thewatermethod Apr 8, 2024
5db968c
Finish tests
thewatermethod Apr 8, 2024
4a6590e
fixes with Garrett
AdamAdHocTeam Apr 8, 2024
6c2cdb3
more updates per Garretts comments
AdamAdHocTeam Apr 8, 2024
06dd944
add a check to make sure we dont count topics without resources
AdamAdHocTeam Apr 8, 2024
de6e962
put back cache
AdamAdHocTeam Apr 8, 2024
464386d
add Matts fix for ar on resource db
AdamAdHocTeam Apr 8, 2024
4820d6f
add frontend to regional dashboard
thewatermethod Apr 10, 2024
1b5458b
Merge branch 'main' into al/ttahub-2570/flat-resource-sql
AdamAdHocTeam Apr 10, 2024
7897e6c
Add widgets to export rollup and comment
thewatermethod Apr 10, 2024
1d210ec
Merge branch 'main' into mb/TTAHUB-2756/tr-reason-list
thewatermethod Apr 10, 2024
0676ed8
Merge branch 'mb/TTAHUB-2756/tr-reason-list' into mb/TTAHUB/frontend-…
thewatermethod Apr 10, 2024
a7c4a83
Merge pull request #2057 from HHS/jp/1185/validate-on-click
nvms Apr 10, 2024
c0c67eb
Implement frontend
thewatermethod Apr 10, 2024
088bd29
Update backend to match FE graph expectation
thewatermethod Apr 10, 2024
72ba8d3
Tests and fixes
thewatermethod Apr 10, 2024
946755d
Regional dashboard test e2e test update
thewatermethod Apr 10, 2024
c9d0bad
Merge remote-tracking branch 'origin/main' into mb/TTAHUB/frontend-fo…
thewatermethod Apr 10, 2024
05b7098
Merge remote-tracking branch 'origin/main' into mb/TTAHUB-2756/tr-rea…
thewatermethod Apr 10, 2024
a272513
Merge branch 'mb/TTAHUB-2756/tr-reason-list' into mb/TTAHUB/frontend-…
thewatermethod Apr 10, 2024
0a79161
Axe updates
thewatermethod Apr 10, 2024
b198f8b
add back test per Matt
AdamAdHocTeam Apr 10, 2024
262fa83
Bump protobufjs from 7.2.4 to 7.2.6
dependabot[bot] Apr 10, 2024
00063f3
Merge pull request #2094 from HHS/dependabot/npm_and_yarn/protobufjs-…
dependabot[bot] Apr 11, 2024
d70baf4
Merge branch 'main' into al/ttahub-2570/flat-resource-sql
AdamAdHocTeam Apr 11, 2024
09260b2
fix issue Matt found with sorting
AdamAdHocTeam Apr 11, 2024
164eaa1
Deploy to sandbox
thewatermethod Apr 11, 2024
d2f8994
Test image requested
thewatermethod Apr 11, 2024
4f64ea7
Take patricks advice instead
thewatermethod Apr 11, 2024
db9429a
Try this instead
thewatermethod Apr 11, 2024
8ffdcd6
Use older image
thewatermethod Apr 11, 2024
03c8a5d
Try a diff image
thewatermethod Apr 11, 2024
b965323
add resize observer hook
thewatermethod Apr 11, 2024
3c4088a
Update image again
thewatermethod Apr 11, 2024
441133f
address height issue
thewatermethod Apr 11, 2024
e5d3c99
Revert change to accessible table widget
thewatermethod Apr 11, 2024
1a8e490
Merge remote-tracking branch 'origin/main' into mb/TTAHUB/frontend-fo…
thewatermethod Apr 11, 2024
7f48a87
Retry
thewatermethod Apr 11, 2024
68bd801
Remove double docker pull
thewatermethod Apr 11, 2024
1bea9e9
New docker image, trying again
thewatermethod Apr 11, 2024
83f8b91
Update CI machine
thewatermethod Apr 11, 2024
ff803ec
Update image ref in sh script
thewatermethod Apr 11, 2024
803609a
Merge pull request #2095 from HHS/debug-owasp-image-issue
thewatermethod Apr 11, 2024
0c34cb9
Merge remote-tracking branch 'origin/main' into mb/TTAHUB/frontend-fo…
thewatermethod Apr 11, 2024
ea7ab34
Merge branch 'main' into al/ttahub-2570/flat-resource-sql
AdamAdHocTeam Apr 12, 2024
1eafaef
Update sorting and test for it
thewatermethod Apr 12, 2024
86fb684
Merge remote-tracking branch 'origin/main' into mb/TTAHUB-2756/tr-rea…
thewatermethod Apr 12, 2024
cf1e0b5
Merge branch 'mb/TTAHUB-2756/tr-reason-list' into mb/TTAHUB/frontend-…
thewatermethod Apr 12, 2024
a4d9126
Merge pull request #2093 from HHS/mb/TTAHUB/frontend-for-tr-dashboard
thewatermethod Apr 12, 2024
d86f35d
Merge pull request #2044 from HHS/al/ttahub-2570/flat-resource-sql
AdamAdHocTeam Apr 12, 2024
dac2c81
Design review changes
thewatermethod Apr 12, 2024
f82f359
Merge pull request #2082 from HHS/mb/TTAHUB-2756/tr-reason-list
thewatermethod Apr 12, 2024
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
10 changes: 5 additions & 5 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -288,10 +288,10 @@ parameters:
type: string
dev_git_branch: # change to feature branch to test deployment
description: "Name of github branch that will deploy to dev"
default: "mb/TTAHUB-2501/front-end-goal-name-filter"
default: "al/ttahub-2570/flat-resource-sql"
type: string
sandbox_git_branch: # change to feature branch to test deployment
default: "mb/TTAHUB-2510/add-IST-visit-dropdown"
default: "mb/TTAHUB/frontend-for-tr-dashboard"
type: string
prod_new_relic_app_id:
default: "877570491"
Expand All @@ -305,7 +305,7 @@ parameters:
sandbox_new_relic_app_id:
default: "867346799"
type: string
jobs:
jobs:
build_and_lint:
executor: docker-executor
steps:
Expand Down Expand Up @@ -644,7 +644,7 @@ jobs:
command: ./bin/ping-server 8080
- run:
name: Pull OWASP ZAP docker image
command: docker pull owasp/zap2docker-stable:latest
command: docker pull softwaresecurityproject/zap-stable:latest
- run:
name: Make reports directory group writeable
command: chmod g+w reports
Expand All @@ -653,7 +653,7 @@ jobs:
command: ./bin/run-owasp-scan
- store_artifacts:
path: reports/owasp_report.html
resource_class: large
resource_class: arm.large
deploy:
executor: docker-executor
steps:
Expand Down
2 changes: 1 addition & 1 deletion bin/ping-server
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ until $(curl --output /dev/null --max-time 10 --silent --head --fail http://loca
fi

attempt_counter=$(($attempt_counter+1))
sleep 10
sleep 30
done
2 changes: 1 addition & 1 deletion bin/run-owasp-scan
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ docker run \
--rm \
--user zap:$(id -g) \
--network=$network \
-t owasp/zap2docker-stable:latest zap-baseline.py \
-t softwaresecurityproject/zap-stable:latest zap-baseline.py \
-t http://server:8080 \
-c zap.conf -I -i -r owasp_report.html
4 changes: 2 additions & 2 deletions docker-compose.override.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ services:
volumes:
- ".:/app:rw"
owasp_zap_backend:
image: owasp/zap2docker-stable:latest
image: softwaresecurityproject/zap-stable:latest
platform: linux/arm64
user: zap
command: zap-full-scan.py -t http://backend:8080 -c zap.conf -i -r owasp_report.html
Expand All @@ -70,7 +70,7 @@ services:
depends_on:
- backend
owasp_zap_similarity:
image: owasp/zap2docker-stable:latest
image: softwaresecurityproject/zap-stable:latest
platform: linux/arm64
user: zap
command: zap-api-scan.py -t http://similarity:8080/openapi.json -f openapi -I -i -r owasp_api_report.html
Expand Down
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@hookform/error-message": "^0.0.5",
"@react-hook/resize-observer": "^1.2.6",
"@trussworks/react-uswds": "4.1.1",
"@ttahub/common": "2.0.18",
"@use-it/interval": "^1.0.0",
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/App.scss
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
@use 'colors.scss' as *;
@use './Grid.scss';

@use './widgets/widgets.scss';

@font-face {
font-family: 'FontAwesome';
src: url('./assets/fa-solid-900.ttf') format('truetype'), url('./assets/fa-solid-900.woff2') format('woff2');
Expand Down Expand Up @@ -403,4 +405,9 @@ fill: #1B1B1B;
.desktop\:maxw-6 {
max-width: 3rem;
}
}
}

.smart-hub--vertical-text {
writing-mode: vertical-lr;
transform: rotate(180deg);
}
5 changes: 3 additions & 2 deletions frontend/src/components/MediaCaptureButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import html2canvas from 'html2canvas';
import { Button } from '@trussworks/react-uswds';

export default function MediaCaptureButton({
reference, className, buttonText, id,
reference, className, buttonText, id, title,
}) {
const capture = async () => {
try {
Expand All @@ -24,7 +24,7 @@ export default function MediaCaptureButton({
const base64image = canvas.toDataURL('image/png');
const a = document.createElement('a');
a.href = base64image;
a.setAttribute('download', '');
a.setAttribute('download', `${title}.png`);
a.click();
} catch (e) {
// eslint-disable-next-line no-console
Expand All @@ -50,6 +50,7 @@ MediaCaptureButton.propTypes = {
className: PropTypes.string,
buttonText: PropTypes.string,
id: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
};

MediaCaptureButton.defaultProps = {
Expand Down
83 changes: 53 additions & 30 deletions frontend/src/components/MultiSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
through to react-select. If the selected value is not in the options prop the multiselect box will
display an empty tag.
*/
import React from 'react';
import React, { useRef } from 'react';
import PropTypes from 'prop-types';
import Select, { components } from 'react-select';
import Creatable from 'react-select/creatable';
Expand Down Expand Up @@ -104,8 +104,10 @@ function MultiSelect({
onCreateOption,
placeholderText,
components: componentReplacements,
onClick = () => {},
}) {
const inputId = `select-${uuidv4()}`;
const selectorRef = useRef(null);

/**
* unfortunately, given our support for ie11, we can't
Expand Down Expand Up @@ -158,42 +160,61 @@ function MultiSelect({
}
};

const onKeyDown = (e) => {
if (e.key === 'Enter' || e.key === ' ') {
selectorRef.current.focus();
onClick();
}
};

const Selector = canCreate ? Creatable : Select;

return (
<Controller
render={({ onChange: controllerOnChange, value, onBlur }) => {
const values = value ? getValues(value) : value;
return (
<Selector
className="ttahub-multi-select margin-top-1"
id={name}
value={values}
onBlur={onBlur}
onChange={(event) => {
if (onItemSelected) {
onItemSelected(event);
} else if (event) {
onChange(event, controllerOnChange);
} else {
controllerOnChange([]);
}
}}
inputId={inputId}
styles={styles(singleRowInput)}
components={{ ...componentReplacements, DropdownIndicator }}
options={options}
isDisabled={disabled}
tabSelectsValue={false}
isClearable={multiSelectOptions.isClearable}
closeMenuOnSelect={multiSelectOptions.closeMenuOnSelect || false}
controlShouldRenderValue={multiSelectOptions.controlShouldRenderValue}
hideSelectedOptions={multiSelectOptions.hideSelectedOptions}
placeholder={placeholderText || ''}
onCreateOption={onCreateOption}
isMulti
required={!!(required)}
/>
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
<div
onClick={onClick}
onKeyDown={onKeyDown}
data-testid={`${name}-click-container`}
// eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={disabled ? 0 : undefined}
>
<div aria-hidden={disabled}>
<Selector
ref={selectorRef}
className="ttahub-multi-select margin-top-1"
id={name}
value={values}
onBlur={onBlur}
onChange={(event) => {
if (onItemSelected) {
onItemSelected(event);
} else if (event) {
onChange(event, controllerOnChange);
} else {
controllerOnChange([]);
}
}}
inputId={inputId}
styles={styles(singleRowInput)}
components={{ ...componentReplacements, DropdownIndicator }}
options={options}
isDisabled={disabled}
tabSelectsValue={false}
isClearable={multiSelectOptions.isClearable}
closeMenuOnSelect={multiSelectOptions.closeMenuOnSelect || false}
controlShouldRenderValue={multiSelectOptions.controlShouldRenderValue}
hideSelectedOptions={multiSelectOptions.hideSelectedOptions}
placeholder={placeholderText || ''}
onCreateOption={onCreateOption}
isMulti
required={!!(required)}
/>
</div>
</div>
);
}}
control={control}
Expand Down Expand Up @@ -253,6 +274,7 @@ MultiSelect.propTypes = {
}),
required: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
placeholderText: PropTypes.string,
onClick: PropTypes.func,
};

MultiSelect.defaultProps = {
Expand All @@ -269,6 +291,7 @@ MultiSelect.defaultProps = {
onItemSelected: null,
onCreateOption: null,
placeholderText: null,
onClick: null,
};

export default MultiSelect;
19 changes: 19 additions & 0 deletions frontend/src/components/WidgetH2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';

export default function WidgetH2({ children, classNames }) {
return (
<h2 className={`ttahub--dashboard-widget-heading margin-0 font-sans-lg ${classNames}`}>
{children}
</h2>
);
}

WidgetH2.propTypes = {
children: PropTypes.node.isRequired,
classNames: PropTypes.string,
};

WidgetH2.defaultProps = {
classNames: '',
};
2 changes: 1 addition & 1 deletion frontend/src/components/__tests__/MediaCaptureButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('MediaCaptureButton', () => {
const RenderCaptureButton = () => {
const widget = useRef();
return (
<div ref={widget}><MediaCaptureButton id="mediaCaptureTest" reference={widget} /></div>
<div ref={widget}><MediaCaptureButton title="title" id="mediaCaptureTest" reference={widget} /></div>
);
};
it('renders', () => {
Expand Down
32 changes: 31 additions & 1 deletion frontend/src/components/__tests__/MultiSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const customOptions = [

describe('MultiSelect', () => {
// eslint-disable-next-line react/prop-types
const TestMultiSelect = ({ onSubmit }) => {
const TestMultiSelect = ({ onSubmit, disabled = false }) => {
const { control, handleSubmit } = useForm({
defaultValues: { name: [] },
mode: 'all',
Expand All @@ -41,6 +41,8 @@ describe('MultiSelect', () => {
name="name"
options={options}
required={false}
onClick={() => {}}
disabled={disabled}
/>
<button data-testid="submit" type="submit">submit</button>
</Label>
Expand Down Expand Up @@ -159,4 +161,32 @@ describe('MultiSelect', () => {
},
]);
});

describe('the div wrapper', () => {
it('forwards space to the Selector, expanding the multiselect', async () => {
render(<TestMultiSelect />);
const container = screen.getByTestId('name-click-container');
container.focus();
await act(async () => {
userEvent.type(container, '{space}');
});
expect(await screen.findByText('one')).toBeVisible();
});
it('forwards enter to the Selector, giving it focus', async () => {
render(<TestMultiSelect disabled onSubmit={() => {}} />);
const container = screen.getByTestId('name-click-container');
container.focus();
await act(async () => {
userEvent.type(container, '{enter}');
});
const selector = container.querySelector('input');
expect(selector).toHaveFocus();
});
it('hides the Selector with aria-hidden when disabled', async () => {
render(<TestMultiSelect disabled />);
const container = screen.getByTestId('name-click-container');
const div = container.querySelector('div');
expect(div).toHaveAttribute('aria-hidden', 'true');
});
});
});
9 changes: 9 additions & 0 deletions frontend/src/fetchers/Resources.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ export const fetchResourceData = async (query) => {
};
};

export const fetchFlatResourceData = async (query) => {
const res = await get(join('/', 'api', 'resources', 'flat', `?${query}`));
const data = await res.json();

return {
...data,
};
};

export const fetchTopicResources = async (sortBy = 'updatedAt', sortDir = 'desc', offset = 0, limit = TOPICS_PER_PAGE, filters) => {
const request = join('/', 'api', 'resources', 'topic-resources', `?sortBy=${sortBy}&sortDir=${sortDir}&offset=${offset}&limit=${limit}${filters ? `&${filters}` : ''}`);
const res = await get(request);
Expand Down
17 changes: 17 additions & 0 deletions frontend/src/hooks/useSize.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// https://www.npmjs.com/package/@react-hook/resize-observer
import { useState, useLayoutEffect } from 'react';
import useResizeObserver from '@react-hook/resize-observer';

const useSize = (target) => {
const [size, setSize] = useState();

useLayoutEffect(() => {
setSize(target.current.getBoundingClientRect());
}, [target]);

// Where the magic happens
useResizeObserver(target, (entry) => setSize(entry.contentRect));
return size;
};

export default useSize;
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,27 @@ describe('activity summary', () => {
expect(await screen.findByText('Duration must be less than or equal to 99 hours')).toBeInTheDocument();
});
});

describe('activity recipients validation', () => {
it('shows a validation message when clicked and recipient type is not selected', async () => {
render(<RenderActivitySummary />);
const input = screen.getByTestId('activityRecipients-click-container');
userEvent.click(input);
expect(await screen.findByText('You must first select who the activity is for')).toBeInTheDocument();
});

it('hides the message when the recipient type is selected', async () => {
const { container } = render(<RenderActivitySummary />);
const input = screen.getByTestId('activityRecipients-click-container');
userEvent.click(input);
expect(await screen.findByText('You must first select who the activity is for')).toBeInTheDocument();
await act(() => {
const recipient = container.querySelector('#category-recipient');
userEvent.click(recipient);
});
expect(screen.queryByText('You must first select who the activity is for')).not.toBeInTheDocument();
});
});
});

describe('groups', () => {
Expand Down
Loading