Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/annotation-context/__tests__/annotation-context.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,25 @@ test('trigger should have aria-expanded depending on open state', () => {
expect(hotspot.findTrigger().getElement().getAttribute('aria-expanded')).toBe('false');
});

test('propagates one-theme class to the portaled annotation when one theme is active', () => {
const themeRoot = document.createElement('div');
themeRoot.className = 'awsui-one-theme';
document.body.appendChild(themeRoot);

try {
const { wrapper } = renderAnnotationContext(<Hotspot hotspotId="first-hotspot" />);
expect(wrapper.findAnnotation()!.findContent().getElement().closest('.awsui-one-theme')).not.toBeNull();
} finally {
themeRoot.remove();
}
});

test('does not stamp one-theme class on the portaled annotation when one theme is inactive', () => {
const { wrapper } = renderAnnotationContext(<Hotspot hotspotId="first-hotspot" />);

expect(wrapper.findAnnotation()!.findContent().getElement().closest('.awsui-one-theme')).toBeNull();
});

test('annotation should have be labeled by header and step counter', () => {
const { wrapper } = renderAnnotationContext(
<>
Expand Down
40 changes: 22 additions & 18 deletions src/annotation-context/annotation/open-annotation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, { useRef } from 'react';
import { Portal } from '@cloudscape-design/component-toolkit/internal';

import { HotspotProps } from '../../hotspot/interfaces';
import { useOneThemePortalClass } from '../../internal/hooks/use-portal-mode-classes';
import { AnnotationContextProps } from '../interfaces';
import { AnnotationPopover } from './annotation-popover';
import AnnotationTrigger from './annotation-trigger';
Expand Down Expand Up @@ -61,6 +62,7 @@ export function OpenAnnotation({
i18nStrings,
}: AnnotationProps) {
const trackRef = useRef<HTMLButtonElement>(null);
const portalClasses = useOneThemePortalClass();

return (
<>
Expand All @@ -73,24 +75,26 @@ export function OpenAnnotation({
taskLocalStepIndex={taskLocalStepIndex}
/>
<Portal>
<AnnotationPopover
trackRef={trackRef}
previousButtonEnabled={previousButtonEnabled}
showPreviousButton={showPreviousButton}
showFinishButton={showFinishButton}
totalLocalSteps={totalLocalSteps}
i18nStrings={i18nStrings}
nextButtonEnabled={nextButtonEnabled}
onDismiss={onDismiss}
onFinish={onFinish}
onNextButtonClick={onNextButtonClick}
onPreviousButtonClick={onPreviousButtonClick}
taskLocalStepIndex={taskLocalStepIndex}
direction={direction}
title={title}
content={content}
alert={alert}
/>
<span className={portalClasses}>
<AnnotationPopover
trackRef={trackRef}
previousButtonEnabled={previousButtonEnabled}
showPreviousButton={showPreviousButton}
showFinishButton={showFinishButton}
totalLocalSteps={totalLocalSteps}
i18nStrings={i18nStrings}
nextButtonEnabled={nextButtonEnabled}
onDismiss={onDismiss}
onFinish={onFinish}
onNextButtonClick={onNextButtonClick}
onPreviousButtonClick={onPreviousButtonClick}
taskLocalStepIndex={taskLocalStepIndex}
direction={direction}
title={title}
content={content}
alert={alert}
/>
</span>
</Portal>
</>
);
Expand Down
19 changes: 19 additions & 0 deletions src/drawer/__tests__/drawer-position-and-placement.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,25 @@ describe('position=fixed', () => {
);
expect(el).toHaveStyle({ insetInlineStart: '8px', insetBlockStart: '4px', insetBlockEnd: '12px' });
});

test('propagates one-theme class to the fixed drawer when one theme is active', () => {
const themeRoot = document.createElement('div');
themeRoot.className = 'awsui-one-theme';
document.body.appendChild(themeRoot);

try {
const el = getDrawerElement(<Drawer position="fixed" />);
expect(el).toHaveClass('awsui-one-theme');
} finally {
themeRoot.remove();
}
});

test('does not stamp one-theme class on the fixed drawer when one theme is inactive', () => {
const el = getDrawerElement(<Drawer position="fixed" />);

expect(el).not.toHaveClass('awsui-one-theme');
});
});

describe('position=sticky', () => {
Expand Down
5 changes: 4 additions & 1 deletion src/drawer/implementation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { getAllFocusables, isFocusable } from '../internal/components/focus-lock
import { fireNonCancelableEvent } from '../internal/events';
import { InternalBaseComponentProps } from '../internal/hooks/use-base-component';
import { useEffectOnUpdate } from '../internal/hooks/use-effect-on-update';
import { useOneThemePortalClass } from '../internal/hooks/use-portal-mode-classes';
import { createWidgetizedComponent } from '../internal/widgets';
import InternalLiveRegion from '../live-region/internal';
import InternalStatusIndicator from '../status-indicator/internal';
Expand Down Expand Up @@ -63,6 +64,7 @@ export function DrawerImplementation({
const returnFocusTargetRef = useRef<HTMLElement | null>(null);

const baseProps = getBaseProps(restProps);
const portalClasses = useOneThemePortalClass();
const isToolbar = useAppLayoutToolbarDesignEnabled();
const i18n = useInternalI18n('drawer');
const positionStyles = getPositionStyles({ position, placement, offset, stickyOffset, zIndex });
Expand All @@ -84,7 +86,8 @@ export function DrawerImplementation({
isToolbar && styles['with-toolbar'],
!!footer && styles['with-footer'],
closeAction && !hideCloseAction && styles['has-close-action'],
positionStyles.className
positionStyles.className,
position === 'fixed' && portalClasses
),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,40 @@ test('resumes position updates when enabled after being disabled', async () => {
expect(portalOverlay.style.height).toBe('20px');
});
});

test('propagates one-theme class to the portal overlay when one theme is active', async () => {
const themeRoot = document.createElement('div');
themeRoot.className = 'awsui-one-theme';
document.body.appendChild(themeRoot);
const mockRef = createMockRef();

try {
render(
<PortalOverlay track={mockRef} isDisabled={false}>
<div id="overlay">Overlay</div>
</PortalOverlay>
);

const portalOverlay = document.querySelector<HTMLElement>(`.${styles['portal-overlay']}`)!;
await waitFor(() => {
expect(portalOverlay).toHaveClass('awsui-one-theme');
});
} finally {
themeRoot.remove();
}
});

test('does not stamp one-theme class on the portal overlay when one theme is inactive', async () => {
const mockRef = createMockRef();

render(
<PortalOverlay track={mockRef} isDisabled={false}>
<div id="overlay">Overlay</div>
</PortalOverlay>
);

const portalOverlay = document.querySelector<HTMLElement>(`.${styles['portal-overlay']}`)!;
await waitFor(() => {
expect(portalOverlay).not.toHaveClass('awsui-one-theme');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
Portal,
} from '@cloudscape-design/component-toolkit/internal';

import { useOneThemePortalClass } from '../../hooks/use-portal-mode-classes';

import styles from './styles.css.js';

export default function PortalOverlay({
Expand All @@ -25,6 +27,7 @@ export default function PortalOverlay({
}) {
const ref = useRef<HTMLSpanElement | null>(null);
const [container, setContainer] = useState<HTMLDivElement | null>(null);
const portalClasses = useOneThemePortalClass();

useLayoutEffect(() => {
if (track.current) {
Expand Down Expand Up @@ -90,7 +93,7 @@ export default function PortalOverlay({
<span
ref={ref}
data-awsui-referrer-id={referrerId}
className={clsx(styles['portal-overlay'], isDisabled && styles['portal-overlay-disabled'])}
className={clsx(portalClasses, styles['portal-overlay'], isDisabled && styles['portal-overlay-disabled'])}
>
<span className={styles['portal-overlay-contents']}>{children}</span>
</span>
Expand Down
104 changes: 104 additions & 0 deletions src/internal/components/sortable-area/__tests__/portal-mode.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 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 SortableArea, { SortableAreaProps } from '../../../../../lib/components/internal/components/sortable-area';
import useDragAndDropReorder from '../../../../../lib/components/internal/components/sortable-area/use-drag-and-drop-reorder';

import styles from '../../../../../lib/components/internal/components/sortable-area/styles.css.js';

jest.mock('@dnd-kit/core', () => ({
...jest.requireActual('@dnd-kit/core'),
DndContext: ({ children }: { children: React.ReactNode }) => <>{children}</>,
DragOverlay: ({ children, className }: { children: React.ReactNode; className?: string }) => (
<div className={className}>{children}</div>
),
}));

jest.mock('@dnd-kit/sortable', () => ({
...jest.requireActual('@dnd-kit/sortable'),
SortableContext: ({ children }: { children: React.ReactNode }) => <>{children}</>,
useSortable: () => ({
isDragging: false,
isSorting: false,
listeners: {},
setNodeRef: jest.fn(),
transform: null,
attributes: {
'aria-describedby': 'drag-handle',
'aria-disabled': false,
},
}),
}));

jest.mock('../../../../../lib/components/internal/components/sortable-area/use-drag-and-drop-reorder');

interface Item {
id: string;
label: string;
}

const items: readonly Item[] = [
{ id: '1', label: 'First' },
{ id: '2', label: 'Second' },
];
const itemDefinition: SortableAreaProps.ItemDefinition<Item> = { id: item => item.id, label: item => item.label };
const mockedUseDragAndDropReorder = useDragAndDropReorder as jest.MockedFunction<typeof useDragAndDropReorder>;

beforeEach(() => {
mockedUseDragAndDropReorder.mockReturnValue({
activeItemId: '1',
setActiveItemId: jest.fn(),
collisionDetection: jest.fn(),
coordinateGetter: jest.fn(),
handleKeyDown: jest.fn(),
sensors: [],
isKeyboard: { current: false },
});
});

test('propagates one-theme class to the drag overlay when one theme is active', () => {
const themeRoot = document.createElement('div');
themeRoot.className = 'awsui-one-theme';
document.body.appendChild(themeRoot);

try {
render(
<SortableArea
items={items}
itemDefinition={itemDefinition}
onItemsChange={() => {}}
renderItem={({ item, className, ref }) => (
<div ref={ref} className={className}>
{item.label}
</div>
)}
i18nStrings={{}}
/>
);

expect(document.querySelector(`.${styles['drag-overlay']}`)).toHaveClass('awsui-one-theme');
} finally {
themeRoot.remove();
}
});

test('does not stamp one-theme class on the drag overlay when one theme is inactive', () => {
render(
<SortableArea
items={items}
itemDefinition={itemDefinition}
onItemsChange={() => {}}
renderItem={({ item, className, ref }) => (
<div ref={ref} className={className}>
{item.label}
</div>
)}
i18nStrings={{}}
/>
);

expect(document.querySelector(`.${styles['drag-overlay']}`)).not.toHaveClass('awsui-one-theme');
});
8 changes: 7 additions & 1 deletion src/internal/components/sortable-area/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import clsx from 'clsx';
import { Portal } from '@cloudscape-design/component-toolkit/internal';

import { fireNonCancelableEvent } from '../../events';
import { useOneThemePortalClass } from '../../hooks/use-portal-mode-classes';
import { joinStrings } from '../../utils/strings';
import { SortableAreaProps } from './interfaces';
import { EventName } from './keyboard-sensor/utilities/events';
Expand Down Expand Up @@ -37,6 +38,7 @@ export default function SortableArea<Item>({
const isDragging = activeItemId !== null;
const announcements = useLiveAnnouncements({ items, itemDefinition, isDragging, ...i18nStrings });
const portalContainer = usePortalContainer();
const portalClasses = useOneThemePortalClass();
return (
<DndContext
sensors={sensors}
Expand Down Expand Up @@ -84,7 +86,11 @@ export default function SortableArea<Item>({
{/* Make sure that the drag overlay is above the modal by assigning the z-index as inline style
so that it prevails over dnd-kit's inline z-index of 999 */}
<DragOverlay
className={clsx(styles['drag-overlay'], styles[`drag-overlay-${getBorderRadiusVariant(itemDefinition)}`])}
className={clsx(
portalClasses,
styles['drag-overlay'],
styles[`drag-overlay-${getBorderRadiusVariant(itemDefinition)}`]
)}
dropAnimation={null}
style={{ zIndex: 5000 }}
transition={isKeyboard.current ? 'transform 250ms' : ''}
Expand Down
33 changes: 33 additions & 0 deletions src/internal/components/tooltip/__tests__/tooltip.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,37 @@ describe('Tooltip', () => {
expect(keydownEvent.stopPropagation).toHaveBeenCalled();
expect(onDismiss).toHaveBeenCalled();
});

it('propagates one-theme class to the portaled tooltip when one theme is active', () => {
const themeRoot = document.createElement('div');
themeRoot.className = 'awsui-one-theme';
document.body.appendChild(themeRoot);
const trackRef = React.createRef<HTMLDivElement>();

try {
render(
<>
<div ref={trackRef} />
<Tooltip trackRef={trackRef} value="Value" onDismiss={() => {}} />
</>
);

expect(createWrapper().findByClassName(tooltipStyles.root)!.getElement()).toHaveClass('awsui-one-theme');
} finally {
themeRoot.remove();
}
});

it('does not stamp one-theme class on the portaled tooltip when one theme is inactive', () => {
const trackRef = React.createRef<HTMLDivElement>();

render(
<>
<div ref={trackRef} />
<Tooltip trackRef={trackRef} value="Value" onDismiss={() => {}} />
</>
);

expect(createWrapper().findByClassName(tooltipStyles.root)!.getElement()).not.toHaveClass('awsui-one-theme');
});
});
Loading
Loading