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

feat: Add UI for Managing Required Reports for Phases #1205

Open
wants to merge 15 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
83 changes: 59 additions & 24 deletions client/components/ManageTestQueue/index.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useState } from 'react';
import styled from '@emotion/styled';
import PropTypes from 'prop-types';
import { LoadingStatus, useTriggerLoad } from '../common/LoadingStatus';
import DisclosureComponent from '../common/DisclosureComponent';
import ManageAtVersions from '@components/ManageTestQueue/ManageAtVersions';
import AddTestPlans from '@components/ManageTestQueue/AddTestPlans';
import { AtPropType, TestPlanVersionPropType } from '../common/proptypes';
import ManageRequiredReportsDisclosure from '../common/ManageRequiredReportsDisclosure';
import styled from '@emotion/styled';

export const DisclosureContainer = styled.div`
// Following directives are related to the ManageTestQueue component
Expand Down Expand Up @@ -91,7 +92,6 @@ export const DisclosureContainer = styled.div`
flex-wrap: wrap;
column-gap: 1rem;
row-gap: 0.75rem;

select {
width: inherit;
@media (max-width: 767px) {
Expand All @@ -100,6 +100,14 @@ export const DisclosureContainer = styled.div`
}
}

.disclosure-row-controls {
display: grid;
grid-auto-flow: column;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 1rem;
align-items: end;
}

.disclosure-form-label {
font-weight: bold;
font-size: 1rem;
Expand All @@ -109,39 +117,64 @@ export const DisclosureContainer = styled.div`
const ManageTestQueue = ({
ats = [],
testPlanVersions = [],
triggerUpdate = () => {}
triggerUpdate = () => {},
browsers = [],
includeManageRequiredReports = false
}) => {
const { loadingMessage } = useTriggerLoad();

const [showManageATs, setShowManageATs] = useState(false);
const [showAddTestPlans, setShowAddTestPlans] = useState(false);
const [showManageReqReports, setShowManageReqReports] = useState(false);

const onManageAtsClick = () => setShowManageATs(!showManageATs);
const onAddTestPlansClick = () => setShowAddTestPlans(!showAddTestPlans);
const onManageReqReportsClick = () =>
setShowManageReqReports(!showManageReqReports);

const titles = [
'Manage Assistive Technology Versions',
'Add Test Plans to the Test Queue'
];

const disclosureViews = [
<ManageAtVersions
key="ManageAtVersions"
ats={ats}
triggerUpdate={triggerUpdate}
/>,
<AddTestPlans
key="AddTestPlans"
ats={ats}
testPlanVersions={testPlanVersions}
triggerUpdate={triggerUpdate}
/>
];

const onClickHandlers = [onManageAtsClick, onAddTestPlansClick];
const expandedStates = [showManageATs, showAddTestPlans];

if (includeManageRequiredReports) {
titles.push('Manage Required Reports');
disclosureViews.push(
<ManageRequiredReportsDisclosure
key="ManageRequiredReportsDisclosure"
ats={ats}
triggerUpdate={triggerUpdate}
browsers={browsers}
/>
);
onClickHandlers.push(onManageReqReportsClick);
expandedStates.push(showManageReqReports);
}

return (
<LoadingStatus message={loadingMessage}>
<DisclosureComponent
componentId="manage-test-queue"
title={[
'Manage Assistive Technology Versions',
'Add Test Plans to the Test Queue'
]}
disclosureContainerView={[
<ManageAtVersions
key="ManageAtVersions"
ats={ats}
triggerUpdate={triggerUpdate}
/>,
<AddTestPlans
key="AddTestPlans"
ats={ats}
testPlanVersions={testPlanVersions}
triggerUpdate={triggerUpdate}
/>
]}
onClick={[onManageAtsClick, onAddTestPlansClick]}
expanded={[showManageATs, showAddTestPlans]}
title={titles}
disclosureContainerView={disclosureViews}
onClick={onClickHandlers}
expanded={expandedStates}
stacked
/>
</LoadingStatus>
Expand All @@ -151,7 +184,9 @@ const ManageTestQueue = ({
ManageTestQueue.propTypes = {
ats: PropTypes.arrayOf(AtPropType).isRequired,
testPlanVersions: PropTypes.arrayOf(TestPlanVersionPropType),
triggerUpdate: PropTypes.func
triggerUpdate: PropTypes.func,
browsers: PropTypes.array,
includeManageRequiredReports: PropTypes.bool
};

export default ManageTestQueue;
50 changes: 50 additions & 0 deletions client/components/ManageTestQueue/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,53 @@ export const DELETE_AT_VERSION_MUTATION = gql`
}
}
`;

export const CREATE_REQUIRED_REPORT_MUTATION = gql`
mutation CreateRequiredReport(
$atId: ID!
$browserId: ID!
$phase: TestPlanVersionPhase!
) {
requiredReport(atId: $atId, browserId: $browserId, phase: $phase) {
createRequiredReport {
atId
browserId
phase
}
}
}
`;

export const UPDATE_REQUIRED_REPORT_MUTATION = gql`
mutation UpdateRequiredReport(
$atId: ID!
$browserId: ID!
$phase: TestPlanVersionPhase!
$updateAtId: ID!
$updateBrowserId: ID!
) {
requiredReport(atId: $atId, browserId: $browserId, phase: $phase) {
updateRequiredReport(atId: $updateAtId, browserId: $updateBrowserId) {
atId
browserId
phase
}
}
}
`;

export const DELETE_REQUIRED_REPORT_MUTATION = gql`
mutation DeleteRequiredReport(
$atId: ID!
$browserId: ID!
$phase: TestPlanVersionPhase!
) {
requiredReport(atId: $atId, browserId: $browserId, phase: $phase) {
deleteRequiredReport {
atId
browserId
phase
}
}
}
`;
2 changes: 2 additions & 0 deletions client/components/TestQueue/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,8 @@ const TestQueue = () => {
ats={data.ats}
testPlanVersions={testPlanVersions}
triggerUpdate={refetch}
includeManageRequiredReports={true}
browsers={data.browsers}
/>
)}

Expand Down
9 changes: 9 additions & 0 deletions client/components/TestQueue/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ export const TEST_QUEUE_PAGE_QUERY = gql`
browsers {
...BrowserFields
}
candidateBrowsers {
...BrowserFields
}
recommendedBrowsers {
...BrowserFields
}
}
browsers {
...BrowserFields
}
testPlans(testPlanVersionPhases: [DRAFT, CANDIDATE, RECOMMENDED]) {
...TestPlanFields
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Button, Form } from 'react-bootstrap';
import styled from '@emotion/styled';
import { AtPropType } from '../proptypes';

const StyledSelect = styled(Form.Select)`
appearance: none;
background-image: url('data:image/svg+xml,%3csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 16 16%27%3e%3cpath fill=%27none%27 stroke=%27%23343a40%27 stroke-linecap=%27round%27 stroke-linejoin=%27round%27 stroke-width=%272%27 d=%27m2 5 6 6 6-6%27/%3e%3c/svg%3e');
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 16px 12px;
padding: 0.375rem 2.25rem 0.375rem 0.75rem;

${props =>
props.value &&
`
background-color: ${props.value === 'Candidate' ? '#ff6c00' : '#8441de'};
color: white;
border-radius: 1.5rem;
padding-left: 1rem;
`}
`;

const FormGroup = ({ label, children }) => (
<Form.Group className="form-group">
<Form.Label className="disclosure-form-label">{label}</Form.Label>
{children}
</Form.Group>
);

export const CreateRequiredReportForm = ({ ats, handleCreate }) => {
const [formState, setFormState] = useState({
phase: '',
at: '',
browser: ''
});

const handleInputChange = (field, value) => {
setFormState(prev => ({ ...prev, [field]: value }));
};

const handleSubmit = async () => {
await handleCreate({
atId: formState.at,
browserId: formState.browser,
phase: formState.phase.toUpperCase()
});
setFormState({ phase: '', at: '', browser: '' });
};

return (
<div className="disclosure-row-controls">
<FormGroup label="Phase">
<StyledSelect
value={formState.phase}
onChange={e => handleInputChange('phase', e.target.value)}
required
>
<option value="" disabled>
Select a Phase
</option>
{['Candidate', 'Recommended'].map(phase => (
<option key={phase} value={phase}>
{phase}
</option>
))}
</StyledSelect>
</FormGroup>
<FormGroup label="Assistive Technology">
<Form.Select
value={formState.at}
onChange={e => handleInputChange('at', e.target.value)}
required
>
<option value="" disabled>
Select an Assistive Technology
</option>
{ats.map(item => (
<option key={item.id} value={item.id}>
{item.name}
</option>
))}
</Form.Select>
</FormGroup>
<FormGroup label="Browser">
<Form.Select
value={formState.browser}
onChange={e => handleInputChange('browser', e.target.value)}
required
>
<option value="" disabled>
Select a Browser
</option>
{ats
.find(at => at.id === formState.at)
?.browsers.map(item => (
<option key={`${item.name}-${item.id}`} value={item.id}>
{item.name}
</option>
))}
</Form.Select>
</FormGroup>
<FormGroup>
<Button
disabled={!formState.phase || !formState.at || !formState.browser}
onClick={handleSubmit}
>
Add Required Reports
</Button>
</FormGroup>
</div>
);
};

FormGroup.propTypes = {
label: PropTypes.string,
children: PropTypes.node.isRequired
};

CreateRequiredReportForm.propTypes = {
ats: PropTypes.arrayOf(AtPropType).isRequired,
handleCreate: PropTypes.func.isRequired
};

CreateRequiredReportForm.propTypes = {};
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import BasicModal from '../BasicModal';
import PhasePill from '../PhasePill';
import PropTypes from 'prop-types';
import { AtPropType, BrowserPropType } from '../proptypes';

const DeleteRequiredReportModal = ({
at,
browser,
phase,
handleClose,
handleDeleteReqReport
}) => {
const handleDelete = () => {
handleDeleteReqReport({ atId: at.id, browserId: browser.id, phase });
handleClose();
};

return (
<BasicModal
show={true}
closeButton={false}
cancelButton={true}
headerSep={true}
title={
<p>
Delete {`${at.name} and ${browser.name} pair for `}
<PhasePill fullWidth={false} forHeader={true}>
{phase}
</PhasePill>
{' required reports'}
</p>
}
dialogClassName="modal-50w"
actions={[
{
label: 'Confirm Delete',
onClick: handleDelete
}
]}
handleClose={handleClose}
staticBackdrop={true}
/>
);
};

DeleteRequiredReportModal.propTypes = {
at: AtPropType.isRequired,
browser: BrowserPropType.isRequired,
phase: PropTypes.string.isRequired,
handleClose: PropTypes.func.isRequired,
handleDeleteReqReport: PropTypes.func.isRequired
};

export default DeleteRequiredReportModal;
Loading
Loading