Skip to content
Draft
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
45 changes: 45 additions & 0 deletions pages/annotation-context/with-iframe.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';

import { Box, SpaceBetween } from '~components';
import AnnotationContext from '~components/annotation-context';
import Hotspot from '~components/hotspot';

import { annotationContextStrings } from '../onboarding/i18n';
import tutorials from '../onboarding/tutorials';
import { IframeWrapper } from '../utils/iframe-wrapper';

const tutorial = tutorials(() => {})[0];

function IframeContent() {
return (
<AnnotationContext
currentTutorial={tutorial}
i18nStrings={annotationContextStrings}
onStartTutorial={() => {}}
onExitTutorial={() => {}}
>
<Box padding="m">
<SpaceBetween size="l">
<div>
Hotspot 1: <Hotspot hotspotId={tutorial.tasks[0].steps[0].hotspotId} />
</div>
<div>
Hotspot 2: <Hotspot hotspotId={tutorial.tasks[0].steps[2].hotspotId} />
</div>
</SpaceBetween>
</Box>
</AnnotationContext>
);
}

export default function () {
return (
<Box margin="m">
<h1>Annotation context in iframe</h1>
<p>Click the hotspot icons — the annotation popover should appear inside the iframe.</p>
<IframeWrapper id="annotation-iframe" AppComponent={IframeContent} />
</Box>
);
}
17 changes: 17 additions & 0 deletions pages/button-dropdown/disabled-reason-iframe.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';

import { Box } from '~components';

import { IframeWrapper } from '../utils/iframe-wrapper';
import { DisabledReasonDemo } from './disabled-reason.page';

export default function () {
return (
<Box margin="m">
<h1>ButtonDropdown disabled reason in iframe</h1>
<IframeWrapper id="button-dropdown-iframe" AppComponent={DisabledReasonDemo} />
</Box>
);
}
12 changes: 10 additions & 2 deletions pages/button-dropdown/disabled-reason.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,10 @@ export const selectableGroupItems: ButtonDropdownProps.Items = [
{ text: 'Action', id: 'action', disabled: false },
];

