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

[To Main] Implement UX designs for engagement wizard #2573

Merged
Merged
Show file tree
Hide file tree
Changes from 6 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
9 changes: 9 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## August 8, 2024

- **Feature** Implement UX designs for engagement creation wizard [🎟️ DESENG-663](https://citz-gdx.atlassian.net/browse/DESENG-663)
- Implemented the new design for the engagement creation wizard
- Added a new "Step" component to handle the wizard's steps
- Added a new "SystemMessage" component to display system messages
- Made engagement configuration form reusable for an edit engagement flow
- Added a new "EngagementWizard" component to handle the entire wizard

## August 6, 2024

- **Hotfix** Fix styling issues with the new admin header - 🎟️ HOTFIX
Expand Down
38 changes: 19 additions & 19 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ In the PR description field, be sure to include:
- Description of the changes (if you made any changes that weren't captured in the Jira ticket, be sure to list them here)
- The linked ticket for updating the user guide, if applicable. When functionality changes, we'll want to be sure to update the user guide for MET. Create a separate ticket for this and link it, if necessary. Otherwise, you can just write "N/A"

If a high volume of changes is to be submitted as a PR, we ask that the work be segmented into multiple PRs. A common threshold is that a PR should contain 20 changed files at most. Of course it also depends on the complexity of the changes but we ask that work be broken up into smaller logical chunks whenver possible.
If a high volume of changes is to be submitted as a PR, we ask that the work be segmented into multiple PRs. A common threshold is that a PR should contain 20 changed files at most. Of course it also depends on the complexity of the changes but we ask that work be broken up into smaller logical chunks whenver possible.

### Examples

Expand All @@ -44,7 +44,7 @@ One common technique used for segmenting PRs is to create a "parent" feature bra

With this method of segmentation, you don't need to worry about committing incomplete or broken features to main. When all child branches have been completed for the feature, the parent is then merged back into main in a final PR. This final review can contain your CHANGELOG.md entry. In the title or description of your PR, please indicate that all work has already been reviewed.

__Example__: You're tasked with adding a new page to the frontend which requires read access to the DB via a new API route. You create a feature branch off of main, then a child branch off of your feature branch. You then add the new API endpoint required. You submit a PR for this work by requesting to merge your child branch into your parent feature branch. Once merged, you create another child branch off your feature branch and add the new page. You submit this work as its own PR - merging into the parent feature branch again. All work is now complete so you finish things off by submitting a PR to merge your parent feature branch into the main branch. The work has already been approved in previous reviews. You can now choose to update the Changelog. If you need to add any additional, unreviewed work, you can make a note about it in the PR description.
**Example**: You're tasked with adding a new page to the frontend which requires read access to the DB via a new API route. You create a feature branch off of main, then a child branch off of your feature branch. You then add the new API endpoint required. You submit a PR for this work by requesting to merge your child branch into your parent feature branch. Once merged, you create another child branch off your feature branch and add the new page. You submit this work as its own PR - merging into the parent feature branch again. All work is now complete so you finish things off by submitting a PR to merge your parent feature branch into the main branch. The work has already been approved in previous reviews. You can now choose to update the Changelog. If you need to add any additional, unreviewed work, you can make a note about it in the PR description.

## Merge Techniques

Expand All @@ -55,12 +55,13 @@ Due to how PRs are commonly segmented in this repository, and due to its active
Conditions for whether to Approve or Request Changes for a PR can come with a lot of nuance. We try to err on the side of Approve unless there are obvious changes needed. Below is a list of reasons to "stop" a PR. Items with a star next to them are subject to interpretation by the review. Some items may prompt to discussion between the reviewer and the submitting developer so that choices can be justified.

Examples of when to Request Changes

- Logging functions are left in that aren't required or needed later (with exceptions for error logging, i.e. console.error, etc.)
- Commented-code is left in that isn't needed later
- Unsafe function calls like `eval()`
- Widespread incorrect syntax use (using snake case for many variables in JS when it's not required)
- Significant amount of repeition in code that can be condensed without much issue (code isn't DRY) *
- Unnecessary function calls, overly verbose code *
- Significant amount of repeition in code that can be condensed without much issue (code isn't DRY) \*
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh thanks for this!

- Unnecessary function calls, overly verbose code \*

## Style Guide

Expand All @@ -77,18 +78,17 @@ Examples of when to Request Changes
### MET Web

- Be sure to make use of shared components found under `src/components/common`. Below is a non-exhaustive list of common app components:
- `Button`: A versatile button with different style types that are complimentary of MET styling. Choose a particular button style with: `variant=<"primary"|"secondary"|"tertiary"|undefined>`
- `RichTextArea`: A WYSIWYG editor used app-wide. It will dynamically render out links and h2s as React components.
- `Header1`, `Header2`: MET-styled h1, h2 components.
- `ResponsiveContainer`: A container that decreases its padding on smaller screens.
- `ResponsiveWrapper`: A route wrapper that adds a responsive container around its child routes.
- `Pagination`: Provides a pagination UI - a wrapper around Material UI's pagination.
- `EngagementStatusChip`: A wrapper around MUI's Chip that's styled for MET.
- `FileUpload`: A file uploader component.
- `MetTable`, `Table`, `TableHead`, `TableCell`, etc.: Complete table components that are wrappers around MUI tables, styled for MET.
- `ConfirmModal`, `EmailModal`, `UpdateModal`: Common, reusable modal window types.
- `AutoBreadcrumbs`: Automatically generates breadcrumbs based on the `handle.crumb` function of the current route and its parents.
- `Link`: A wrapper around MUI's Link, styled for MET.
- `UnsavedWorkConfirmation`: Uses a route [blocker](https://reactrouter.com/en/main/hooks/use-blocker) to display a ConfirmModal so that the user doesn't lose any unsaved work on their current page.


- `Button`: A versatile button with different style types that are complimentary of MET styling. Choose a particular button style with: `variant=<"primary"|"secondary"|"tertiary"|undefined>`
- `RichTextArea`: A WYSIWYG editor used app-wide. It will dynamically render out links and h2s as React components.
- `Header1`, `Header2`: MET-styled h1, h2 components.
- `ResponsiveContainer`: A container that decreases its padding on smaller screens.
- `ResponsiveWrapper`: A route wrapper that adds a responsive container around its child routes.
- `Pagination`: Provides a pagination UI - a wrapper around Material UI's pagination.
- `EngagementStatusChip`: A wrapper around MUI's Chip that's styled for MET.
- `FileUpload`: A file uploader component.
- `MetTable`, `Table`, `TableHead`, `TableCell`, etc.: Complete table components that are wrappers around MUI tables, styled for MET.
- `ConfirmModal`, `EmailModal`, `UpdateModal`: Common, reusable modal window types.
- `AutoBreadcrumbs`: Automatically generates breadcrumbs based on the `handle.crumb` function of the current route and its parents.
- `Link`: A wrapper around MUI's Link, styled for MET.
- `UnsavedWorkConfirmation`: Uses a route [blocker](https://reactrouter.com/en/main/hooks/use-blocker) to display a ConfirmModal so that the user doesn't lose any unsaved work on their current page.
- `OutlineBox`: A styled box that displays a rounded outline around its children.
60 changes: 60 additions & 0 deletions met-web/src/components/common/Communication/StatusIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import { FontAwesomeIcon, FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
import {
faInfoCircle,
faExclamationTriangle,
faExclamationCircle,
faCheckCircle,
} from '@fortawesome/pro-solid-svg-icons';
import {
faInfoCircle as faInfoCircleRegular,
faExclamationTriangle as faExclamationTriangleRegular,
faExclamationCircle as faExclamationCircleRegular,
faCheckCircle as faCheckCircleRegular,
} from '@fortawesome/pro-regular-svg-icons';
import {
faCheckCircle as faCheckCircleLight,
faExclamationCircle as faExclamationCircleLight,
faExclamationTriangle as faExclamationTriangleLight,
faInfoCircle as faInfoCircleLight,
} from '@fortawesome/pro-light-svg-icons';

import { colors } from 'styles/Theme';

type IconWeight = 'solid' | 'regular' | 'light';

export const StatusIcon = ({
status,
color,
weight = 'solid',
...props
}: {
status: 'success' | 'warning' | 'error' | 'info';
color?: string;
weight?: IconWeight;
} & Partial<FontAwesomeIconProps>) => {
let iconMap = {
success: faCheckCircle,
warning: faExclamationTriangle,
error: faExclamationCircle,
info: faInfoCircle,
};
if (weight === 'regular') {
iconMap = {
success: faCheckCircleRegular,
warning: faExclamationTriangleRegular,
error: faExclamationCircleRegular,
info: faInfoCircleRegular,
};
}
if (weight === 'light') {
iconMap = {
success: faCheckCircleLight,
warning: faExclamationTriangleLight,
error: faExclamationCircleLight,
info: faInfoCircleLight,
};
}

return <FontAwesomeIcon icon={iconMap[status]} color={color ?? colors.notification[status].icon} {...props} />;
};
12 changes: 6 additions & 6 deletions met-web/src/components/common/Input/FormField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';

export type FormFieldProps = {
title: string;
title?: string;
disabled?: boolean;
instructions?: string;
required?: boolean;
Expand All @@ -26,20 +26,20 @@ export const FormField = ({
...gridProps
}: FormFieldProps) => {
return (
<label style={{ width: '100%' }}>
<label style={{ width: '100%', display: 'block' }}>
<Grid
className="met-input-form-field"
container
spacing={0}
direction="column"
{...gridProps}
sx={{ opacity: disabled ? '0.5' : '1' }}
sx={{ opacity: disabled ? '0.5' : '1', ...gridProps.sx }}
>
<Grid item xs={12}>
<BodyText bold size="large">
<BodyText bold size="large" className="met-input-form-field-title">
{title}
{required && <span title="(Required)">*</span>}
{optional && <span style={{ fontWeight: '400' }}> (Optional)</span>}
{required && title && <span title="(Required)">*</span>}
{optional && title && <span style={{ fontWeight: '400' }}> (Optional)</span>}
</BodyText>
</Grid>
<Grid item xs={12} sx={{ mb: '8px' }}>
Expand Down
4 changes: 4 additions & 0 deletions met-web/src/components/common/Input/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export type TextFieldProps = {
counter?: boolean;
maxLength?: number;
clearable?: boolean;
formFieldProps?: Partial<FormFieldProps>;
onChange?: (value: string, name?: string) => void;
} & Omit<FormFieldProps, 'children' | 'onChange'> &
Omit<TextInputProps, 'fullWidth' | 'error' | 'onChange'>;
Expand All @@ -140,6 +141,7 @@ export const TextField = ({
clearable,
onChange,
disabled,
formFieldProps,
...textInputProps
}: TextFieldProps) => {
const [value, setValue] = React.useState(textInputProps.value || '');
Expand All @@ -151,6 +153,7 @@ export const TextField = ({
const handleSetValue = (newValue: string) => {
if (onChange === undefined) return setValue(newValue);
onChange?.(newValue, name);
return setValue(newValue);
};

const isError = !!error;
Expand All @@ -164,6 +167,7 @@ export const TextField = ({
required={required}
optional={optional}
error={error}
{...formFieldProps}
>
<TextInput
fullWidth
Expand Down
106 changes: 106 additions & 0 deletions met-web/src/components/common/Layout/FormStep.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import React from 'react';
import { Box, Grid } from '@mui/material';
import {
faCircle1,
faCircle2,
faCircle3,
faCircle4,
faCircle5,
faCircle6,
faCircle7,
faCircle8,
faCircle9,
} from '@fortawesome/pro-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCircleCheck } from '@fortawesome/free-solid-svg-icons';
import { colors } from 'styles/Theme';
import { BodyText, Header2 } from '../Typography';

export const circleNumberIcons = [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does const this need to be exported?

faCircle1,
faCircle2,
faCircle3,
faCircle4,
faCircle5,
faCircle6,
faCircle7,
faCircle8,
faCircle9,
];

export const FormStep = ({
step,
completing,
completed,
question,
details,
children,
}: {
step: number;
completing?: boolean;
completed?: boolean;
question?: string;
details?: string;
children?: React.ReactNode;
}) => {
const [isFocused, setIsFocused] = React.useState(false);
const activityColor = completed || completing || isFocused ? colors.surface.blue[90] : colors.surface.gray[70];

return (
<Grid
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
container
direction="row"
justifyContent="flex-start"
alignItems="stretch"
spacing={1}
sx={{
padding: '1rem',
backgroundColor: 'white',
marginBottom: '1rem',
maxWidth: '100%',
}}
>
<Grid
item
container
alignItems="stretch"
direction="column"
gap={1}
sx={{ pt: 1.25, fontSize: '16px', width: '3rem' }}
>
<Grid item>
<FontAwesomeIcon
icon={completed ? faCircleCheck : circleNumberIcons[step - 1]}
color={activityColor}
size="2x"
/>
</Grid>
<Grid item xs>
<Box
sx={{
height: '100%',
width: '1rem',
borderRight: '1px solid',
borderColor: activityColor,
}}
/>
</Grid>
</Grid>
<Grid item container xs justifyContent="flex-start" alignItems="flex-start" pb="16px">
<Grid item xs={12}>
<Header2 sx={{ mt: 0, fontSize: '20px', fontWeight: '300' }}>{question}</Header2>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's nice that you've built this handy modular component for the "step" style fields! Can we modify this slightly so that nonsighted users get all the same nice instructions as sighted users? There are multiple ways this could be done but we should definitely use a label tag somewhere and make sure it's tied to the right form field.

This WCAG resource states we should ideally have a label tag and possibly an aria-label to concatenate lengthy instructions. https://www.w3.org/WAI/WCAG21/Understanding/labels-or-instructions.html

</Grid>
{details && (
<Grid item sx={{ marginTop: '-0.5rem', marginBottom: '1.5rem' }}>
<BodyText size="small">{details}</BodyText>
</Grid>
)}
<Grid item xs={12}>
{children}
</Grid>
</Grid>
</Grid>
);
};
68 changes: 68 additions & 0 deletions met-web/src/components/common/Layout/SystemMessage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import { Button, Grid, GridProps } from '@mui/material';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { StatusIcon } from '../Communication/StatusIcon';
import { colors } from 'styles/Theme';
import { faClose } from '@fortawesome/pro-regular-svg-icons';

export const SystemMessage = ({
status,
onDismiss,
color,
coloredBackground,
icon,
children,
...props
}: {
status: 'success' | 'warning' | 'error' | 'info';
onDismiss?: () => void;
color?: string;
coloredBackground?: boolean;
icon?: string;
} & GridProps) => {
return (
<Grid
container
direction="row"
justifyContent="flex-start"
alignItems="flex-start"
sx={{
maxWidth: { xs: '100%', md: '700px' },
borderRadius: '8px',
backgroundColor: coloredBackground ? colors.notification[status].tint : 'transparent',
color: 'type.primary',
padding: '0.8rem 1rem',
paddingLeft: { xs: '0.5rem', md: '1rem' },
border: `1px solid ${colors.notification[status].shade}`,
flexWrap: 'nowrap',
...props.sx,
}}
>
<Grid item sx={{ pr: 1, mt: -0.5, fontSize: '18px' }}>
<StatusIcon status={status} color={color} />
</Grid>
<Grid item sx={{ width: '100%', maxWidth: { xs: '100%', md: '600px', fontSize: '14px' } }}>
{children}
</Grid>
<Grid item justifySelf="flex-end" sx={{ mt: -0.75, opacity: onDismiss ? 1 : 0 }}>
<Button
disabled={!onDismiss}
onClick={onDismiss}
sx={{
padding: 0,
height: '100%',
width: '32px',
minWidth: '24px',
marginTop: '8px',
backgroundColor: 'transparent',
color: colors.notification[status].icon,
'&:hover': {
backgroundColor: 'transparent',
},
}}
startIcon={<FontAwesomeIcon icon={faClose} />}
></Button>
</Grid>
</Grid>
);
};
Loading
Loading