From 82b4c9c51567be2a3fbb70df9ed0b006a51409d9 Mon Sep 17 00:00:00 2001 From: Katie George Date: Fri, 8 Sep 2023 11:14:06 -0700 Subject: [PATCH] chore: Creates internal drawer component (#1495) Co-authored-by: Katie George --- src/i18n/messages-types.ts | 3 ++ src/i18n/messages/all.de.json | 3 ++ src/i18n/messages/all.en-GB.json | 3 ++ src/i18n/messages/all.en.json | 3 ++ src/i18n/messages/all.es.json | 3 ++ src/i18n/messages/all.fr.json | 3 ++ src/i18n/messages/all.id.json | 3 ++ src/i18n/messages/all.it.json | 3 ++ src/i18n/messages/all.ja.json | 3 ++ src/i18n/messages/all.ko.json | 3 ++ src/i18n/messages/all.pt-BR.json | 3 ++ src/i18n/messages/all.zh-CN.json | 3 ++ src/i18n/messages/all.zh-TW.json | 3 ++ .../drawer/__tests__/drawer.test.tsx | 49 +++++++++++++++++++ src/internal/components/drawer/index.tsx | 31 ++++++++++++ src/internal/components/drawer/interfaces.ts | 37 ++++++++++++++ src/internal/components/drawer/styles.scss | 41 ++++++++++++++++ 17 files changed, 197 insertions(+) create mode 100644 src/internal/components/drawer/__tests__/drawer.test.tsx create mode 100644 src/internal/components/drawer/index.tsx create mode 100644 src/internal/components/drawer/interfaces.ts create mode 100644 src/internal/components/drawer/styles.scss diff --git a/src/i18n/messages-types.ts b/src/i18n/messages-types.ts index 18c4464b32..5f563e492b 100644 --- a/src/i18n/messages-types.ts +++ b/src/i18n/messages-types.ts @@ -166,6 +166,9 @@ export interface I18nFormatArgTypes { "i18nStrings.successIconAriaLabel": never; "i18nStrings.warningIconAriaLabel": never; } + "drawer": { + "i18nStrings.loadingText": never; + } "form": { "errorIconAriaLabel": never; } diff --git a/src/i18n/messages/all.de.json b/src/i18n/messages/all.de.json index 36936eb0ba..9cfb1fc7aa 100644 --- a/src/i18n/messages/all.de.json +++ b/src/i18n/messages/all.de.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {Letzte {amount} {unit}} other {Letzte {amount} {unit}s}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}s}}" }, + "drawer": { + "i18nStrings.loadingText": "Inhalte werden geladen" + }, "flashbar": { "i18nStrings.ariaLabel": "Benachrichtigungen", "i18nStrings.errorIconAriaLabel": "Fehler", diff --git a/src/i18n/messages/all.en-GB.json b/src/i18n/messages/all.en-GB.json index 26c55f9d83..aae2b8e86b 100644 --- a/src/i18n/messages/all.en-GB.json +++ b/src/i18n/messages/all.en-GB.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {Last {amount} {unit}} other {Last {amount} {unit}s}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}s}}" }, + "drawer": { + "i18nStrings.loadingText": "Loading content" + }, "flashbar": { "i18nStrings.ariaLabel": "Notifications", "i18nStrings.errorIconAriaLabel": "Error", diff --git a/src/i18n/messages/all.en.json b/src/i18n/messages/all.en.json index 516a82085c..64adae54ac 100644 --- a/src/i18n/messages/all.en.json +++ b/src/i18n/messages/all.en.json @@ -115,6 +115,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {Last {amount} {unit}} other {Last {amount} {unit}s}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}s}}" }, + "drawer": { + "i18nStrings.loadingText": "Loading content" + }, "flashbar": { "i18nStrings.ariaLabel": "Notifications", "i18nStrings.errorIconAriaLabel": "Error", diff --git a/src/i18n/messages/all.es.json b/src/i18n/messages/all.es.json index 2e81e24a8c..c1ea595a97 100644 --- a/src/i18n/messages/all.es.json +++ b/src/i18n/messages/all.es.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {Último {amount} {unit}} other {Últimos {amount} {unit}}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}}}" }, + "drawer": { + "i18nStrings.loadingText": "Cargando contenido" + }, "flashbar": { "i18nStrings.ariaLabel": "Notificaciones", "i18nStrings.errorIconAriaLabel": "Error", diff --git a/src/i18n/messages/all.fr.json b/src/i18n/messages/all.fr.json index 9cff3f75dc..22301a0469 100644 --- a/src/i18n/messages/all.fr.json +++ b/src/i18n/messages/all.fr.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {Dernier {amount} {unit}} other {Dernières {amount} {unit}s}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}s}}" }, + "drawer": { + "i18nStrings.loadingText": "Chargement du contenu en cours" + }, "flashbar": { "i18nStrings.ariaLabel": "Notifications", "i18nStrings.errorIconAriaLabel": "Erreur", diff --git a/src/i18n/messages/all.id.json b/src/i18n/messages/all.id.json index aa850873d7..d0883b928d 100644 --- a/src/i18n/messages/all.id.json +++ b/src/i18n/messages/all.id.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {{amount} {unit} terakhir} other {{amount} {unit} terakhir}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}}}" }, + "drawer": { + "i18nStrings.loadingText": "Memuat konten" + }, "flashbar": { "i18nStrings.ariaLabel": "Notifikasi", "i18nStrings.errorIconAriaLabel": "Kesalahan", diff --git a/src/i18n/messages/all.it.json b/src/i18n/messages/all.it.json index 014467ace2..b2c7c072b9 100644 --- a/src/i18n/messages/all.it.json +++ b/src/i18n/messages/all.it.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {Ultimo {amount} {unit}} other {Ultimi {amount} {unit}}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}}}" }, + "drawer": { + "i18nStrings.loadingText": "Caricamento dei contenuti" + }, "flashbar": { "i18nStrings.ariaLabel": "Notifiche", "i18nStrings.errorIconAriaLabel": "Errore", diff --git a/src/i18n/messages/all.ja.json b/src/i18n/messages/all.ja.json index 53c95c88c9..3f5572880b 100644 --- a/src/i18n/messages/all.ja.json +++ b/src/i18n/messages/all.ja.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {最後の {amount} {unit}} other {最後の {amount} {unit}}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}}}" }, + "drawer": { + "i18nStrings.loadingText": "コンテンツのロード中" + }, "flashbar": { "i18nStrings.ariaLabel": "通知", "i18nStrings.errorIconAriaLabel": "エラー", diff --git a/src/i18n/messages/all.ko.json b/src/i18n/messages/all.ko.json index 638f96d14b..1374070ac8 100644 --- a/src/i18n/messages/all.ko.json +++ b/src/i18n/messages/all.ko.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {마지막 {amount}{unit}} other {마지막 {amount}{unit}}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}}}" }, + "drawer": { + "i18nStrings.loadingText": "콘텐츠 로드 중" + }, "flashbar": { "i18nStrings.ariaLabel": "알림", "i18nStrings.errorIconAriaLabel": "오류", diff --git a/src/i18n/messages/all.pt-BR.json b/src/i18n/messages/all.pt-BR.json index 2f375d450a..1cabb2b09f 100644 --- a/src/i18n/messages/all.pt-BR.json +++ b/src/i18n/messages/all.pt-BR.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {Último(a) {amount} {unit}} other {Últimos(as) {amount} {unit}s}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}s}}" }, + "drawer": { + "i18nStrings.loadingText": "Carregando conteúdo" + }, "flashbar": { "i18nStrings.ariaLabel": "Notificações", "i18nStrings.errorIconAriaLabel": "Erro", diff --git a/src/i18n/messages/all.zh-CN.json b/src/i18n/messages/all.zh-CN.json index 80c47855f7..40c9517683 100644 --- a/src/i18n/messages/all.zh-CN.json +++ b/src/i18n/messages/all.zh-CN.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {最近 {amount} {unit}} other {最近 {amount} {unit}}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}}}" }, + "drawer": { + "i18nStrings.loadingText": "正在加载内容" + }, "flashbar": { "i18nStrings.ariaLabel": "通知", "i18nStrings.errorIconAriaLabel": "错误", diff --git a/src/i18n/messages/all.zh-TW.json b/src/i18n/messages/all.zh-TW.json index b2d2028a5b..2d96eadc8c 100644 --- a/src/i18n/messages/all.zh-TW.json +++ b/src/i18n/messages/all.zh-TW.json @@ -114,6 +114,9 @@ "i18nStrings.formatRelativeRange": "{amount, plural, one {最後 {amount} {unit}} other {最後 {amount} {unit}}}", "i18nStrings.formatUnit": "{amount, plural, one {{unit}} other {{unit}}}" }, + "drawer": { + "i18nStrings.loadingText": "載入內容" + }, "flashbar": { "i18nStrings.ariaLabel": "通知", "i18nStrings.errorIconAriaLabel": "錯誤", diff --git a/src/internal/components/drawer/__tests__/drawer.test.tsx b/src/internal/components/drawer/__tests__/drawer.test.tsx new file mode 100644 index 0000000000..d0b0951cdb --- /dev/null +++ b/src/internal/components/drawer/__tests__/drawer.test.tsx @@ -0,0 +1,49 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; +import { render } from '@testing-library/react'; +import createWrapper from '../../../../../lib/components/test-utils/dom'; +import Drawer from '../../../../../lib/components/internal/components/drawer'; +import TestI18nProvider from '../../../../../lib/components/i18n/testing'; +import styles from '../../../../../lib/components/internal/components/drawer/styles.css.js'; + +function renderDrawer(jsx: React.ReactElement) { + const { container } = render(jsx); + const wrapper = createWrapper(container).findByClassName(styles.drawer)!; + const header = wrapper.findByClassName(styles.header); + const content = wrapper.findByClassName(styles['test-utils-drawer-content']); + return { wrapper, header, content }; +} + +test('renders only children by default', () => { + const { header, content } = renderDrawer(test content); + + expect(header).toBeNull(); + expect(content!.getElement()).toHaveTextContent('test content'); +}); + +test('renders header if it is provided', () => { + const { header, content } = renderDrawer(there is a header above); + expect(header!.getElement()).toHaveTextContent('Bla bla'); + expect(content!.getElement()).toHaveTextContent('there is a header above'); +}); + +test('renders loading state', () => { + const { wrapper, header, content } = renderDrawer( + + ); + expect(wrapper.findStatusIndicator()!.getElement()).toHaveTextContent('Loading content'); + expect(header).toBeNull(); + expect(content).toBeNull(); +}); + +describe('i18n', () => { + test('supports providing loadingText from i18n provider', () => { + const { container } = render( + + + + ); + expect(createWrapper(container).findStatusIndicator()!.getElement()).toHaveTextContent('Custom loading text'); + }); +}); diff --git a/src/internal/components/drawer/index.tsx b/src/internal/components/drawer/index.tsx new file mode 100644 index 0000000000..81faac8e3a --- /dev/null +++ b/src/internal/components/drawer/index.tsx @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import clsx from 'clsx'; +import React from 'react'; +import { getBaseProps } from '../../base-component'; +import InternalStatusIndicator from '../../../status-indicator/internal'; +import styles from './styles.css.js'; +import { DrawerProps } from './interfaces'; +import LiveRegion from '../../components/live-region'; +import { useInternalI18n } from '../../../i18n/context'; + +export default function Drawer({ header, children, loading, i18nStrings, ...restProps }: DrawerProps) { + const baseProps = getBaseProps(restProps); + const i18n = useInternalI18n('drawer'); + const containerProps = { + ...baseProps, + className: clsx(baseProps.className, styles.drawer), + }; + return loading ? ( +
+ + {i18n('i18nStrings.loadingText', i18nStrings?.loadingText)} + +
+ ) : ( +
+ {header &&
{header}
} +
{children}
+
+ ); +} diff --git a/src/internal/components/drawer/interfaces.ts b/src/internal/components/drawer/interfaces.ts new file mode 100644 index 0000000000..dbb2704706 --- /dev/null +++ b/src/internal/components/drawer/interfaces.ts @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { BaseComponentProps } from '../../base-component'; +import React from 'react'; + +export interface DrawerProps extends BaseComponentProps { + /** + * Header of the drawer. + * + * It should contain the only `h2` used in the drawer. + */ + header?: React.ReactNode; + + /** + * Main content of the drawer. + * + */ + children?: React.ReactNode; + + /** + * Renders the drawer in a loading state. We recommend that you also set a `loadingText`. + */ + loading?: boolean; + + /** + * An object containing all the necessary localized strings required by the component. + * @i18n + */ + i18nStrings?: I18nStrings; +} + +export interface I18nStrings { + /** + Specifies the text that's displayed when the panel is in a loading state. + */ + loadingText?: string; +} diff --git a/src/internal/components/drawer/styles.scss b/src/internal/components/drawer/styles.scss new file mode 100644 index 0000000000..d71f17c984 --- /dev/null +++ b/src/internal/components/drawer/styles.scss @@ -0,0 +1,41 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + SPDX-License-Identifier: Apache-2.0 +*/ + +/* stylelint-disable @cloudscape-design/no-implicit-descendant, selector-max-type */ +@use '../../styles/tokens' as awsui; +@use '../../styles' as styles; + +.drawer { + @include styles.styles-reset; + word-wrap: break-word; + padding: awsui.$space-scaled-l awsui.$space-panel-side-right awsui.$space-scaled-xxxl awsui.$space-panel-side-left; +} + +.header { + @include styles.font-panel-header; + color: awsui.$color-text-heading-default; + padding-bottom: awsui.$space-scaled-l; + padding-left: awsui.$space-panel-side-left; + // padding to make sure the header doesn't overlap with the close icon + padding-right: calc(#{awsui.$space-xl} + #{awsui.$space-scaled-xxl}); + border-bottom: awsui.$border-divider-section-width solid awsui.$color-border-divider-default; + margin: 0 calc(-1 * #{awsui.$space-panel-side-right}) awsui.$space-scaled-l calc(-1 * #{awsui.$space-panel-side-left}); + + h2, + h3, + h4, + h5, + h6 { + @include styles.font-panel-header; + padding-top: 0; + padding-bottom: 0; + margin-top: 0; + margin-bottom: 0; + } +} + +.test-utils-drawer-content { + /* used in test-utils */ +}