Skip to content

Commit

Permalink
TTA overview widget (#316)
Browse files Browse the repository at this point in the history
* First pass at a pattern for widgets

* Set coverage limits to 0 so we deploy to staging

These limits will be increased back to what they are on main before
merging.

* Reset coverage limits

* TTA Overview widget initial commit

* Add backend tests

* Deploy to sandbox

* Fix lint error

* Fix frontend lint errors

* Merge main and fix invalid region

* Refactor test

* Add frontend tests

* Add frontend tests

* Styling improvements

* Add region number in caption

* Update mocks in landing tests

* Adjusted sizing, flickering

* Add temp border to overview widget

Co-authored-by: Josh Salisbury <[email protected]>
  • Loading branch information
kryswisnaskas and jasalisbury authored Jun 15, 2021
1 parent 0e4fa94 commit 6b48a0f
Show file tree
Hide file tree
Showing 38 changed files with 1,242 additions and 118 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ parameters:
default: "main"
type: string
sandbox_git_branch: # change to feature branch to test deployment
default: "kw-adjust-grantee-job"
default: "kw-overview-widget"
type: string
prod_new_relic_app_id:
default: "877570491"
Expand Down
1 change: 1 addition & 0 deletions frontend/.env
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ BACKEND_PROXY=http://localhost:8080
REACT_APP_INACTIVE_MODAL_TIMEOUT=1500000
REACT_APP_SESSION_TIMEOUT=1800000
REACT_APP_TTA_SMART_HUB_URI=http://localhost:3000
REACT_APP_ENABLE_WIDGETS=true
12 changes: 11 additions & 1 deletion frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import Home from './pages/Home';
import Landing from './pages/Landing';
import ActivityReport from './pages/ActivityReport';
import LegacyReport from './pages/LegacyReport';
import Widgets from './pages/Widgets';

import isAdmin from './permissions';
import 'react-dates/initialize';
import 'react-dates/lib/css/_datepicker.css';
Expand Down Expand Up @@ -76,6 +78,7 @@ function App() {
}

const admin = isAdmin(user);
const enableWidgets = process.env.REACT_APP_ENABLE_WIDGETS === 'true';

const renderAuthenticatedRoutes = () => (
<div role="main" id="main-content">
Expand Down Expand Up @@ -113,7 +116,14 @@ function App() {
<ActivityReport location={location} match={match} user={user} />
)}
/>

{enableWidgets && (
<Route
path="/widgets"
render={() => (
<Widgets />
)}
/>
)}
{admin && (
<Route
path="/admin"
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/__tests__/permissions.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ describe('permissions', () => {
},
],
};
const regions = allRegionsUserHasPermissionTo(user);
const includeAdmin = true;
const regions = allRegionsUserHasPermissionTo(user, includeAdmin);
expect(regions).toEqual(expect.arrayContaining([14, 3, 4]));
});

Expand Down
24 changes: 24 additions & 0 deletions frontend/src/components/RegionDropdown.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.smart-hub--region-dropdown {
background-color: #0166AB;
border: none;
border-radius: 5px;
color: #FFFFFF;
padding-left: 18px;
font-weight: 700;
font-size: 17px;
display:inline-block;
background-image: url(../images/triange_down.png);
}

.smart-hub--region-dropdown option{
background-color: white;
border: none;
border-radius: 5px;
padding: 0px;
}

/* .arrow-down {
border-left:5px solid rgba(0,0,0,0);
border-right:5px solid rgba(0,0,0,0);
color: #FFFFFF;
} */
10 changes: 10 additions & 0 deletions frontend/src/fetchers/Widgets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import join from 'url-join';
import { get } from './index';

const fetchWidget = async (widgetId, region, query = '') => {
const queryStr = query ? `?${query}&` : '?&';
const res = await get(join('/', 'api', 'widgets', `${widgetId}${queryStr}region.in[]=${region}`));
return res.json();
};

export default fetchWidget;
1 change: 1 addition & 0 deletions frontend/src/images/check.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/src/images/triange_down.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion frontend/src/pages/Landing/Filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
DATE_FMT,
QUERY_CONDITIONS,
} from './constants';
import { DECIMAL_BASE } from '../../Constants';
import './Filter.css';

