Skip to content

Commit

Permalink
Merge branch 'ver-11.20.1' into production
Browse files Browse the repository at this point in the history
  • Loading branch information
tmy1313 committed Sep 7, 2021
2 parents f96a620 + 2dfb6c2 commit 5f3941f
Show file tree
Hide file tree
Showing 25 changed files with 366 additions and 84 deletions.
17 changes: 17 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
# Release Notes

## Version 11.20.1
_7 September 2021_

### Technical Update
* Update Quarterly Surveillance Reporting to use non-deprecated fields

---

## Version 11.20.0
_31 August 2021_

### Features
* Track Analytics separately on Cures vs. non-Cures criteria
* Require confirmation of cancel and delete in certain circumstances

---

## Version 11.19.0
_23 August 2021_

Expand Down
1 change: 1 addition & 0 deletions e2e/components/surveillance/complaints/complaints.po.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class ComplaintsComponent {
this.viewComplaint(id);
$('//*[text()="Edit"]/parent::button').scrollAndClick();
$('//span[text()="Delete"]/parent::button').scrollAndClick();
$('//span[text()="Yes"]/parent::button').scrollAndClick();
}
}

Expand Down
2 changes: 1 addition & 1 deletion e2e/pages/organizations/developers/products.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ describe('the Product part of the Developers page', () => {
expect(page.getMergeButton(product)).not.toExist();
});

it.skip('should show correct error message when spliting product with listings owned by different ACBs', () => {
it('should show correct error message when spliting product with listings owned by different ACBs', () => {
const newName = `${name} - split - ${(new Date()).getTime()}`;
const newCode = newName.substring(newName.length - 4);
const movingVersionId = '2039';
Expand Down
2 changes: 1 addition & 1 deletion e2e/pages/organizations/developers/versions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ describe('the Version part of the Developers page', () => {
page.editVersionCode.setValue(newCode);
page.moveListing(movingListingId);
actionBar.save();
expect(actionBar.errorMessages.getText()).toEqual('Access is denied to update listing CHP-025300 because it is owned by ICSA Labs.');
expect(actionBar.errorMessages.getText()).toEqual('Version split involves multiple ONC-ACBs, which requires additional approval. Please contact ONC.');
});
});
});
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "chpl",
"version": "11.19.0",
"version": "11.20.1",
"license": "SEE LICENSE IN LICENSE",
"repository": "chpladmin/chpl-website",
"scripts": {
Expand Down
84 changes: 84 additions & 0 deletions src/app/components/action-bar/action-bar-confirmation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react';
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
Divider,
Slide,
makeStyles,
} from '@material-ui/core';
import {
func, string,
} from 'prop-types';

import CheckIcon from '@material-ui/icons/Check';
import CloseIcon from '@material-ui/icons/Close';

const Transition = React.forwardRef((props, ref) => <Slide direction="up" ref={ref} {...props} />);

const useStyles = makeStyles({
dialogTitle: {
fontWeight: 800,
fontSize: '1.5em',
},
dialogContent: {
color: '#000000',
},
dialogActions: {
justifyContent: 'flex-start',
},
iconSpacing: {
marginLeft: '4px',
},
});

function ChplActionBarConfirmation(props) {
const classes = useStyles();

const act = (action) => {
props.dispatch(action);
};

return (
<Dialog
open
onClose={() => act('no')}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
TransitionComponent={Transition}
>
<DialogTitle id="alert-dialog-title" className={classes.dialogTitle}>
Confirm
</DialogTitle>
<Divider />
<DialogContent>
<DialogContentText id="alert-dialog-description" className={classes.dialogContent}>
{ props.pendingMessage }
</DialogContentText>
</DialogContent>
<Divider />
<DialogActions className={classes.dialogActions}>
<Button onClick={() => act('yes')} color="primary" variant="contained" autoFocus>
Yes
{' '}
<CheckIcon className={classes.iconSpacing} />
</Button>
<Button onClick={() => act('no')} color="default" variant="contained">
No
{' '}
<CloseIcon className={classes.iconSpacing} />
</Button>
</DialogActions>
</Dialog>
);
}

export default ChplActionBarConfirmation;

