diff --git a/pages/modal/with-dropdowns.page.tsx b/pages/modal/with-dropdowns.page.tsx new file mode 100644 index 0000000000..833b601824 --- /dev/null +++ b/pages/modal/with-dropdowns.page.tsx @@ -0,0 +1,186 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useState } from 'react'; +import { + Modal, + Button, + SpaceBetween, + Autosuggest, + ButtonDropdown, + DatePicker, + DateRangePicker, + DateRangePickerProps, + Multiselect, + MultiselectProps, + Popover, +} from '~components'; + +export default function () { + const [visible, setVisible] = useState(false); + const [value, setValue] = useState(''); + const [dateRangeValue, setDateRangeValue] = useState(null); + const [selectedOptions, setSelectedOptions] = useState([ + { + label: 'Option 1', + value: '1', + description: 'This is a description', + }, + ]); + + return ( +
+

Simple modal

+ + setVisible(false)} + closeAriaLabel="Close modal" + footer={ + + + + + } + > + + setValue(detail.value)} + value={value} + options={[ + { value: 'Suggestion 1' }, + { value: 'Suggestion 2' }, + { value: 'Suggestion 3' }, + { value: 'Suggestion 4' }, + ]} + ariaLabel="Autosuggest example with suggestions" + placeholder="Enter value" + empty="No matches found" + /> + + + Short + + + setValue(detail.value)} + value={value} + openCalendarAriaLabel={selectedDate => + 'Choose certificate expiry date' + (selectedDate ? `, selected date is ${selectedDate}` : '') + } + placeholder="YYYY/MM/DD" + /> + + setDateRangeValue(detail.value)} + value={dateRangeValue} + relativeOptions={[ + { + key: 'previous-5-minutes', + amount: 5, + unit: 'minute', + type: 'relative', + }, + { + key: 'previous-30-minutes', + amount: 30, + unit: 'minute', + type: 'relative', + }, + { + key: 'previous-1-hour', + amount: 1, + unit: 'hour', + type: 'relative', + }, + { + key: 'previous-6-hours', + amount: 6, + unit: 'hour', + type: 'relative', + }, + ]} + isValidRange={range => { + if (range?.type === 'absolute') { + const [startDateWithoutTime] = range.startDate.split('T'); + const [endDateWithoutTime] = range.endDate.split('T'); + if (!startDateWithoutTime || !endDateWithoutTime) { + return { + valid: false, + errorMessage: + 'The selected date range is incomplete. Select a start and end date for the date range.', + }; + } + } + return { valid: true }; + }} + i18nStrings={{}} + placeholder="Filter by a date and time range" + /> + + setSelectedOptions(detail.selectedOptions)} + options={[ + { + label: 'Option 1', + value: '1', + description: 'This is a description', + }, + { + label: 'Option 2', + value: '2', + iconName: 'unlocked', + labelTag: 'This is a label tag', + }, + { + label: 'Option 3 (disabled)', + value: '3', + iconName: 'share', + tags: ['Tags go here', 'Tag1', 'Tag2'], + disabled: true, + }, + { + label: 'Option 4', + value: '4', + filteringTags: ['filtering', 'tags', 'these are filtering tags'], + }, + { label: 'Option 5', value: '5' }, + ]} + placeholder="Choose options" + /> + + + Error + + + + + + + +
+ ); +} diff --git a/src/button-dropdown/utils/use-button-dropdown.ts b/src/button-dropdown/utils/use-button-dropdown.ts index b9f9af8d62..3b8ec0060c 100644 --- a/src/button-dropdown/utils/use-button-dropdown.ts +++ b/src/button-dropdown/utils/use-button-dropdown.ts @@ -161,6 +161,9 @@ export function useButtonDropdown({ onReturnFocus(); closeDropdown(); event.preventDefault(); + if (isOpen) { + event.stopPropagation(); + } break; } case KeyCode.tab: { diff --git a/src/date-picker/index.tsx b/src/date-picker/index.tsx index 5d810363d8..2d07dfd398 100644 --- a/src/date-picker/index.tsx +++ b/src/date-picker/index.tsx @@ -89,6 +89,7 @@ const DatePicker = React.forwardRef( const onWrapperKeyDownHandler = (event: React.KeyboardEvent) => { if (event.keyCode === KeyCode.escape && isDropDownOpen) { + event.stopPropagation(); buttonRef.current?.focus(); setIsDropDownOpen(false); } diff --git a/src/date-range-picker/index.tsx b/src/date-range-picker/index.tsx index 77f00015ea..5ff12e1345 100644 --- a/src/date-range-picker/index.tsx +++ b/src/date-range-picker/index.tsx @@ -146,6 +146,9 @@ const DateRangePicker = React.forwardRef( const onWrapperKeyDownHandler = (event: React.KeyboardEvent) => { if (event.keyCode === KeyCode.escape) { + if (isDropDownOpen) { + event.stopPropagation(); + } closeDropdown(true); } }; diff --git a/src/internal/components/autosuggest-input/index.tsx b/src/internal/components/autosuggest-input/index.tsx index 8a4d1fcbe6..7b878ec79e 100644 --- a/src/internal/components/autosuggest-input/index.tsx +++ b/src/internal/components/autosuggest-input/index.tsx @@ -171,8 +171,10 @@ const AutosuggestInput = React.forwardRef( } case KeyCode.escape: { if (open) { + event.stopPropagation(); closeDropdown(); } else if (value) { + event.stopPropagation(); fireNonCancelableEvent(onChange, { value: '' }); } event.preventDefault(); diff --git a/src/internal/components/options-list/__tests__/use-keyboard.test.ts b/src/internal/components/options-list/__tests__/use-keyboard.test.ts index b8f3ae36ef..541c24440f 100644 --- a/src/internal/components/options-list/__tests__/use-keyboard.test.ts +++ b/src/internal/components/options-list/__tests__/use-keyboard.test.ts @@ -22,7 +22,7 @@ const menuKeys: any = [ ['enter', KeyCode.enter], ['space', KeyCode.space], ].reduce((acc, [name, keyCode]) => { - acc[name] = { detail: { keyCode }, preventDefault: jest.fn() }; + acc[name] = { detail: { keyCode }, preventDefault: jest.fn(), stopPropagation: jest.fn() }; return acc; }, {}); diff --git a/src/internal/components/options-list/utils/use-keyboard.ts b/src/internal/components/options-list/utils/use-keyboard.ts index aab2350d10..a387f697f0 100644 --- a/src/internal/components/options-list/utils/use-keyboard.ts +++ b/src/internal/components/options-list/utils/use-keyboard.ts @@ -44,6 +44,7 @@ export const useMenuKeyboard: UseMenuKeyboard = ({ goEnd(); break; case KeyCode.escape: + e.stopPropagation(); closeDropdown(); break; case KeyCode.enter: diff --git a/src/modal/__tests__/modal.test.tsx b/src/modal/__tests__/modal.test.tsx index ea99aa7453..a1046d0671 100644 --- a/src/modal/__tests__/modal.test.tsx +++ b/src/modal/__tests__/modal.test.tsx @@ -3,6 +3,14 @@ import * as React from 'react'; import { act, render, fireEvent } from '@testing-library/react'; import Modal, { ModalProps } from '../../../lib/components/modal'; +import Select from '../../../lib/components/select'; +import Multiselect from '../../../lib/components/multiselect'; +import Autosuggest from '../../../lib/components/autosuggest'; +import DatePicker from '../../../lib/components/date-picker'; +import DateRangePicker from '../../../lib/components/date-range-picker'; +import ButtonDropdown from '../../../lib/components/button-dropdown'; +import Popover from '../../../lib/components/popover'; +import StatusIndicator from '../../../lib/components/status-indicator'; import createWrapper, { ElementWrapper, ModalWrapper } from '../../../lib/components/test-utils/dom'; import styles from '../../../lib/components/modal/styles.css.js'; @@ -184,6 +192,181 @@ describe('Modal component', () => { expect(onDismissSpy).toHaveBeenCalled(); }); + + it('does not dismiss modal if ESC pressed with child select opened', () => { + const onDismissSpy = jest.fn(); + const select = ( +