Skip to content

Commit

Permalink
feat: Add autoFocus property to alert
Browse files Browse the repository at this point in the history
  • Loading branch information
timogasda committed Jul 28, 2023
1 parent 725901c commit 8a35f5d
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 9 deletions.
3 changes: 3 additions & 0 deletions pages/alert/simple.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import React, { useState } from 'react';
import Alert from '~components/alert';
import Button from '~components/button';
import Link from '~components/link';
import ScreenshotArea from '../utils/screenshot-area';
import SpaceBetween from '~components/space-between';
Expand All @@ -16,6 +17,7 @@ export default function AlertScenario() {
<I18nProvider messages={[messages]} locale="en">
<article>
<h1>Simple alert</h1>
<Button onClick={() => setVisible(!visible)}>Toggle visibility</Button>
<ScreenshotArea>
<SpaceBetween size="s">
<div className={styles['alert-container']}>
Expand All @@ -27,6 +29,7 @@ export default function AlertScenario() {
buttonText="Button text"
type="warning"
onDismiss={() => setVisible(false)}
autoFocus={true}
>
Content
<br />
Expand Down
7 changes: 7 additions & 0 deletions src/__tests__/__snapshots__/documenter.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ when the \`dismissible\` property is set to \`true\`.",
"functions": Array [],
"name": "Alert",
"properties": Array [
Object {
"description": "Automatically focuses the alert when component is mounted.
Use this when the alert should immediately notify the user of a critical error.",
"name": "autoFocus",
"optional": true,
"type": "boolean",
},
Object {
"deprecatedTag": "Custom CSS is not supported. For other use cases, use [data attributes](https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes).",
"description": "Adds the specified classes to the root element of the component.",
Expand Down
7 changes: 7 additions & 0 deletions src/alert/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ export interface AlertProps extends BaseComponentProps {
* Although it is technically possible to insert any content, our UX guidelines only allow you to add a button.
*/
action?: React.ReactNode;

/**
* Automatically focuses the alert when component is mounted.
* Use this when the alert should immediately notify the user of a critical error.
*/
autoFocus?: boolean;

/**
* Fired when the user clicks the close icon that is displayed
* when the `dismissible` property is set to `true`.
Expand Down
28 changes: 19 additions & 9 deletions src/alert/internal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import React, { useEffect, useRef } from 'react';
import clsx from 'clsx';
import { InternalButton } from '../button/internal';
import { IconProps } from '../icon/interfaces';
Expand Down Expand Up @@ -37,6 +37,7 @@ export default function InternalAlert({
header,
buttonText,
action,
autoFocus,
onDismiss,
onButtonClick,
__internalRootRef = null,
Expand All @@ -45,6 +46,7 @@ export default function InternalAlert({
const baseProps = getBaseProps(rest);
const i18n = useInternalI18n('alert');

const focusRef = useRef<HTMLDivElement>(null);
const [breakpoint, breakpointRef] = useContainerBreakpoints(['xs']);
const mergedRef = useMergeRefs(breakpointRef, __internalRootRef);

Expand All @@ -66,6 +68,12 @@ export default function InternalAlert({
[DATA_ATTR_ANALYTICS_ALERT]: type,
};

useEffect(() => {
if (autoFocus && visible && focusRef.current) {
focusRef.current.focus();

Check warning on line 73 in src/alert/internal.tsx

View check run for this annotation

Codecov / codecov/patch

src/alert/internal.tsx#L73

Added line #L73 was not covered by tests
}
}, [autoFocus, visible, focusRef]);

return (
<div
{...baseProps}
Expand All @@ -81,15 +89,17 @@ export default function InternalAlert({
>
<VisualContext contextName="alert">
<div className={clsx(styles.alert, styles[`type-${type}`])}>
<div className={clsx(styles.icon, styles.text)} role="img" aria-label={statusIconAriaLabel}>
<InternalIcon name={typeToIcon[type]} size={size} />
</div>
<div className={styles.body}>
<div className={clsx(styles.message, styles.text)}>
{header && <div className={styles.header}>{header}</div>}
<div className={styles.content}>{children}</div>
<div className={styles['alert-focus-wrapper']} tabIndex={-1} ref={focusRef}>
<div className={clsx(styles.icon, styles.text)} role="img" aria-label={statusIconAriaLabel}>
<InternalIcon name={typeToIcon[type]} size={size} />
</div>
<div className={styles.body}>
<div className={clsx(styles.message, styles.text)}>
{header && <div className={styles.header}>{header}</div>}
<div className={styles.content}>{children}</div>
</div>
{hasAction && <div className={styles.action}>{actionButton}</div>}
</div>
{hasAction && <div className={styles.action}>{actionButton}</div>}
</div>
{dismissible && (
<div className={styles.dismiss}>
Expand Down
12 changes: 12 additions & 0 deletions src/alert/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
@use 'sass:map';
@use '../internal/styles/tokens' as awsui;
@use '../internal/styles' as styles;
@use '../internal/hooks/focus-visible' as focus-visible;

@use './motion';

Expand Down Expand Up @@ -50,6 +51,17 @@
/* used in test-utils */
}

.alert-focus-wrapper {
display: flex;

&:focus {
outline: none;
}
@include focus-visible.when-visible {
@include styles.focus-highlight(awsui.$space-button-focus-outline-gutter);
}
}

.text {
padding: awsui.$border-field-width 0; // To account for vertical misalignment due to button borders
margin: awsui.$space-scaled-xxs awsui.$space-xxs;
Expand Down

0 comments on commit 8a35f5d

Please sign in to comment.