const defaultFilter = () => (
Expand Down Expand Up @@ -186,7 +187,7 @@ Filter.defaultProps = {
forMyAlerts: false,
};

export function filtersToQueryString(filters) {
export function filtersToQueryString(filters, region) {
const filtersWithValues = filters.filter((f) => {
if (f.condition === WITHIN) {
const [startDate, endDate] = f.query.split('-');
Expand All @@ -198,6 +199,9 @@ export function filtersToQueryString(filters) {
const con = QUERY_CONDITIONS[filter.condition];
return `${filter.topic}.${con}=${filter.query}`;
});
if (region && (parseInt(region, DECIMAL_BASE) !== -1)) {
queryFragments.push(`region.in[]=${parseInt(region, DECIMAL_BASE)}`);
}
return queryFragments.join('&');
}

Expand Down
152 changes: 152 additions & 0 deletions frontend/src/pages/Landing/RegionalSelect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/* eslint-disable react/forbid-prop-types */
/* eslint-disable react/jsx-props-no-spreading */
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import Select, { components } from 'react-select';
import { Button } from '@trussworks/react-uswds';

import 'uswds/dist/css/uswds.css';
import '@trussworks/react-uswds/lib/index.css';
import './index.css';

import triangleDown from '../../images/triange_down.png';
import check from '../../images/check.svg';

const DropdownIndicator = (props) => (
<components.DropdownIndicator {...props}>
<img alt="" style={{ width: '22px' }} src={triangleDown} />
</components.DropdownIndicator>
);

const Placeholder = (props) => <components.Placeholder {...props} />;

export const getUserOptions = (regions) => regions.map((region) => ({ value: region, label: `Region ${region}` }));

const styles = {
container: (provided, state) => {
// To match the focus indicator provided by uswds
const outline = state.isFocused ? '0.25rem solid #2491ff;' : '';
return {
...provided,
outline,
};
},
input: () => ({ display: 'none' }),
control: (provided) => ({
...provided,
borderColor: '#0166AB',
backgroundColor: '#0166AB',
borderRadius: '5px',
paddingLeft: '5px',
paddingTop: '4px',
paddingBottom: '4px',
whiteSpace: 'nowrap',
color: 'white',
width: '120px',
}),
indicatorSeparator: () => ({ display: 'none' }),
menu: (provided) => ({
...provided,
width: '200px',
}),
option: (provided, state) => ({
...provided,
color: state.isSelected ? '#0166AB' : 'black',
fontWeight: state.isSelected ? '700' : 'normal',
backgroundColor: state.isSelected ? '#F8F8F8' : '#FFFFFF',
padding: 11,
}),
singleValue: (provided) => {
const single = { color: '#FFFFFF', fontWeight: 600 };

return {
...provided, ...single,
};
},
valueContainer: () => ({ padding: '10px 8px' }),
};

function RegionalSelect(props) {
const {
regions, onApply,
} = props;

const [selectedItem, setSelectedItem] = useState();
const [appliedItem, setAppliedItem] = useState();
const [menuIsOpen, setMenuIsOpen] = useState(false);

// const delayedCloseMenu = () => setTimeout(setMenuIsOpen(false), 1000);

const CustomOption = (customOptionProps) => {
const {
data, innerRef, innerProps, isSelected,
} = customOptionProps;
return data.custom ? (
<div ref={innerRef} {...innerProps}>
<Button
type="button"
className="float-left margin-2 smart-hub--filter-button"
onClick={() => {
onApply(selectedItem);
setAppliedItem(selectedItem);
setMenuIsOpen(false);
}}
>
Apply
</Button>
</div>
) : (
<components.Option {...customOptionProps}>
{data.label}
{isSelected && (
<img
className="tta-smarthub--check"
src={check}
style={{
width: 32,
float: 'right',
marginTop: '-9px ',
}}
alt={data.label}
/>
)}
</components.Option>
);
};

CustomOption.propTypes = {
data: PropTypes.object.isRequired,
innerRef: PropTypes.string.isRequired,
innerProps: PropTypes.object.isRequired,
};

const options = [...getUserOptions(regions), { custom: true }];
return (
<Select
options={options}
menuIsOpen={menuIsOpen}
onChange={(value) => { if (value && value.value) setSelectedItem(value); }}
onMenuOpen={() => setMenuIsOpen(true)}
onBlur={() => setMenuIsOpen(false)}
// onBlur={() => delayedCloseMenu()}
name="RegionalSelect"
defaultValue={options[0]}
value={{
value: selectedItem ? selectedItem.value : options[0].value,
label: appliedItem ? appliedItem.label : options[0].label,
}}
styles={styles}
components={{ Placeholder, DropdownIndicator, Option: CustomOption }}
placeholder="Select Region"
closeMenuOnSelect={false}
maxMenuHeight={600}
/>
);
}

RegionalSelect.propTypes = {
regions: PropTypes.arrayOf(PropTypes.number).isRequired,
onApply: PropTypes.func.isRequired,
};

export default RegionalSelect;
5 changes: 5 additions & 0 deletions frontend/src/pages/Landing/__tests__/MyAlerts.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ const renderMyAlerts = () => {
const alertsPerPage = ALERTS_PER_PAGE;
const alertsActivePage = 1;
const alertReportsCount = 10;
const updateReportAlerts = jest.fn();
const setAlertReportsCount = jest.fn();
const requestAlertsSort = jest.fn();

render(
<Router history={history}>
<MyAlerts
Expand All @@ -31,6 +34,8 @@ const renderMyAlerts = () => {
alertsActivePage={alertsActivePage}
alertReportsCount={alertReportsCount}
sortHandler={requestAlertsSort}
updateReportAlerts={updateReportAlerts}
setAlertReportsCount={setAlertReportsCount}
fetchReports={() => {}}
updateReportFilters={() => {}}
handleDownloadAllAlerts={() => {}}
Expand Down
44 changes: 44 additions & 0 deletions frontend/src/pages/Landing/__tests__/RegionalSelect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import '@testing-library/jest-dom';
import React from 'react';
import {
render, screen, fireEvent,
} from '@testing-library/react';
import { Router } from 'react-router';
import { createMemoryHistory } from 'history';

import selectEvent from 'react-select-event';
import RegionalSelect from '../RegionalSelect';

const renderRegionalSelect = () => {
const history = createMemoryHistory();
const onApplyRegion = jest.fn();

render(
<Router history={history}>
<RegionalSelect
regions={[1, 2]}
onApply={onApplyRegion}
/>
</Router>,
);
return history;
};

describe('Regional Select', () => {
test('displays correct region in input', async () => {
renderRegionalSelect();
const input = await screen.findByText(/region 1/i);
expect(input).toBeVisible();
});

test('changes input value on apply', async () => {
renderRegionalSelect();
let input = await screen.findByText(/region 1/i);
expect(input).toBeVisible();
await selectEvent.select(screen.getByText(/region 1/i), [/region 2/i]);
const applyButton = await screen.findByText(/apply/i);
fireEvent.click(applyButton);
input = await screen.findByText(/region 2/i);
expect(input).toBeVisible();
});
});
Loading

0 comments on commit 6b48a0f

Please sign in to comment.