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
+
+
{
+ setShowSuccess(!showSuccess);
+ setSuccessCount(showSuccess ? successCount + 1 : successCount);
+ }}
+ >
+ show success alert
+
+
+
+
+ This is success alert number {successCount}
+
+
+
+
Error alert
+
+
{
+ setShowError(!showError);
+ setErrorCount(showError ? errorCount + 1 : errorCount);
+ }}
+ >
+ show 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
+
+ .
+
+
>
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}{' '}
+ dismissSuccessMsg()}
+ className="usa-button--unstyled text-bold text-ink"
>
- {successMsg}{' '}
- dismissSuccessMsg()}
- className="usa-button--unstyled text-bold text-ink"
- >
- (Dismiss this message.)
-
-
- )}
+ (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 && ''}
);
}