diff --git a/__tests__/components/uswds/Alert.test.js b/__tests__/components/uswds/Alert.test.js index 672187d2..0384f743 100644 --- a/__tests__/components/uswds/Alert.test.js +++ b/__tests__/components/uswds/Alert.test.js @@ -11,7 +11,7 @@ function selectAlert(rendered) { describe('', () => { it('renders a default USWDS styled alert', () => { - const res = render(); + const res = render(); const alert = selectAlert(res); expect(alert).toBeInTheDocument(); @@ -21,7 +21,9 @@ describe('', () => { for (const type of types) { it(`renders an alert of type ${type}`, () => { - const res = render(); + const res = render( + + ); const alert = selectAlert(res); expect(alert).toHaveClass(`usa-alert--${type}`); @@ -29,14 +31,21 @@ describe('', () => { } it('renders an h4 heading by default', () => { - render(); + render(); const heading = screen.getByRole('heading', { level: 4 }); expect(heading).toBeInTheDocument(); }); it('renders an h3 if specified', () => { - render(); + render( + + ); const heading = screen.getByRole('heading', { level: 3 }); expect(heading).toBeInTheDocument(); @@ -44,7 +53,11 @@ describe('', () => { describe('with children', () => { it('renders normally within a p tag', () => { - const res = render(Test contents); + const res = render( + + Test contents + + ); const alert = selectAlert(res); expect(alert).toHaveTextContent('Test contents'); @@ -53,7 +66,7 @@ describe('', () => { it('renders validation without a p tag', () => { const res = render( - + Test contents ); @@ -65,7 +78,7 @@ describe('', () => { }); it('can render a slim alert without an icon', () => { - const res = render(); + const res = render(); const alert = selectAlert(res); expect(alert).toHaveClass('usa-alert--slim'); diff --git a/src/app/orgs/[orgId]/users/[userId]/org-roles/error.tsx b/src/app/orgs/[orgId]/users/[userId]/org-roles/error.tsx index 49a9ed1e..3f287346 100644 --- a/src/app/orgs/[orgId]/users/[userId]/org-roles/error.tsx +++ b/src/app/orgs/[orgId]/users/[userId]/org-roles/error.tsx @@ -3,5 +3,9 @@ import { Alert } from '@/components/uswds/Alert'; export default function EditOrgPageError({ error }: { error: Error }) { - return {error.message}; + return ( + + {error.message} + + ); } diff --git a/src/app/orgs/error.tsx b/src/app/orgs/error.tsx index 596af33a..795ff77d 100644 --- a/src/app/orgs/error.tsx +++ b/src/app/orgs/error.tsx @@ -17,7 +17,9 @@ export default function Error({ return (
- Error: {error.message} + + Error: {error.message} +
); } diff --git a/src/app/prototype/alerts/page.tsx b/src/app/prototype/alerts/page.tsx new file mode 100644 index 00000000..55b947ad --- /dev/null +++ b/src/app/prototype/alerts/page.tsx @@ -0,0 +1,51 @@ +'use client'; + +import { Alert } from '@/components/uswds/Alert'; +import { useState } from 'react'; + +export default function AlertsPage() { + const [showSuccess, setShowSuccess] = useState(false); + const [successCount, setSuccessCount] = useState(0); + const [showError, setShowError] = useState(false); + const [errorCount, setErrorCount] = useState(0); + + return ( +
+

Success alert

+ + + +
+ + This is success alert number {successCount} + +
+ +

Error alert

+ + + +
+ + This is an error alert number {errorCount} + +
+
+ ); +} diff --git a/src/app/prototype/design-guide/page.tsx b/src/app/prototype/design-guide/page.tsx index 656785ce..4102a12d 100644 --- a/src/app/prototype/design-guide/page.tsx +++ b/src/app/prototype/design-guide/page.tsx @@ -84,22 +84,43 @@ export default function DesignGuidePage() {

Full sized with icons and default h4 heading size

- + Good job - + So you know - + Watch out - + Something is wrong Fear! Fire! Foes! @@ -110,42 +131,88 @@ export default function DesignGuidePage() { heading="Validation" validation className="display-block" + isVisible={true} > Validations do not use usa-alert__text p tag

Slim with icons and no headings

- + Success - + Info - + Warning - + Error - + Emergency

Slim with no icons and no headings

- + Success - + Info - + Warning - + Error - + Emergency
diff --git a/src/components/OrgActions/OrgActionsAddUser.tsx b/src/components/OrgActions/OrgActionsAddUser.tsx index 990cf863..e6f1e566 100644 --- a/src/components/OrgActions/OrgActionsAddUser.tsx +++ b/src/components/OrgActions/OrgActionsAddUser.tsx @@ -78,22 +78,21 @@ export function OrgActionsAddUser({ able to manage their roles once they're added.

- {actionStatus === 'error' && ( - {actionErrors.join(', ')} - )} - {actionStatus === 'success' && ( - - {emailInput || 'User'} has been added! - {userId && ( - <> -
- - Manage their organization roles - - - )} -
- )} + + {actionErrors.join(', ')} + + + + {emailInput || 'User'} has been added! + {userId && ( + <> +
+ + Manage their organization roles + + + )} +
diff --git a/src/components/UsersActions/UsersActionsOrgRoles.tsx b/src/components/UsersActions/UsersActionsOrgRoles.tsx index 7acaa162..9b42f9d3 100644 --- a/src/components/UsersActions/UsersActionsOrgRoles.tsx +++ b/src/components/UsersActions/UsersActionsOrgRoles.tsx @@ -144,7 +144,11 @@ export function UsersActionsOrgRoles({ } if (loadingErrorMessage) { - return {loadingErrorMessage}; + return ( + + {loadingErrorMessage} + + ); } if (!dataLoaded) { @@ -162,21 +166,26 @@ export function UsersActionsOrgRoles({ > {actionErrors.join(', ')}
- {actionStatus === 'success' && ( - Org roles have been saved! - )} - {actionStatus === 'error' && ( - - {actionErrors.join(', ')} If the error occurs again, please contact{' '} - - Cloud.gov support - - . - - )} + + + Org roles have been saved! + + + + {actionErrors.join(', ')} If the error occurs again, please contact{' '} + + Cloud.gov support + + . + +
@@ -226,7 +235,11 @@ export function UsersActionsOrgRoles({ )} - {actionStatus === 'pending' &&

submission in progress...

} + {actionStatus === 'pending' && ( +
+

submission in progress...

+
+ )}
diff --git a/src/components/UsersActions/UsersActionsRemoveFromOrg.tsx b/src/components/UsersActions/UsersActionsRemoveFromOrg.tsx index 672186c9..dedc2de7 100644 --- a/src/components/UsersActions/UsersActionsRemoveFromOrg.tsx +++ b/src/components/UsersActions/UsersActionsRemoveFromOrg.tsx @@ -56,19 +56,6 @@ function FormDefault({ ); } -function FormSuccess({ user }: { user: UserObj }) { - return ( - - {user.username} has been successfully removed from the - org. - - ); -} - -function FormError({ errors }: { errors: string[] }) { - return {errors.join(', ')}; -} - export function UsersActionsRemoveFromOrg({ user, roles, @@ -126,7 +113,10 @@ export function UsersActionsRemoveFromOrg({ modalId={`modal-remove-from-org-user-${user.guid}`} headingId={modalHeadingId(user)} > - {actionStatus === 'error' && } + + {actionErrors.join(', ')} + + {actionStatus !== 'success' && ( )} - {actionStatus === 'success' && } + + + {user.username} has been successfully removed from + the org. + )} diff --git a/src/components/UsersActions/UsersActionsSpaceRoles/UsersActionsSpaceRoles.tsx b/src/components/UsersActions/UsersActionsSpaceRoles/UsersActionsSpaceRoles.tsx index 226ad817..7227d7f9 100644 --- a/src/components/UsersActions/UsersActionsSpaceRoles/UsersActionsSpaceRoles.tsx +++ b/src/components/UsersActions/UsersActionsSpaceRoles/UsersActionsSpaceRoles.tsx @@ -99,7 +99,11 @@ export function UsersActionsSpaceRoles({ }; if (loadingErrorMessage) { - return {loadingErrorMessage}; + return ( + + {loadingErrorMessage} + + ); } if (!dataLoaded) { @@ -115,21 +119,26 @@ export function UsersActionsSpaceRoles({

- {actionStatus === 'success' && ( - Changes saved! - )} - {actionStatus === 'error' && ( - - {actionErrors.join(', ')} If the error occurs again, please contact{' '} - - Cloud.gov support - - . - - )} + + + Changes saved! + + + + {actionErrors.join(', ')} If the error occurs again, please contact{' '} + + Cloud.gov support + + . + +
{spaces.map((space: any) => ( @@ -169,7 +178,11 @@ export function UsersActionsSpaceRoles({ )}
- {actionStatus === 'pending' &&

Submission in progress...

} + {actionStatus === 'pending' && ( +
+

Submission in progress...

+
+ )} ); } diff --git a/src/components/UsersList/UsersList.tsx b/src/components/UsersList/UsersList.tsx index f687a42c..e851b2a9 100644 --- a/src/components/UsersList/UsersList.tsx +++ b/src/components/UsersList/UsersList.tsx @@ -162,13 +162,6 @@ export function UsersList({ return ( <> - {/* - aria-live region needs to show up on initial page render. - More info: https://tetralogical.com/blog/2024/05/01/why-are-my-live-regions-not-working/ - */} -
- {successMsg} -
- {successMsg && ( - + {successMsg}{' '} + - - )} + (Dismiss this message.) + +
- + {removedUsername} has successfully been removed. diff --git a/src/components/uswds/Alert.tsx b/src/components/uswds/Alert.tsx index 0ac4acbe..608fd086 100644 --- a/src/components/uswds/Alert.tsx +++ b/src/components/uswds/Alert.tsx @@ -1,10 +1,17 @@ -// Copied largely from -// https://github.com/trussworks/react-uswds/ Alert component +/*** + * Because Alert components are aria-live regions, + * Alerts should always be present on page load + * and show/hide themselves using the isVisible prop: + * Examples: + * Good: ... + * Bad: { status === 'success' && ... } + ***/ import React from 'react'; import classnames from 'classnames'; type AlertProps = { + isVisible: boolean; type: 'emergency' | 'error' | 'info' | 'success' | 'warning'; heading?: React.ReactNode; headingLevel?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; @@ -19,6 +26,7 @@ export function Alert({ className, heading, headingLevel = 'h4', + isVisible = false, noIcon, slim, type, @@ -51,25 +59,26 @@ export function Alert({ } return ( -
-
- {heading && ( - - {heading} - - )} - {children && - (validation ? ( - children - ) : ( -

{children}

- ))} -
+
+ {isVisible && ( +
+
+ {heading && ( + + {heading} + + )} + {children && + (validation ? ( + children + ) : ( + // extra padding-left is needed here due to a bug with the $theme-site-margins-width setting +

{children}

+ ))} +
+
+ )} + {!isVisible && ''}
); }