Skip to content

Commit

Permalink
TTA-HUB: 397 Add Pill Filters to Grantee Page (#481)
Browse files Browse the repository at this point in the history
* first version of filter pills

* added first version of filter pills

* added tests for filter pill ui

* change colors and add test

* fixed issues found by Matt

* fixed filter issues and specialists check boxes

* fixed pill row spacing and moved AND to front

* fix audit vuln

* fix lint errors

* wip

* css fixes

* code clean up

* added unit test for tool tip and fixed filter pill test

* align text in ellipsis pill

* fixes based on Joshs PR review

Co-authored-by: Matt Bevilacqua <[email protected]>
  • Loading branch information
AdamAdHocTeam and thewatermethod authored Nov 9, 2021
1 parent 210c2e3 commit 53da9e7
Show file tree
Hide file tree
Showing 13 changed files with 423 additions and 67 deletions.
2 changes: 1 addition & 1 deletion frontend/src/components/CheckboxSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default function CheckboxSelect(props) {
*/
const onApplyClick = () => {
const checked = Object.keys(checkboxes).filter((checkbox) => checkboxes[checkbox]);
onApply(checked);
onApply(checked, toggleAllChecked);
};

const canBlur = (e) => {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/SpecialistSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ export const ROLES_MAP = [
];

export default function SpecialistSelect({ onApplyRoles, labelId }) {
const onApply = (selected) => {
const onApply = (selected, toggleAllChecked) => {
const roleValues = selected.map((s) => parseInt(s, 10));

const roles = ROLES_MAP.filter(
(role) => roleValues.includes(role.selectValue),
).map((role) => role.value);

onApplyRoles(roles);
onApplyRoles(roles, toggleAllChecked);
};

return (
Expand Down
29 changes: 18 additions & 11 deletions frontend/src/components/Tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import PropTypes from 'prop-types';
import './Tooltip.css';

export default function Tooltip({
displayText, tooltipText, buttonLabel, screenReadDisplayText,
displayText, tooltipText, buttonLabel, screenReadDisplayText, hideUnderline,
}) {
const [showTooltip, setShowTooltip] = useState(false);

Expand All @@ -21,16 +21,21 @@ export default function Tooltip({
<span className="smart-hub--ellipsis">
<span aria-hidden={!screenReadDisplayText}>
{displayText}
<svg height="5" xmlns="http://www.w3.org/2000/svg" version="1.1" aria-hidden="true">
<path
d="M 0 5 L 190 5"
stroke="black"
strokeLinecap="round"
strokeWidth="1"
strokeDasharray="5,5"
fill="none"
/>
</svg>
{
hideUnderline ? null
: (
<svg height="5" xmlns="http://www.w3.org/2000/svg" version="1.1" aria-hidden="true">
<path
d="M 0 5 L 190 5"
stroke="black"
strokeLinecap="round"
strokeWidth="1"
strokeDasharray="5,5"
fill="none"
/>
</svg>
)
}
</span>
</span>
<span className="sr-only">
Expand All @@ -54,8 +59,10 @@ Tooltip.propTypes = {
]).isRequired,
buttonLabel: PropTypes.string.isRequired,
screenReadDisplayText: PropTypes.bool,
hideUnderline: PropTypes.bool,
};

Tooltip.defaultProps = {
screenReadDisplayText: true,
hideUnderline: false,
};
2 changes: 1 addition & 1 deletion frontend/src/components/__tests__/CheckboxSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ describe('Checkbox select', () => {
expect(pom).toBeChecked();
const apply = screen.getByRole('button', { name: /apply filters/i });
userEvent.click(apply);
expect(onApply).toHaveBeenCalledWith(['1']);
expect(onApply).toHaveBeenCalledWith(['1'], false);
userEvent.click(button);
expect(document.querySelectorAll(':checked').length).toBe(1);
});
Expand Down
46 changes: 46 additions & 0 deletions frontend/src/components/__tests__/ToolTip.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import '@testing-library/jest-dom';
import React from 'react';
import {
render, screen,
} from '@testing-library/react';
import Tooltip from '../Tooltip';

describe('Tooltip', () => {
const renderTooltip = (
displayText,
screenReadDisplayText,
buttonLabel,
tooltipText,
hideUnderline,
) => {
render(
<div data-testid="tooltip-container">
<Tooltip
displayText={displayText}
screenReadDisplayText={screenReadDisplayText}
buttonLabel={buttonLabel}
tooltipText={tooltipText}
hideUnderline={hideUnderline}
/>
</div>,
);
};

afterAll(() => {
jest.clearAllMocks();
});

it('renders correctly', async () => {
renderTooltip('my display text', false, 'my button label', 'my tool tip text', false);
expect(await screen.findByText(/my tool tip text/i)).toBeVisible();
expect(await screen.findByText(/my button label/i)).toBeVisible();
expect(await screen.findByText(/my display text/i)).toBeVisible();
const button = await screen.findByRole('button', { name: /button label/i });
await expect(button).toBeInTheDocument();
await expect(document.querySelector('svg')).toBeInTheDocument();
});
it('renders without underline', async () => {
renderTooltip('my display text', false, 'my button label', 'my tool tip text', true);
await expect(document.querySelector('svg')).not.toBeInTheDocument();
});
});
34 changes: 27 additions & 7 deletions frontend/src/components/filter/FilterItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,37 @@ import React from 'react';
import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTimesCircle } from '@fortawesome/free-solid-svg-icons';
import moment from 'moment';
import FilterDateRange from './FilterDateRange';
import SpecialistSelect from '../SpecialistSelect';
import { formatDateRange } from '../DateRangeSelect';
import SpecialistSelect, { ROLES_MAP } from '../SpecialistSelect';
import {
DATE_CONDITIONS,
SELECT_CONDITIONS,
} from '../constants';

import './FilterItem.css';

const YEAR_TO_DATE = formatDateRange({
yearToDate: true,
forDateTime: true,
});

const filterProp = PropTypes.shape({
topic: PropTypes.string,
condition: PropTypes.string,
query: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
id: PropTypes.string,
});

const specialistName = ROLES_MAP.map((r) => r.value);

const todaysDate = moment().format('YYYY/MM/DD');

const DEFAULT_VALUES = {
startDate: { 'Is within': YEAR_TO_DATE, 'Is after': todaysDate, 'Is before': todaysDate },
role: { Contains: specialistName, 'Does not contain': specialistName },
};

/**
* The individual filter controls with the set of dropdowns
*
Expand All @@ -32,16 +47,21 @@ export default function FilterItem({ filter, onRemoveFilter, onUpdateFilter }) {
query,
} = filter;

const onUpdate = (name, value) => {
onUpdateFilter(id, name, value);
};
const onUpdate = (name, value, toggleAllChecked = true) => {
if (!query && name === 'condition') {
// Set default value.
const defaultQuery = DEFAULT_VALUES[topic][value];
onUpdateFilter(id, 'query', defaultQuery, toggleAllChecked);
}

onUpdateFilter(id, name, value, toggleAllChecked);
};
const DummySelect = () => (
<span className="margin-x-1"><select className="usa-select ttahub-dummy-select" disabled aria-label="select a topic and condition first and then select a query" /></span>
);

const onApplyQuery = (q) => {
onUpdate('query', q);
const onApplyQuery = (q, toggleAllChecked) => {
onUpdate('query', q, toggleAllChecked);
};

const updateSingleDate = (name, value) => {
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/components/filter/FilterMenu.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { v4 as uuidv4 } from 'uuid';
import DropdownMenu from '../DropdownMenu';
Expand All @@ -20,6 +20,11 @@ const filterProp = PropTypes.shape({
export default function FilterMenu({ filters, onApplyFilters }) {
const [items, setItems] = useState([...filters]);

useEffect(() => {
// If filters where changes outside of this component update.
setItems(filters);
}, [filters]);

const onApply = () => {
onApplyFilters(items.filter((item) => item.topic && item.condition && item.query));
};
Expand All @@ -37,7 +42,7 @@ export default function FilterMenu({ filters, onApplyFilters }) {
// reset state if we hit cancel
const onCancel = () => setItems([...filters]);

const onUpdateFilter = (id, name, value) => {
const onUpdateFilter = (id, name, value, toggleAllChecked) => {
const newItems = [...items];
const toUpdate = newItems.find((item) => item.id === id);
toUpdate[name] = value;
Expand All @@ -46,7 +51,7 @@ export default function FilterMenu({ filters, onApplyFilters }) {
toUpdate.condition = '';
toUpdate.query = '';
}

toUpdate.toggleAllChecked = toggleAllChecked;
setItems(newItems);
};
const onAddFilter = () => {
Expand All @@ -55,6 +60,7 @@ export default function FilterMenu({ filters, onApplyFilters }) {
id: uuidv4(),
display: '',
conditions: [],
toggleAllChecked: true,
};
newItems.push(newItem);
setItems(newItems);
Expand Down Expand Up @@ -83,7 +89,7 @@ export default function FilterMenu({ filters, onApplyFilters }) {
showCancel
onCancel={onCancel}
cancelAriaLabel="discard changes and close filter menu"
className="margin-bottom-2 ttahub-filter-menu"
className="ttahub-filter-menu"
menuName="filter menu"
canBlur={canBlur}
>
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/components/filter/FilterPills.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.filter-pill-container .smart-hub--ellipsis {
display: inline;
width: 290px;
font-size: 16px;
vertical-align: top;
}

.filter-pill-container .smart-hub--tooltip button {
display: inline;
line-height: 1.15;
}
Loading

0 comments on commit 53da9e7

Please sign in to comment.