ChplActionBarConfirmation.propTypes = {
dispatch: func.isRequired,
pendingMessage: string.isRequired,
};
47 changes: 47 additions & 0 deletions src/app/components/action-bar/action-bar-confirmation.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react';
import {
cleanup, render, screen, waitFor,
} from '@testing-library/react';
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';

import ChplActionBarConfirmation from './action-bar-confirmation';

const hocMock = {
dispatch: jest.fn(),
};

describe('the ChplActionBarConfirmation component', () => {
beforeEach(async () => {
render(
<ChplActionBarConfirmation
dispatch={hocMock.dispatch}
pendingMessage="This is your message"
/>,
);
});

afterEach(() => {
cleanup();
});

describe('when taking actions', () => {
it('should call the callback for "No"', async () => {
hocMock.dispatch.mockClear();
userEvent.click(screen.getByRole('button', { name: /No/i }));

await waitFor(() => {
expect(hocMock.dispatch).toHaveBeenCalledWith('no');
});
});

it('should call the callback for "Yes"', async () => {
hocMock.dispatch.mockClear();
userEvent.click(screen.getByRole('button', { name: /Yes/i }));

await waitFor(() => {
expect(hocMock.dispatch).toHaveBeenCalledWith('yes');
});
});
});
});
35 changes: 33 additions & 2 deletions src/app/components/action-bar/action-bar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import CloseOutlinedIcon from '@material-ui/icons/CloseOutlined';
import DeleteOutlinedIcon from '@material-ui/icons/DeleteOutlined';
import SaveIcon from '@material-ui/icons/Save';

import ChplActionBarConfirmation from './action-bar-confirmation';
import theme from '../../themes/theme';

