Skip to content

Commit

Permalink
Merge 244b8a8 into 759fdf6
Browse files Browse the repository at this point in the history
  • Loading branch information
shleewhite committed Mar 1, 2022
2 parents 759fdf6 + 244b8a8 commit ed11833
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 41 deletions.
6 changes: 6 additions & 0 deletions .changeset/sweet-days-talk.md
@@ -0,0 +1,6 @@
---
'@twilio-paste/alert': patch
'@twilio-paste/core': patch
---

[Alert] add i18n props to support i18n
70 changes: 70 additions & 0 deletions packages/paste-core/components/alert/__tests__/index.spec.tsx
Expand Up @@ -208,4 +208,74 @@ describe('Alert', () => {
);
});
});

describe('i18n', () => {
it('should have default dismiss button text', () => {
render(
<Alert variant="neutral" onDismiss={onDismissMock}>
This is an alert
</Alert>
);
const dismissButton = screen.getByRole('button', {name: 'Dismiss alert'});
expect(dismissButton).toBeDefined();
});

it('should use i18nDismissLabel for dismiss button text', () => {
render(
<Alert variant="neutral" i18nDismissLabel="Fermez l'alerte" onDismiss={onDismissMock}>
C&apos;est une alerte neutre.
</Alert>
);
const dismissButton = screen.getByRole('button', {name: "Fermez l'alerte"});
expect(dismissButton).toBeDefined();
});

it('should have default error variant icon text', () => {
render(<Alert variant="error">This is an alert</Alert>);
const iconText = screen.getByText('(error)');
expect(iconText).toBeDefined();
});

it('should have default neutral variant icon text', () => {
render(<Alert variant="neutral">This is an alert</Alert>);
const iconText = screen.getByText('(information)');
expect(iconText).toBeDefined();
});

it('should have default warning variant icon text', () => {
render(<Alert variant="warning">This is an alert</Alert>);
const iconText = screen.getByText('(warning)');
expect(iconText).toBeDefined();
});

it('should use the i18nErrorLabel for error variant icon text', () => {
render(
<Alert variant="error" i18nErrorLabel="(erreur)">
C&apos;est une alerte.
</Alert>
);
const iconText = screen.getByText('(erreur)');
expect(iconText).toBeDefined();
});

it('should use the i18nNeutralLabel for neutral variant icon text', () => {
render(
<Alert variant="neutral" i18nNeutralLabel="(information)">
C&apos;est une alerte.
</Alert>
);
const iconText = screen.getByText('(information)');
expect(iconText).toBeDefined();
});

it('should use the i18nWarningLabel for warning variant icon text', () => {
render(
<Alert variant="warning" i18nWarningLabel="(avertissement)">
C&apos;est une alerte.
</Alert>
);
const iconText = screen.getByText('(avertissement)');
expect(iconText).toBeDefined();
});
});
});
71 changes: 36 additions & 35 deletions packages/paste-core/components/alert/src/index.tsx
Expand Up @@ -5,6 +5,7 @@ import type {ValueOf} from '@twilio-paste/types';
import {Box, safelySpreadBoxProps} from '@twilio-paste/box';
import {MediaObject, MediaFigure, MediaBody} from '@twilio-paste/media-object';
import {Button} from '@twilio-paste/button';
import {ScreenReaderOnly} from '@twilio-paste/screen-reader-only';
import {CloseIcon} from '@twilio-paste/icons/esm/CloseIcon';
import {ErrorIcon} from '@twilio-paste/icons/esm/ErrorIcon';
import {NeutralIcon} from '@twilio-paste/icons/esm/NeutralIcon';
Expand Down Expand Up @@ -49,46 +50,46 @@ export interface AlertProps extends Pick<BoxProps, 'element'> {
onDismiss?: () => void;
role?: string;
variant: AlertVariants;
i18nDismissLabel?: string;
i18nErrorLabel?: string;
i18nNeutralLabel?: string;
i18nWarningLabel?: string;
}

const renderAlertIcon = (variant: AlertVariants, element: string): React.ReactElement => {
switch (variant) {
case AlertVariants.ERROR:
return (
<ErrorIcon
element={`${element}_ICON`}
color="colorTextIconError"
decorative={false}
title="error: "
size="sizeIcon20"
/>
);
return <ErrorIcon element={`${element}_ICON`} color="colorTextIconError" decorative size="sizeIcon20" />;
case AlertVariants.WARNING:
return (
<WarningIcon
element={`${element}_ICON`}
color="colorTextIconWarning"
decorative={false}
title="warning: "
size="sizeIcon20"
/>
);
return <WarningIcon element={`${element}_ICON`} color="colorTextIconWarning" decorative size="sizeIcon20" />;
case AlertVariants.NEUTRAL:
default:
return (
<NeutralIcon
element={`${element}_ICON`}
color="colorTextIconNeutral"
decorative={false}
title="information: "
size="sizeIcon20"
/>
);
return <NeutralIcon element={`${element}_ICON`} color="colorTextIconNeutral" decorative size="sizeIcon20" />;
}
};