export default function DescriptionPage() {
export function DisabledReasonDemo() {
const [isRightAligned, setIsRightAligned] = useState(false);
return (
<>
<h1>Descriptions in ButtonDropdown</h1>
<label>
<input
type="checkbox"
Expand Down Expand Up @@ -184,3 +183,12 @@ export default function DescriptionPage() {
</>
);
}

export default function DescriptionPage() {
return (
<>
<h1>Descriptions in ButtonDropdown</h1>
<DisabledReasonDemo />
</>
);
}
17 changes: 17 additions & 0 deletions pages/button/disabled-reason-iframe.page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';

import { Box } from '~components';

import { IframeWrapper } from '../utils/iframe-wrapper';
import { DisabledReasonDemo } from './disabled-reason.page';

export default function () {
return (
<Box margin="m">
<h1>Button disabled reason in iframe</h1>
<IframeWrapper id="button-iframe" AppComponent={DisabledReasonDemo} />
</Box>
);
}
48 changes: 28 additions & 20 deletions pages/button/disabled-reason.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,39 @@ import ScreenshotArea from '../utils/screenshot-area';

import styles from './styles.scss';

export function DisabledReasonDemo() {
return (
<>
<Button variant="primary" disabled={true} disabledReason="disabled reason">
Primary
</Button>
<Button disabled={true} disabledReason="disabled reason">
Default
</Button>
<Button
disabled={true}
disabledReason="disabled reason"
href="https://smth.com"
data-testid="normal-button-with-href"
>
Button with href
</Button>
<Button
disabled={true}
disabledReason="disabled reason"
iconName="star"
ariaLabel="Disabled reason icon button"
/>
</>
);
}

export default function ButtonsScenario() {
return (
<article>
<ScreenshotArea>
<h1 className={styles.styledWrapper}>Buttons with disabled reason</h1>
<Button variant="primary" disabled={true} disabledReason="disabled reason">
Primary
</Button>
<Button disabled={true} disabledReason="disabled reason">
Default
</Button>
<Button
disabled={true}
disabledReason="disabled reason"
href="https://smth.com"
data-testid="normal-button-with-href"
>
Button with href
</Button>
<Button
disabled={true}
disabledReason="disabled reason"
iconName="star"
ariaLabel="Disabled reason icon button"
/>
<DisabledReasonDemo />
</ScreenshotArea>
</article>
);
Expand Down
29 changes: 26 additions & 3 deletions pages/table/with-iframe.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import zipObject from 'lodash/zipObject';

import { useCollection } from '@cloudscape-design/collection-hooks';

import { Box, NonCancelableCustomEvent, SpaceBetween, Table, TableProps, TextFilter } from '~components';
import { Box, Input, NonCancelableCustomEvent, SpaceBetween, Table, TableProps, TextFilter } from '~components';

import AppContext, { AppContextType } from '../app/app-context';
import { IframeWrapper } from '../utils/iframe-wrapper';
Expand All @@ -21,6 +21,21 @@ type PageContext = React.Context<

const allItems = generateItems(10);

const editableColumnsConfig: typeof columnsConfig = [
...columnsConfig,
{
id: 'type-edit',
header: 'Type (disabled edit)',
cell: item => item.type,
editConfig: {
editingCell: (item, { currentValue, setValue }) => (
<Input value={currentValue ?? item.type} onChange={event => setValue(event.detail.value)} />
),
disabledReason: () => 'Editing is disabled in iframe test',
},
},
];

export default function () {
const {
urlParams: { iframe = true },
Expand All @@ -34,7 +49,7 @@ export default function () {
}

function DemoTable() {
const [columns, setColumns] = useState(columnsConfig);
const [columns, setColumns] = useState(editableColumnsConfig);

function handleWidthChange(event: NonCancelableCustomEvent<TableProps.ColumnWidthsChangeDetail>) {
const widths = zipObject(
Expand Down Expand Up @@ -67,13 +82,21 @@ function DemoTable() {
<Table
items={items}
{...collectionProps}
ariaLabels={selectionLabels}
ariaLabels={{
...selectionLabels,
activateEditLabel: (column, item) => `Edit ${item.id} ${column.header}`,
cancelEditLabel: column => `Cancel editing ${column.header}`,
submitEditLabel: column => `Submit edit ${column.header}`,
submittingEditText: () => 'Submitting',
successfulEditLabel: () => 'Edit successful',
}}
stickyHeader={true}
resizableColumns={true}
enableKeyboardNavigation={true}
selectionType="multi"
columnDefinitions={columns}
onColumnWidthsChange={handleWidthChange}
submitEdit={() => {}}
/>
</SpaceBetween>
);
Expand Down
21 changes: 21 additions & 0 deletions src/annotation-context/__integ__/with-iframe.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { BasePageObject } from '@cloudscape-design/browser-test-tools/page-objects';
import useBrowser from '@cloudscape-design/browser-test-tools/use-browser';

import createWrapper from '../../../lib/components/test-utils/selectors';

const wrapper = createWrapper();

test(
'annotation popover is visible inside iframe',
useBrowser(async browser => {
const page = new BasePageObject(browser);
await browser.url('#/light/annotation-context/with-iframe');
await page.runInsideIframe('#annotation-iframe', true, async () => {
const annotationSelector = wrapper.findAnnotation().toSelector();
await page.waitForVisible(annotationSelector);
await expect(page.isDisplayed(annotationSelector)).resolves.toBe(true);
});
})
);
4 changes: 3 additions & 1 deletion 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 { usePortalContainer } from '../../internal/hooks/use-portal-container';
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 portalContainer = usePortalContainer(() => trackRef.current);

return (
<>
Expand All @@ -72,7 +74,7 @@ export function OpenAnnotation({
totalLocalSteps={totalLocalSteps}
taskLocalStepIndex={taskLocalStepIndex}
/>
<Portal>
<Portal container={portalContainer}>
<AnnotationPopover
trackRef={trackRef}
previousButtonEnabled={previousButtonEnabled}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,49 @@ const overlaps = (one: ElementRect, two: ElementRect) => {

return true;
};

const setupIframeTest = (testFn: (page: ButtonDropdownDisabledReasonPage) => Promise<void>) => {
return useBrowser(async browser => {
const page = new ButtonDropdownDisabledReasonPage(browser);
await browser.url('#/light/button-dropdown/disabled-reason-iframe');
await page.runInsideIframe('#button-dropdown-iframe', true, async () => {
await page.waitForVisible(page.findButtonDropdown().toSelector());
await page.openDropdown();
await testFn(page);
});
});
};

describe('Button Dropdown - Disabled Reason in iframe', () => {
it(
'shows tooltip on hover',
setupIframeTest(async page => {
await page.hoverElement(page.findButtonDropdown().findItemById('connect').toSelector());
await page.waitForVisible(page.findDisabledReason().toSelector());
expect(await page.getDisabledReason()).toEqual('Instance must be running.');
})
);

it(
'closes on escape',
setupIframeTest(async page => {
await page.hoverElement(page.findButtonDropdown().findItemById('connect').toSelector());
await page.waitForVisible(page.findDisabledReason().toSelector());
await page.keys('Escape');
await page.waitForAssertion(async () =>
expect(await page.isDisplayed(page.findDisabledReason().toSelector())).toBeFalsy()
);
})
);

it(
'shows tooltip on nested disabled elements',
setupIframeTest(async page => {
await page.click(page.findButtonDropdown().findExpandableCategoryById('settings').toSelector());
await page.waitForVisible(page.findDisabledReason().toSelector());
expect(await page.getDisabledReason()).toEqual(
'Instance must be running and not already be attached to an Auto Scaling Group.'
);
})
);
});
4 changes: 3 additions & 1 deletion src/button-dropdown/tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import React, { KeyboardEventHandler, useEffect, useRef, useState } from 'react'

import { Portal, useReducedMotion } from '@cloudscape-design/component-toolkit/internal';

import { usePortalContainer } from '../internal/hooks/use-portal-container';
import { usePortalModeClasses } from '../internal/hooks/use-portal-mode-classes';
import Arrow from '../popover/arrow';
import PopoverBody from '../popover/body';
Expand All @@ -23,12 +24,13 @@ export default function Tooltip({ children, content, position = 'right', classNa
const isReducedMotion = useReducedMotion(ref);
const { open, triggerProps } = useTooltipOpen(isReducedMotion ? 0 : DEFAULT_OPEN_TIMEOUT_IN_MS);
const portalClasses = usePortalModeClasses(ref);
const portalContainer = usePortalContainer(() => ref.current);

return (
<span ref={ref} {...triggerProps} className={className}>
{children}
{open && (
<Portal>
<Portal container={portalContainer}>
<span className={portalClasses}>
<PopoverContainer
size="small"
Expand Down
Loading
Loading