Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow dropdowns of autosuggest, select, and multiselect to expand beyond their trigger width #1423

Merged
merged 9 commits into from
Aug 14, 2023
Merged
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
3 changes: 2 additions & 1 deletion pages/autosuggest/virtual-resize.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ declare const window: ExtendedWindow;

const options = [
{
value: 'A very long option label that wraps upon resizing',
value:
'A very very very very very very very very very very very very very long option label that wraps upon resizing',
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extended this string so that it still wraps when needed by the integration test.

tags: ['tag1', 'tag2'],
filteringTags: ['bla', 'opt'],
description: 'description1',
Expand Down
3 changes: 2 additions & 1 deletion pages/select/virtual-resize.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ declare const window: ExtendedWindow;

const options = [
{
value: 'A very long option label that wraps upon resizing',
value:
'A very very very very very very very very very very very very very long option label that wraps upon resizing',
tags: ['tag1', 'tag2'],
filteringTags: ['bla', 'opt'],
description: 'description1',
Expand Down
19 changes: 18 additions & 1 deletion src/internal/__tests__/breakpoints.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import { getMatchingBreakpoint, matchBreakpointMapping } from '../breakpoints';
import { getMatchingBreakpoint, matchBreakpointMapping, getBreakpointValue } from '../breakpoints';

describe('getMatchingBreakpoint', () => {
it('returns the correct breakpoint value', () => {
Expand Down Expand Up @@ -45,3 +45,20 @@ describe('matchBreakpointMapping', () => {
expect(matchBreakpointMapping({}, 'xl')).toBeNull();
});
});

describe('getBreakpointValue', () => {
it.each([
['xl', 1840],
['l', 1320],
['m', 1120],
['s', 912],
['xs', 688],
['xxs', 465],
] as const)('returns correct value for %s', (breakpoint, value) => {
expect(getBreakpointValue(breakpoint)).toBe(value);
});

it('returns -1 for the default breakpoint', () => {
expect(getBreakpointValue('default')).toBe(-1);
});
});
4 changes: 4 additions & 0 deletions src/internal/breakpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ export function getMatchingBreakpoint(width: number, breakpointFilter?: readonly
}
return 'default';
}

export function getBreakpointValue(breakpoint: Breakpoint): number {
return BREAKPOINT_MAPPING.find(bp => bp[0] === breakpoint)![1];
}
1 change: 1 addition & 0 deletions src/internal/components/autosuggest-input/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ const AutosuggestInput = React.forwardRef(
<Dropdown
minWidth={dropdownWidth}
stretchWidth={!dropdownWidth}
stretchBeyondTriggerWidth={true}
contentKey={dropdownContentKey}
onFocus={handleFocus}
onBlur={handleBlur}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ describe('Dropdown and trigger element alignment', () => {
const dropdownBox = await page.getBoundingBox(wrapper.findDropdown({ expandToViewport }).toSelector());
const triggerBox = await page.getBoundingBox(wrapper.findNativeInput().toSelector());
expect(dropdownBox.left).toEqual(triggerBox.left);

// TODO: Remove the condition once AWSUI-16369 is resolved
if (!expandToViewport) {
expect(dropdownBox.width).toEqual(triggerBox.width);
}
expect(dropdownBox.width).toBeGreaterThanOrEqual(triggerBox.width);
})();
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,17 @@ describe('getDropdownPosition', () => {
test('prefers dropping down by default', () => {
const trigger = getSizedElement(100, 50, 300, 100);
const dropdown = getSizedElement(100, 300);
expect(getDropdownPosition(trigger, dropdown, [windowSize])).toEqual(defaults);
expect(
getDropdownPosition({ triggerElement: trigger, dropdownElement: dropdown, overflowParents: [windowSize] })
).toEqual(defaults);
});

test('drops up if space above is not enough', () => {
const trigger = getSizedElement(100, 50, 900, 100);
const dropdown = getSizedElement(100, 300);
expect(getDropdownPosition(trigger, dropdown, [windowSize])).toEqual({
expect(
getDropdownPosition({ triggerElement: trigger, dropdownElement: dropdown, overflowParents: [windowSize] })
).toEqual({
...defaults,
dropUp: true,
height: '853px',
Expand All @@ -45,7 +49,9 @@ describe('getDropdownPosition', () => {
test('drops left if dropdown is too wide', () => {
const trigger = getSizedElement(100, 50, 300, 500);
const dropdown = getSizedElement(600, 400);
expect(getDropdownPosition(trigger, dropdown, [windowSize])).toEqual({
expect(
getDropdownPosition({ triggerElement: trigger, dropdownElement: dropdown, overflowParents: [windowSize] })
).toEqual({
...defaults,
dropLeft: true,
width: '550px',
Expand All @@ -55,7 +61,9 @@ describe('getDropdownPosition', () => {
test('falls back to central position when there is not enough space from both sides', () => {
const trigger = getSizedElement(900, 50, 300, 50);
const dropdown = getSizedElement(900, 400);
expect(getDropdownPosition(trigger, dropdown, [windowSize])).toEqual({
expect(
getDropdownPosition({ triggerElement: trigger, dropdownElement: dropdown, overflowParents: [windowSize] })
).toEqual({
...defaults,
width: '900px',
});
Expand All @@ -64,7 +72,14 @@ describe('getDropdownPosition', () => {
test('supports dropdown minWidth override', () => {
const trigger = getSizedElement(500, 50, 300, 100);
const dropdown = getSizedElement(200, 400);
expect(getDropdownPosition(trigger, dropdown, [windowSize], 300)).toEqual({
expect(
getDropdownPosition({
triggerElement: trigger,
dropdownElement: dropdown,
overflowParents: [windowSize],
minWidth: 300,
})
).toEqual({
...defaults,
width: '300px',
});
Expand All @@ -73,7 +88,14 @@ describe('getDropdownPosition', () => {
test('dropdown can grow beyond the minWidth', () => {
const trigger = getSizedElement(500, 50, 300, 100);
const dropdown = getSizedElement(600, 400);
expect(getDropdownPosition(trigger, dropdown, [windowSize], 300)).toEqual({
expect(
getDropdownPosition({
triggerElement: trigger,
dropdownElement: dropdown,
overflowParents: [windowSize],
minWidth: 300,
})
).toEqual({
...defaults,
width: '600px',
});
Expand All @@ -82,7 +104,14 @@ describe('getDropdownPosition', () => {
test('minWidth cannot be more than trigger width', () => {
const trigger = getSizedElement(200, 50, 300, 100);
const dropdown = getSizedElement(100, 400);
expect(getDropdownPosition(trigger, dropdown, [windowSize], 300)).toEqual({
expect(
getDropdownPosition({
triggerElement: trigger,
dropdownElement: dropdown,
overflowParents: [windowSize],
minWidth: 300,
})
).toEqual({
...defaults,
width: '200px',
});
Expand All @@ -92,7 +121,9 @@ describe('getDropdownPosition', () => {
const trigger = getSizedElement(100, 50, 300, windowSize.width - 110);
const dropdown = getSizedElement(100, 400);

expect(getDropdownPosition(trigger, dropdown, [windowSize])).toEqual({
expect(
getDropdownPosition({ triggerElement: trigger, dropdownElement: dropdown, overflowParents: [windowSize] })
).toEqual({
...defaults,
dropLeft: true, // TODO: this value is incorrect, should be fixed, see AWSUI-16369 for details
});
Expand All @@ -101,9 +132,17 @@ describe('getDropdownPosition', () => {
test('takes parent overflow elements when they are found', () => {
const trigger = getSizedElement(100, 50, 300, 100);
const dropdown = getSizedElement(100, 300);
expect(getDropdownPosition(trigger, dropdown, [windowSize])).toEqual(defaults);
expect(
getDropdownPosition({ triggerElement: trigger, dropdownElement: dropdown, overflowParents: [windowSize] })
).toEqual(defaults);
const scrollableContainer = { top: 100, left: 0, height: 400, width: 400 };
expect(getDropdownPosition(trigger, dropdown, [windowSize, scrollableContainer])).toEqual({
expect(
getDropdownPosition({
triggerElement: trigger,
dropdownElement: dropdown,
overflowParents: [windowSize, scrollableContainer],
})
).toEqual({
...defaults,
dropUp: true,
height: '140px',
Expand All @@ -113,7 +152,14 @@ describe('getDropdownPosition', () => {
test('adjusts left offset if preferCenter=true', () => {
const trigger = getSizedElement(100, 50, 300, 100);
const dropdown = getSizedElement(200, 300);
expect(getDropdownPosition(trigger, dropdown, [windowSize], undefined, true)).toEqual({
expect(
getDropdownPosition({
triggerElement: trigger,
dropdownElement: dropdown,
overflowParents: [windowSize],
preferCenter: true,
})
).toEqual({
...defaults,
width: '200px',
left: '-50px',
Expand All @@ -123,9 +169,69 @@ describe('getDropdownPosition', () => {
test('does not change offset if preferCenter=true, but it does not fit', () => {
const trigger = getSizedElement(100, 50, 300, 15);
const dropdown = getSizedElement(200, 300);
expect(getDropdownPosition(trigger, dropdown, [windowSize], undefined, true)).toEqual({
expect(
getDropdownPosition({
triggerElement: trigger,
dropdownElement: dropdown,
overflowParents: [windowSize],
preferCenter: true,
})
).toEqual({
...defaults,
width: '200px',
});
});

describe('with stretchBeyondTriggerWidth=true', () => {
test('can expand beyond trigger width', () => {
const triggerElement = getSizedElement(100, 50, 300, 15);
const dropdownElement = getSizedElement(200, 300);

expect(
getDropdownPosition({
triggerElement,
dropdownElement,
overflowParents: [windowSize],
stretchBeyondTriggerWidth: true,
})
).toEqual({
...defaults,
width: '200px',
});
});

test('cannot expand beyond the XXS breakpoint', () => {
const triggerElement = getSizedElement(100, 50, 300, 15);
const dropdownElement = getSizedElement(1000, 300);

expect(
getDropdownPosition({
triggerElement,
dropdownElement,
overflowParents: [windowSize],
stretchBeyondTriggerWidth: true,
})
).toEqual({
...defaults,
width: '465px',
});
});

test('will always grow to the width of the trigger if possible', () => {
const triggerElement = getSizedElement(700, 50, 300, 15);
const dropdownElement = getSizedElement(400, 300);

expect(
getDropdownPosition({
triggerElement,
dropdownElement,
overflowParents: [windowSize],
stretchBeyondTriggerWidth: true,
})
).toEqual({
...defaults,
width: '700px',
});
});
});
});
Loading
Loading