diff --git a/pages/app-layout/with-stacked-notifications-and-no-paddings.page.tsx b/pages/app-layout/with-stacked-notifications-and-no-paddings.page.tsx new file mode 100644 index 0000000000..a906694a32 --- /dev/null +++ b/pages/app-layout/with-stacked-notifications-and-no-paddings.page.tsx @@ -0,0 +1,61 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; +import AppLayout from '~components/app-layout'; +import ScreenshotArea from '../utils/screenshot-area'; +import labels from './utils/labels'; +import Flashbar from '~components/flashbar'; +import Table from '~components/table'; +import Header from '~components/header'; + +export default function () { + return ( + + void 0, + }, + { + type: 'warning', + header: 'Warning message', + statusIconAriaLabel: 'warning', + dismissLabel: 'Dismiss notification', + dismissible: true, + onDismiss: () => void 0, + }, + ]} + stackItems={true} + i18nStrings={{ + ariaLabel: 'Notifications', + notificationBarText: 'Notifications', + notificationBarAriaLabel: 'View all notifications', + errorIconAriaLabel: 'Error', + successIconAriaLabel: 'Success', + warningIconAriaLabel: 'Warning', + infoIconAriaLabel: 'Information', + inProgressIconAriaLabel: 'In progress', + }} + /> + } + disableContentPaddings={true} + content={ + Sticky Table Header} + items={[]} + columnDefinitions={[]} + stickyHeader={true} + /> + } + /> + + ); +} diff --git a/pages/app-layout/with-stacked-notifications-and-table.page.tsx b/pages/app-layout/with-stacked-notifications-and-table.page.tsx deleted file mode 100644 index 3761456b76..0000000000 --- a/pages/app-layout/with-stacked-notifications-and-table.page.tsx +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import React, { useState } from 'react'; -import ScreenshotArea from '../utils/screenshot-area'; -import labels from './utils/labels'; -import { FlashbarProps } from '~components/flashbar'; -import { AppLayout, Box, Button, Flashbar, Header, SpaceBetween, SplitPanel, Table, Toggle } from '~components'; - -export default function () { - const [notifications, setNotifications] = useState>([]); - const [nextId, setNextId] = useState(0); - const [disableContentPaddings, setDisableContentPaddings] = useState(true); - const [stackItems, setStackItems] = useState(true); - const [stickyNotifications, setStickyNotifications] = useState(false); - const [stickyTableHeader, setStickyTableHeader] = useState(true); - const [splitPanelOpen, setSplitPanelOpen] = useState(true); - - const addNotification = () => { - setNotifications(notifications => { - const id = nextId.toString(); - return [ - { - statusIconAriaLabel: 'Info', - content: - 'Considerably long notification message to verify that the notifications bar in the Flashbar does not overlap the text.', - id, - dismissible: true, - onDismiss: () => dismissNotification(id), - }, - ...notifications, - ]; - }); - setNextId(nextId + 1); - }; - - const dismissNotification = (id: string): void => - setNotifications(notifications => notifications.filter(item => item.id !== id)); - - return ( - - - } - disableContentPaddings={disableContentPaddings} - content={ -
- - - - - - } - > - Table Header - - } - footer={ -
-

Table Footer

-

Long footer to make the main content scrollable.

-

- Use the controls in the Tools panel on the right and the checkboxes at the top in order to test the - integration of the Flashbar with App layout in different settings. -

-
- } - items={[]} - columnDefinitions={[]} - stickyHeader={stickyTableHeader} - /> - } - tools={ - - - setDisableContentPaddings(event.detail.checked)} - data-id="toggle-content-paddings" - > - Disable content paddings - - setStickyNotifications(event.detail.checked)} - data-id="toggle-sticky-notifications" - > - Sticky Notifications - - setStickyTableHeader(event.detail.checked)} - data-id="toggle-sticky-table-header" - > - Sticky Table header - - setStackItems(event.detail.checked)} - data-id="toggle-stack-items" - > - Stack notifications - - - - } - toolsOpen={true} - splitPanelOpen={splitPanelOpen} - onSplitPanelToggle={event => setSplitPanelOpen(event.detail.open)} - splitPanel={ - -

Split panel content

-
- } - splitPanelPreferences={{ position: 'bottom' }} - /> - - ); -} diff --git a/src/app-layout/index.tsx b/src/app-layout/index.tsx index 4d8441582f..f6549a6d80 100644 --- a/src/app-layout/index.tsx +++ b/src/app-layout/index.tsx @@ -453,7 +453,6 @@ const OldAppLayout = React.forwardRef( > {notifications && ( ) => { + ({ sticky, ...props }: NotificationWrapperProps, ref: React.Ref) => { return sticky ? (
-
+
{props.children}
@@ -32,11 +27,7 @@ export const Notifications = React.forwardRef(
{props.children} diff --git a/src/app-layout/notifications/styles.scss b/src/app-layout/notifications/styles.scss index 9f9bae4d3e..0f560015ba 100644 --- a/src/app-layout/notifications/styles.scss +++ b/src/app-layout/notifications/styles.scss @@ -21,11 +21,3 @@ position: sticky; #{custom-props.$flashbarStickyBottomMargin}: #{awsui.$space-xxl}; } - -.no-content-paddings { - /* - When using the disableContentPaddings option, the Flashbar will use this custom property to add additional space - when the notification bar is rendered, to prevent it from overlapping the content. - */ - #{custom-props.$stackedNotificationsBottomMargin}: #{awsui.$space-scaled-l}; -} diff --git a/src/app-layout/visual-refresh/notifications.tsx b/src/app-layout/visual-refresh/notifications.tsx index ad6c631493..4444d87b83 100644 --- a/src/app-layout/visual-refresh/notifications.tsx +++ b/src/app-layout/visual-refresh/notifications.tsx @@ -25,11 +25,6 @@ export default function Notifications() { return null; } - /* - The notificationsElement ref is assigned to an inner div to prevent internal bottom margin from affecting the - calculated height, which is used for sticky elements below. - */ - return (
-
{notifications}
+ {notifications}
); } diff --git a/src/flashbar/collapsible-flashbar.tsx b/src/flashbar/collapsible-flashbar.tsx index 98feaa5864..ae5d8888d2 100644 --- a/src/flashbar/collapsible-flashbar.tsx +++ b/src/flashbar/collapsible-flashbar.tsx @@ -20,7 +20,6 @@ import { sendToggleMetric } from './internal/analytics'; import { useFlashbar } from './common'; import { throttle } from '../internal/utils/throttle'; import { scrollElementIntoView } from '../internal/utils/scrollable-containers'; -import { findUpUntil } from '../internal/utils/dom'; export { FlashbarProps }; @@ -113,7 +112,7 @@ export default function CollapsibleFlashbar({ items, ...restProps }: FlashbarPro const windowHeight = window.innerHeight; // Take the parent region into account if using the App Layout, because it might have additional margins. // Otherwise we use the Flashbar component for this calculation. - const outerElement = findUpUntil(flashbar, element => element.getAttribute('role') === 'region') || flashbar; + const outerElement = flashbar.parentElement?.parentElement || flashbar; const applySpacing = isFlashbarStackExpanded && Math.ceil(outerElement.getBoundingClientRect().bottom) >= windowHeight; if (!applySpacing) { @@ -286,10 +285,7 @@ export default function CollapsibleFlashbar({ items, ...restProps }: FlashbarPro styles.flashbar, styles[`breakpoint-${breakpoint}`], styles.stack, - isCollapsible && styles.collapsible, - items.length === 2 && styles['short-list'], isFlashbarStackExpanded && styles.expanded, - isVisualRefresh && styles['visual-refresh'], getVisualContextClassname('flashbar') )} ref={mergedRef} @@ -302,8 +298,7 @@ export default function CollapsibleFlashbar({ items, ...restProps }: FlashbarPro styles['notification-bar'], isVisualRefresh && styles['visual-refresh'], isFlashbarStackExpanded ? styles.expanded : styles.collapsed, - transitioning && styles['animation-running'], - items.length === 2 && styles['short-list'] + transitioning && styles['animation-running'] )} onClick={toggleCollapseExpand} ref={notificationBarRef} diff --git a/src/flashbar/collapsible.scss b/src/flashbar/collapsible.scss index 252992609e..6bd6d55c41 100644 --- a/src/flashbar/collapsible.scss +++ b/src/flashbar/collapsible.scss @@ -2,7 +2,6 @@ Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -@use 'sass:map'; @use '../internal/generated/custom-css-properties/index.scss' as custom-props; @use '../internal/hooks/focus-visible' as focus-visible; @use '../internal/styles/tokens' as awsui; @@ -10,23 +9,10 @@ @use '../internal/styles/typography' as typography; @use './collapsible.motion'; -$notification-bar-top-overlap: ( - 'collapsed': calc(#{awsui.$space-scaled-xl} + 3px), - 'expanded': awsui.$space-scaled-s, -); -$notification-bar-line-height: awsui.$font-body-m-line-height; -$notification-bar-padding-vertical: styles.$control-padding-vertical; -$notification-bar-collapsed-item-overflow: ( - 'x': 10px, - 'y': 8px, -); -$notification-bar-border-width: ( - 'classic': 1px, - 'visual-refresh': 2px, -); +$collapsed-item-overflow: 10px; .stack { - $notification-bar-horizontal-margin: 3 * map.get($notification-bar-collapsed-item-overflow, 'x'); + $notification-bar-horizontal-margin: 3 * $collapsed-item-overflow; display: grid; grid-template-columns: $notification-bar-horizontal-margin 1fr $notification-bar-horizontal-margin; } @@ -55,10 +41,10 @@ the grid layout will be: display: grid; grid-column: 1 / 4; grid-template-columns: - repeat(var(#{custom-props.$flashbarStackDepth}), map.get($notification-bar-collapsed-item-overflow, 'x')) + repeat(var(#{custom-props.$flashbarStackDepth}), $collapsed-item-overflow) 1fr - repeat(var(#{custom-props.$flashbarStackDepth}), map.get($notification-bar-collapsed-item-overflow, 'x')); - row-gap: map.get($notification-bar-collapsed-item-overflow, 'y'); + repeat(var(#{custom-props.$flashbarStackDepth}), $collapsed-item-overflow); + row-gap: 8px; z-index: 0; > .item { @@ -101,88 +87,27 @@ the grid layout will be: @include stacked-flash-z-index; } -@mixin vertical-spacing($top-overlap, $border-width, $buffer-bottom: 0px, $offset-bottom: 0px) { - $notification-bar-full-height: calc( - #{$notification-bar-line-height} + 2 * (#{$notification-bar-padding-vertical} + #{$border-width}) - ); - - /* - The default bottom margin ($default-margin-bottom) is just enough to prevent the notification bar from overlapping the - elements below. But if using disableContentPaddings, $stackedNotificationsBottomMargin will be set at the App Layout - level so that this distance is increased to prevent the main area from touching the notifications area. - */ - $default-margin-bottom: calc(#{$notification-bar-full-height} - #{$top-overlap}); - margin-bottom: calc( - var(#{custom-props.$stackedNotificationsBottomMargin}, #{$default-margin-bottom}) + #{$buffer-bottom} + #{$offset-bottom} - ); - - > .notification-bar { - margin-top: calc(-1 * #{$top-overlap} + #{$offset-bottom}); - margin-bottom: calc(#{$top-overlap} - #{$notification-bar-full-height} - #{$offset-bottom}); - padding-top: $notification-bar-padding-vertical; - padding-bottom: $notification-bar-padding-vertical; - } -} +@mixin toggle-button-layout($top-overlap-collapsed, $border-width) { + $margin-top-when-collapsed: -$top-overlap-collapsed; + $line-height: awsui.$font-body-m-line-height; + $padding-vertical: styles.$control-padding-vertical; + $margin-top-when-expanded: -12px; -.stack.collapsible { - &:not(.expanded) { - &:not(.short-list) { - &:not(.visual-refresh) { - @include vertical-spacing( - map.get($notification-bar-top-overlap, 'collapsed'), - map.get($notification-bar-border-width, 'classic') - ); - } - &.visual-refresh { - @include vertical-spacing( - map.get($notification-bar-top-overlap, 'collapsed'), - map.get($notification-bar-border-width, 'visual-refresh') - ); - } - } + padding-top: $padding-vertical; + padding-bottom: $padding-vertical; - /* - Since the notification bar is anchored to the bottom of the Flashbar, we need to push it up to account for the - missing third item when there are only 2 items in the stack. - */ - &.short-list { - &:not(.visual-refresh) { - @include vertical-spacing( - map.get($notification-bar-top-overlap, 'collapsed'), - map.get($notification-bar-border-width, 'classic'), - 0px, - map.get($notification-bar-collapsed-item-overflow, 'y') - ); - } - &.visual-refresh { - @include vertical-spacing( - map.get($notification-bar-top-overlap, 'collapsed'), - map.get($notification-bar-border-width, 'visual-refresh'), - 0px, - map.get($notification-bar-collapsed-item-overflow, 'y') - ); - } - } + &:not(.expanded) { + margin-top: #{$margin-top-when-collapsed}; + margin-bottom: calc( + #{-$margin-top-when-collapsed} - (#{$line-height} + 2 * (#{$padding-vertical} + #{$border-width})) + ); } &.expanded { - &:not(.visual-refresh) { - /* - Give a bit more bottom margin when expanded, but only in Classic because in Visual Refresh the margin around the - notifications slot already gives it enough space. - */ - @include vertical-spacing( - map.get($notification-bar-top-overlap, 'expanded'), - map.get($notification-bar-border-width, 'classic'), - awsui.$space-scaled-m - ); - } - &.visual-refresh { - @include vertical-spacing( - map.get($notification-bar-top-overlap, 'expanded'), - map.get($notification-bar-border-width, 'visual-refresh') - ); - } + margin-top: #{$margin-top-when-expanded}; + margin-bottom: calc( + #{-$margin-top-when-expanded} - (#{$line-height} + 2 * (#{$padding-vertical} + #{$border-width})) + ); } } @@ -260,14 +185,20 @@ the grid layout will be: } &.visual-refresh { - $border-width: map.get($notification-bar-border-width, 'visual-refresh'); + $border-width: 2px; + // Amount of pixels that the element is pushed up to overlap the Flashbar (when collapsed) + $top-overlap-collapsed: 27px; + @include toggle-button-layout($top-overlap-collapsed, $border-width); border-width: $border-width; padding-left: awsui.$space-l; padding-right: awsui.$space-l; } &:not(.visual-refresh) { - $border-width: map.get($notification-bar-border-width, 'classic'); + $border-width: 1px; + // Amount of pixels that the element is pushed up to overlap the Flashbar (when collapsed) + $top-overlap-collapsed: 24px; + @include toggle-button-layout($top-overlap-collapsed, $border-width); border-width: $border-width; padding-left: awsui.$space-s; padding-right: awsui.$space-s; @@ -286,8 +217,6 @@ the grid layout will be: flex-grow: 1; background: none; border: 0 none; - padding-top: 0; - padding-bottom: 0; > .icon { @include styles.with-motion { diff --git a/src/internal/generated/custom-css-properties/list.js b/src/internal/generated/custom-css-properties/list.js index 3f6fe062a4..8a9d58571f 100644 --- a/src/internal/generated/custom-css-properties/list.js +++ b/src/internal/generated/custom-css-properties/list.js @@ -34,6 +34,5 @@ const customCssPropertiesList = [ 'flashbarStackDepth', 'flashbarStackIndex', 'flashbarStickyBottomMargin', - 'stackedNotificationsBottomMargin', ]; module.exports = customCssPropertiesList;