Skip to content

Commit

Permalink
Merge pull request #2573 from bcgov/feature/DESENG-663-engagement-aut…
Browse files Browse the repository at this point in the history
…horing-wizard-design-compliance

[To Main] Implement UX designs for engagement wizard
  • Loading branch information
NatSquared authored Aug 13, 2024
2 parents 9ace9ec + 71839a6 commit 8d2b133
Show file tree
Hide file tree
Showing 22 changed files with 1,202 additions and 579 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.MD
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@
- **Feature** New engagement details page [🎟️ DESENG-666](https://citz-gdx.atlassian.net/browse/DESENG-666)
- Old engagement details page still available under "old-view" route

- **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
41 changes: 22 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) \*
- Unnecessary function calls, overly verbose code \*

## Style Guide

Expand All @@ -77,18 +78,20 @@ 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.
- `StatusIcon`: A simple component that displays a status icon based on the status string passed to it.
- `FormStep`: A wrapper around a form component that accepts a completion criteria and displays the user's progress. Accepts a `step` prop that is a number (from 1 to 9) that represents the current step in the form. This will be rendered as an icon with a checkmark if the step is complete, and a number if it's the current step or if it's incomplete.
- `SystemMessage`: An informational message that can be displayed to the user. Accepts a `type` prop that can be "error", "warning", "info", or "success", which affects the display of the message.
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
6 changes: 4 additions & 2 deletions met-web/src/components/common/Input/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export const textInputStyles = {
};

export const TextInput: React.FC<TextInputProps> = ({
id,
value,
onChange,
placeholder,
Expand Down Expand Up @@ -77,7 +76,6 @@ export const TextInput: React.FC<TextInputProps> = ({
inputProps={{
error: error,
...inputProps,
'aria-describedby': id,
sx: {
fontSize: '16px',
lineHeight: '24px',
Expand Down Expand Up @@ -126,6 +124,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 +139,7 @@ export const TextField = ({
clearable,
onChange,
disabled,
formFieldProps,
...textInputProps
}: TextFieldProps) => {
const [value, setValue] = React.useState(textInputProps.value || '');
Expand All @@ -151,6 +151,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 +165,7 @@ export const TextField = ({
required={required}
optional={optional}
error={error}
{...formFieldProps}
>
<TextInput
fullWidth
Expand Down
130 changes: 130 additions & 0 deletions met-web/src/components/common/Layout/FormStep.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import React, { useId } 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';

const circleNumberIcons = [
faCircle1,
faCircle2,
faCircle3,
faCircle4,
faCircle5,
faCircle6,
faCircle7,
faCircle8,
faCircle9,
];

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

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"
component={isGroup ? 'fieldset' : 'div'}
aria-labelledby={titleId + ' ' + instructionsId}
sx={{ border: 'none' }}
>
<Grid item xs={12}>
<Header2 sx={{ mt: 0, fontSize: '20px', fontWeight: '300' }}>
<label htmlFor={isGroup ? undefined : labelFor} id={titleId}>
{question}
</label>
</Header2>
</Grid>
{details && (
<Grid item sx={{ marginTop: '-0.5rem', marginBottom: '1.5rem' }}>
<BodyText size="small">
<label htmlFor={isGroup ? undefined : labelFor} id={instructionsId}>
{details}
</label>
</BodyText>
</Grid>
)}
<Grid item xs={12}>
{children}
</Grid>
</Grid>
</Grid>
);
};
Loading

0 comments on commit 8d2b133

Please sign in to comment.