const useStyles = makeStyles(() => ({
Expand All @@ -32,7 +33,10 @@ const useStyles = makeStyles(() => ({

function ChplActionBar(props) {
/* eslint-disable react/destructuring-assignment */
const [pendingAction, setPendingAction] = useState('');
const [pendingMessage, setPendingMessage] = useState('');
const [canDelete] = useState(props.canDelete);
const [isConfirming, setIsConfirming] = useState(false);
const [isDisabled] = useState(props.isDisabled);
const errors = props.errors.sort((a, b) => (a < b ? 1 : -1));
const warnings = props.warnings.sort((a, b) => (a < b ? 1 : -1));
Expand All @@ -46,9 +50,36 @@ function ChplActionBar(props) {
}
};

const confirmCancel = () => {
setIsConfirming(true);
setPendingAction('cancel');
setPendingMessage('Are you sure you want to cancel?');
};

const confirmDelete = () => {
setIsConfirming(true);
setPendingAction('delete');
setPendingMessage('Are you sure you want to delete this?');
};

const handleConfirmation = (response) => {
if (response === 'yes' && pendingAction) {
act(pendingAction);
}
setIsConfirming(false);
setPendingAction('');
};

return (
<ThemeProvider theme={theme}>
<div className="action-bar">
{ isConfirming
&& (
<ChplActionBarConfirmation
dispatch={handleConfirmation}
pendingMessage={pendingMessage}
/>
)}
{ ((errors && errors.length > 0) || (warnings && warnings.length > 0))
&& (
<>
Expand Down Expand Up @@ -128,7 +159,7 @@ function ChplActionBar(props) {
<Button
id="action-bar-cancel"
variant="outlined"
onClick={() => act('cancel')}
onClick={() => confirmCancel()}
className={classes.buttons}
>
Cancel
Expand All @@ -155,7 +186,7 @@ function ChplActionBar(props) {
id="action-bar-delete"
variant="contained"
className={`${classes.buttons} ${classes.deleteButton}`}
onClick={() => act('delete')}
onClick={() => confirmDelete()}
>
Delete
<DeleteOutlinedIcon
Expand Down
1 change: 1 addition & 0 deletions src/app/components/action-bar/action-bar.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ describe('the ChplActionBar component', () => {
it('should call the callback for cancel', async () => {
hocMock.dispatch.mockClear();
userEvent.click(screen.getByRole('button', { name: /Cancel/i }));
userEvent.click(screen.getByRole('button', { name: /Yes/i }));

await waitFor(() => {
expect(hocMock.dispatch).toHaveBeenCalledWith('cancel');
Expand Down
12 changes: 7 additions & 5 deletions src/app/components/listing/details/criteria/criterion.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
func,
} from 'prop-types';
import CloudDoneIcon from '@material-ui/icons/CloudDone';
import DoneAllIcon from '@material-ui/icons/DoneAll';
import CheckIcon from '@material-ui/icons/Check';
import EditOutlinedIcon from '@material-ui/icons/EditOutlined';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import SyncIcon from '@material-ui/icons/Sync';
Expand Down Expand Up @@ -74,12 +74,14 @@ function ChplCriterion(props) {
const [qmsStandards] = useState(props.qmsStandards);
const [accessibilityStandards] = useState(props.accessibilityStandards);
const $analytics = getAngularService('$analytics');
const utilService = getAngularService('utilService');
const classes = useStyles();
/* eslint-enable react/destructuring-assignment */

const handleAccordionChange = (event, isExpanded) => {
if (!isExpanded) {
$analytics.eventTrack('Viewed criteria details', { category: 'Listing Details', label: criterion.criterion.number });
if (isExpanded) {
const label = criterion.criterion.number + (utilService.isCures(criterion.criterion) ? ' (Cures Update)' : '');
$analytics.eventTrack('Viewed criteria details', { category: 'Listing Details', label });
}
};

Expand All @@ -106,7 +108,7 @@ function ChplCriterion(props) {
<Accordion
disabled={!criterion.success && !(criterion.g1Success !== null || criterion.g2Success !== null) && !canEdit}
className={classes.criterionAccordion}
onChange={() => handleAccordionChange()}
onChange={handleAccordionChange}
id={`criterion-id-${criterion.criterion.id}`}
>
<AccordionSummary
Expand All @@ -118,7 +120,7 @@ function ChplCriterion(props) {
<Grid item xs={1}>
{ criterion.success
&& (
<DoneAllIcon fontSize="large" aria-label={`Listing attests to criterion ${criterion.number}`} />
<CheckIcon fontSize="large" aria-label={`Listing attests to criterion ${criterion.number}`} />
)}
</Grid>
<Grid item xs={3}>
Expand Down
75 changes: 75 additions & 0 deletions src/app/components/listing/details/criteria/criterion.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react';
import { when } from 'jest-when';
import {
cleanup, render, screen, waitFor,
} from '@testing-library/react';
import '@testing-library/jest-dom';
import userEvent from '@testing-library/user-event';

import * as angularReactHelper from '../../../../services/angular-react-helper';
import ChplCriterion from './criterion';

const $analyticsMock = {
eventTrack: jest.fn(),
};
const utilServiceMock = {
isCures: jest.fn(),
};
angularReactHelper.getAngularService = jest.fn();
when(angularReactHelper.getAngularService).calledWith('$analytics').mockReturnValue($analyticsMock);
when(angularReactHelper.getAngularService).calledWith('utilService').mockReturnValue(utilServiceMock);

jest.mock('./criterion-details-view', () => ({
__esModule: true,
default: jest.fn(() => 42),
}));

describe('the ChplCriterion component', () => {
afterEach(() => {
cleanup();
});

describe('when opening the accordion', () => {
beforeEach(async () => {
render(
<ChplCriterion
accessibilityStandards={[]}
certificationResult={{
success: true,
criterion: {
number: '170.315 (z)(1)',
title: 'Criterion Title',
},
}}
qmsStandards={[]}
/>,
);
});

it('should track analytics', async () => {
$analyticsMock.eventTrack.mockClear();
utilServiceMock.isCures.mockReturnValueOnce(false);
userEvent.click(screen.getByText('170.315 (z)(1)'));

await waitFor(() => {
expect($analyticsMock.eventTrack).toHaveBeenCalledWith(
'Viewed criteria details',
{ category: 'Listing Details', label: '170.315 (z)(1)' },
);
});
});

it('should track analytics for cures criteria', async () => {
$analyticsMock.eventTrack.mockClear();
utilServiceMock.isCures.mockReturnValueOnce(true);
userEvent.click(screen.getByText('170.315 (z)(1)'));

await waitFor(() => {
expect($analyticsMock.eventTrack).toHaveBeenCalledWith(
'Viewed criteria details',
{ category: 'Listing Details', label: '170.315 (z)(1) (Cures Update)' },
);
});
});
});
});
Loading

0 comments on commit 5f3941f

Please sign in to comment.