const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
({children, onDismiss, variant, role, element = 'ALERT', ...props}, ref) => {
(
{
children,
onDismiss,
variant,
role,
element = 'ALERT',
i18nDismissLabel = 'Dismiss alert',
i18nErrorLabel = '(error)',
i18nNeutralLabel = '(information)',
i18nWarningLabel = '(warning)',
...props
},
ref
) => {
const i18nLabelVariantMap = {
error: i18nErrorLabel,
neutral: i18nNeutralLabel,
warning: i18nWarningLabel,
};

return (
<Box
{...safelySpreadBoxProps(props)}
Expand All @@ -108,18 +109,14 @@ const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
<MediaObject as="div">
<MediaFigure as="div" spacing="space30">
{renderAlertIcon(variant, element)}
<ScreenReaderOnly>{i18nLabelVariantMap[variant]}</ScreenReaderOnly>
</MediaFigure>
<MediaBody as="div">{children}</MediaBody>
{onDismiss && typeof onDismiss === 'function' && (
<MediaFigure align="end" spacing="space60">
<Button onClick={onDismiss} variant="secondary_icon" size="reset" element={`${element}_DISMISS_BUTTON`}>
<CloseIcon
element={`${element}_DISMISS_ICON`}
color="colorTextIcon"
decorative={false}
title="Dismiss alert"
size="sizeIcon20"
/>
<CloseIcon element={`${element}_DISMISS_ICON`} color="colorTextIcon" decorative size="sizeIcon20" />
<ScreenReaderOnly>{i18nDismissLabel}</ScreenReaderOnly>
</Button>
</MediaFigure>
)}
Expand All @@ -136,6 +133,10 @@ Alert.propTypes = {
role: PropTypes.string,
variant: PropTypes.oneOf(Object.values(AlertVariants)).isRequired,
element: PropTypes.string,
i18nDismissLabel: PropTypes.string,
i18nErrorLabel: PropTypes.string,
i18nNeutralLabel: PropTypes.string,
i18nWarningLabel: PropTypes.string,
};

export {Alert};
24 changes: 24 additions & 0 deletions packages/paste-core/components/alert/stories/index.stories.tsx
Expand Up @@ -199,3 +199,27 @@ export const CustomAlert = (): React.ReactNode => {
</CustomizationProvider>
);
};

export const I18nAlerts = (): React.ReactNode => {
const i18nStrings = {
i18nDismissLabel: "Fermez l'alerte",
i18nErrorLabel: '(erreur)',
i18nWarningLabel: '(avertissement)',
i18nNeutralLabel: '(information)',
};
return (
<>
<Alert variant="error" {...i18nStrings} onDismiss={action('dismiss')}>
<Text as="div">C&apos;est une alerte d&apos;erreur.</Text>
</Alert>
<Alert variant="warning" {...i18nStrings} onDismiss={action('dismiss')}>
<Text as="div">C&apos;est une alerte d&apos;avertissement.</Text>
</Alert>
<Alert variant="neutral" {...i18nStrings} onDismiss={action('dismiss')}>
<Text as="div">C&apos;est une alerte neutre.</Text>
</Alert>
</>
);
};

I18nAlerts.storyName = 'i18n Alerts';
30 changes: 24 additions & 6 deletions packages/paste-website/src/pages/components/alert/index.mdx
Expand Up @@ -222,6 +222,20 @@ Keep in mind that the longer an alert is present, the more it starts looking lik
</Alert>`}
</LivePreview>

### Internationalization

To internationalize an alert, simply pass different text as children to the alert.
The only exceptions to this are the labels for both the dismiss button and the variant icons.
To change the dismiss button&apos;s label text, use the `i18nDismissLabel` prop.
To change the label of a variant's icon, use the matching i18n prop for the variant.
For an `error` variant, for example, set the `i18nErrorLabel` prop.

<LivePreview scope={{Alert, Text}} language="jsx">
{`<Alert onDismiss={() => alert('fermée')} variant="warning" i18nWarningLabel="(avertissement)">
C&apos;est une alerte d&apos;avertissement.
</Alert>`}
</LivePreview>

## Composition Notes

Keep alert text brief by placing only the highest priority information in an alert. Too much text in an alert can overwhelm a user. A good rule of thumb is to keep alert text to a single line in a desktop-sized container, or limiting it to 90 characters.
Expand Down Expand Up @@ -369,12 +383,16 @@ const Component = () => (

#### Props

| Prop | Type | Description | Default |
| ---------- | ------------ | ------------------------------------------------------------------------------------------------ | ------- |
| children? | `ReactNode` | | null |
| onDismiss? | `() => void` | Create dismissable alerts by providing an onDismiss callback | null |
| role? | `string` | Provide a specific ARIA role to the alert that overrides the calculated one set by the component | null |
| variant? | `string` | `error` / `neutral` / `warning` | null |
| Prop | Type | Description | Default |
| ----------------- | ------------ | ------------------------------------------------------------------------------------------------ | --------------- |
| children? | `ReactNode` | | null |
| onDismiss? | `() => void` | Create dismissable alerts by providing an onDismiss callback | null |
| role? | `string` | Provide a specific ARIA role to the alert that overrides the calculated one set by the component | null |
| variant? | `string` | `error` / `neutral` / `warning` | null |
| i18nDismissLabel? | `string` | Label for the dismiss button in a dismissable alert | 'Dismiss alert' |
| i18nErrorLabel? | `string` | Icon label text for the `error` variant | '(error)' |
| i18nNeutralLabel? | `string` | Icon label text for the `neutral` variant | '(information)' |
| i18nWarningLabel? | `string` | Icon label text for the `warning` variant | '(warning)' |

<ChangelogRevealer>
<Changelog />
Expand Down

0 comments on commit ed11833

Please sign in to comment.