From eda44495d4d10aa7eabc93d019c104b23b66c1e1 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Tue, 8 Oct 2024 16:38:20 +0200 Subject: [PATCH 1/8] Revert "fix: Partially controllable tools (#2793)" This reverts commit 227dd63ea3e3706353ff3e00de4e03c254ca47b4. --- src/app-layout/__tests__/tools.test.tsx | 22 +------------------ src/app-layout/utils/use-drawers.ts | 4 +--- .../visual-refresh-toolbar/index.tsx | 2 -- .../visual-refresh-toolbar/interfaces.ts | 2 -- .../toolbar/drawer-triggers.tsx | 11 +++------- .../visual-refresh-toolbar/toolbar/index.tsx | 4 ---- 6 files changed, 5 insertions(+), 40 deletions(-) diff --git a/src/app-layout/__tests__/tools.test.tsx b/src/app-layout/__tests__/tools.test.tsx index 1105ac54db..2a28f2c7f7 100644 --- a/src/app-layout/__tests__/tools.test.tsx +++ b/src/app-layout/__tests__/tools.test.tsx @@ -6,7 +6,7 @@ import { act, waitFor } from '@testing-library/react'; import { describeEachAppLayout, renderComponent, isDrawerClosed } from './utils'; import AppLayout, { AppLayoutProps } from '../../../lib/components/app-layout'; -describeEachAppLayout({ themes: ['classic', 'refresh', 'refresh-toolbar'] }, ({ theme }) => { +describeEachAppLayout({ themes: ['classic', 'refresh', 'refresh-toolbar'] }, () => { test('opens tools drawer', () => { let ref: AppLayoutProps.Ref | null = null; const { wrapper } = renderComponent( (ref = newRef)} />); @@ -56,24 +56,4 @@ describeEachAppLayout({ themes: ['classic', 'refresh', 'refresh-toolbar'] }, ({ expect(wrapper.find('#custom-button')!.getElement()).toEqual(document.activeElement); }); }); - - test('should not open partially controllable tools', () => { - const { wrapper } = renderComponent( - Click me} toolsOpen={false} /> - ); - - wrapper.findToolsToggle()!.click(); - - if (theme === 'refresh-toolbar') { - expect(wrapper.findTools()).toBeFalsy(); - } - - if (theme === 'refresh') { - expect(wrapper.findTools().getElement()).toHaveAttribute('aria-hidden', 'true'); - } - - if (theme === 'classic') { - expect(wrapper.findTools().getElement().style).not.toContain('width'); - } - }); }); diff --git a/src/app-layout/utils/use-drawers.ts b/src/app-layout/utils/use-drawers.ts index df4ae9668b..fdce5fd12e 100644 --- a/src/app-layout/utils/use-drawers.ts +++ b/src/app-layout/utils/use-drawers.ts @@ -198,9 +198,7 @@ export function useDrawers( } function onActiveDrawerChange(newDrawerId: string | null) { - if (newDrawerId !== TOOLS_DRAWER_ID) { - setActiveDrawerId(newDrawerId); - } + setActiveDrawerId(newDrawerId); if (newDrawerId) { onAddNewActiveDrawer?.(newDrawerId); } diff --git a/src/app-layout/visual-refresh-toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/index.tsx index ceb8832a03..e2c35c56db 100644 --- a/src/app-layout/visual-refresh-toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/index.tsx @@ -332,8 +332,6 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef void; onActiveDrawerResize: (detail: { id: string; size: number }) => void; onActiveGlobalDrawersChange: (newDrawerId: string) => void; - toolsOpen: boolean; - onToolsToggle: (value: boolean) => void; } diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx index b7e5ec1c39..1c8bf62ffb 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx @@ -38,9 +38,6 @@ interface DrawerTriggersProps { globalDrawers: ReadonlyArray; onActiveGlobalDrawersChange?: (newDrawerId: string) => void; - toolsOpen: boolean; - onToolsToggle: (value: boolean) => void; - splitPanelOpen?: boolean; splitPanelPosition?: AppLayoutProps.SplitPanelPreferences['position']; splitPanelToggleProps: SplitPanelToggleProps | undefined; @@ -65,7 +62,6 @@ export function DrawerTriggers({ globalDrawers, globalDrawersFocusControl, onActiveGlobalDrawersChange, - toolsOpen, }: DrawerTriggersProps) { const isMobile = useMobile(); const hasMultipleTriggers = drawers.length > 1; @@ -156,12 +152,11 @@ export function DrawerTriggers({ )} {visibleItems.slice(0, globalDrawersStartIndex).map(item => { const isForPreviousActiveDrawer = previousActiveLocalDrawerId?.current === item.id; - const isActive = item.id === TOOLS_DRAWER_ID ? toolsOpen : item.id === activeDrawerId; return ( onActiveDrawerChange?.(activeDrawerId !== item.id ? item.id : null)} ref={item.id === previousActiveLocalDrawerId.current ? drawersFocusRef : undefined} - selected={isActive} + selected={item.id === activeDrawerId} badge={item.badge} testId={`awsui-app-layout-trigger-${item.id}`} hasTooltip={true} diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx index 8351a00760..9aa7c1732c 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx @@ -97,8 +97,6 @@ export function AppLayoutToolbarImplementation({ setToolbarState, setToolbarHeight, globalDrawersFocusControl, - toolsOpen, - onToolsToggle, } = appLayoutInternals; const { ariaLabels, @@ -224,8 +222,6 @@ export function AppLayoutToolbarImplementation({ globalDrawers={globalDrawers?.filter(item => !!item.trigger) ?? []} activeGlobalDrawersIds={activeGlobalDrawersIds ?? []} onActiveGlobalDrawersChange={onActiveGlobalDrawersChange} - toolsOpen={toolsOpen} - onToolsToggle={onToolsToggle} /> )} From 0fe9d99a08dc2dbce59149ef6a11a4ba2cf93a71 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Wed, 9 Oct 2024 17:59:46 +0200 Subject: [PATCH 2/8] feat: Mobile breadcrumbs --- pages/app-layout/utils/content-blocks.tsx | 4 ++ pages/breadcrumb-group/mobile.page.tsx | 28 ++++++++ src/breadcrumb-group/implementation.tsx | 84 ++++++++++++++++++++++- src/breadcrumb-group/interfaces.ts | 7 ++ src/breadcrumb-group/styles.scss | 27 ++++++++ src/button-dropdown/interfaces.ts | 1 + src/button-dropdown/items-list.tsx | 2 +- 7 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 pages/breadcrumb-group/mobile.page.tsx diff --git a/pages/app-layout/utils/content-blocks.tsx b/pages/app-layout/utils/content-blocks.tsx index 8015fc5bd1..f8d73be248 100644 --- a/pages/app-layout/utils/content-blocks.tsx +++ b/pages/app-layout/utils/content-blocks.tsx @@ -21,6 +21,10 @@ export function Breadcrumbs() { items={[ { text: 'Home', href: '#' }, { text: 'Service', href: '#' }, + { + text: 'Service sdfjlsdk sdjfklds sdjfklds sdjfkldssdjfkldssdjfklds', + href: '#', + }, ]} /> ); diff --git a/pages/breadcrumb-group/mobile.page.tsx b/pages/breadcrumb-group/mobile.page.tsx new file mode 100644 index 0000000000..929137a4d4 --- /dev/null +++ b/pages/breadcrumb-group/mobile.page.tsx @@ -0,0 +1,28 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import * as React from 'react'; + +import BreadcrumbGroup from '~components/breadcrumb-group'; + +import ScreenshotArea from '../utils/screenshot-area'; + +export default function ButtonDropdownPage() { + return ( + +
+

BreadcrumbGroup mobile

+ +
+
+ ); +} diff --git a/src/breadcrumb-group/implementation.tsx b/src/breadcrumb-group/implementation.tsx index ec43c87212..e6a1719ecd 100644 --- a/src/breadcrumb-group/implementation.tsx +++ b/src/breadcrumb-group/implementation.tsx @@ -15,13 +15,19 @@ import InternalIcon from '../icon/internal'; import { getBaseProps } from '../internal/base-component'; import { fireCancelableEvent } from '../internal/events'; import { useMergeRefs } from '../internal/hooks/use-merge-refs'; +import { useMobile } from '../internal/hooks/use-mobile'; import { checkSafeUrl } from '../internal/utils/check-safe-url'; import { createWidgetizedComponent } from '../internal/widgets'; import { GeneratedAnalyticsMetadataBreadcrumbGroupClick, GeneratedAnalyticsMetadataBreadcrumbGroupComponent, } from './analytics-metadata/interfaces'; -import { BreadcrumbGroupProps, EllipsisDropdownProps, InternalBreadcrumbGroupProps } from './interfaces'; +import { + BreadcrumbGroupProps, + EllipsisDropdownProps, + InternalBreadcrumbGroupProps, + MobileDropdownProps, +} from './interfaces'; import { BreadcrumbItem } from './item/item'; import { getEventDetail, getItemsDisplayProperties } from './utils'; @@ -96,6 +102,61 @@ const EllipsisDropdown = ({ ); }; +const MobileDropdown = ({ + ariaLabel, + dropdownItems, + onDropdownItemClick, + onDropdownItemFollow, +}: MobileDropdownProps) => { + const i18n = useInternalI18n('breadcrumb-group'); + + return ( + + index === dropdownItems.length - 1 ? { ...item, href: undefined } : item + )} + onItemClick={onDropdownItemClick} + onItemFollow={onDropdownItemFollow} + customTriggerBuilder={props => ( + { + event.preventDefault(); + props.onClick(); + }} + ariaExpanded={props.isOpen} + aria-haspopup={true} + ariaLabel={ariaLabel} + variant="breadcrumb-group" + formAction="none" + > +
+ + + {dropdownItems[dropdownItems.length - 1].text} + +
+
+ )} + analyticsMetadataTransformer={metadata => { + if (metadata.detail?.id) { + delete metadata.detail.id; + } + if (metadata.detail?.position) { + metadata.detail.position = `${parseInt(metadata.detail.position as string, 10) + 1}`; + } + return metadata; + }} + /> + ); +}; + interface ItemsRefsType { ghost: Record; real: Record; @@ -129,6 +190,7 @@ export function BreadcrumbGroupImplementation(rect => rect.borderBoxWidth); const mergedRef = useMergeRefs(navRef, __internalRootRef); + const isMobile = useMobile(); const itemsRefs = useRef({ ghost: {}, real: {} }); const setBreadcrumb = (type: keyof ItemsRefsType, index: string, node: null | HTMLLIElement) => { @@ -241,6 +303,26 @@ export function BreadcrumbGroupImplementation { + const isLast = index === items.length - 1; + return { + id: index.toString(), // the first item doesn't get inside dropdown + text: item.text, + href: item.href || '#', + linkStyle: !isLast, + }; + })} + onDropdownItemClick={e => fireCancelableEvent(onClick, getEventDetail(getEventItem(e)), e)} + onDropdownItemFollow={e => fireCancelableEvent(onFollow, getEventDetail(getEventItem(e)), e)} + /> + ); + } + return (