Skip to content

Commit edc99ef

Browse files
authored
Merge pull request #673 from lumapps/fix/nested-clickaway
fix(click-away): fix nested click away using React ref
2 parents cce04a2 + 3204dd6 commit edc99ef

File tree

10 files changed

+248
-182
lines changed

10 files changed

+248
-182
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- AlertDialog: props forwarding on the confirm and cancel buttons.
1313

14+
### Fixed
15+
16+
- Fix Select/Dropdown inside Dialog not closing when clicking outside.
17+
1418
## [1.0.17][] - 2021-05-27
1519

1620
### Fixed

packages/lumx-react/src/components/dialog/Dialog.stories.tsx

Lines changed: 40 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { mdiClose } from '@lumx/icons';
22
import {
33
Button,
44
Checkbox,
5+
DatePickerField,
56
Emphasis,
7+
FlexBox,
68
IconButton,
79
List,
810
ListItem,
@@ -217,14 +219,16 @@ export const DialogWithFocusableElements = ({ theme }: any) => {
217219
const inputRef = useRef(null);
218220

219221
const selectChoices = ['First item', 'Second item', 'Third item'];
220-
const [value, setValue] = React.useState<string>('');
222+
const [value, setValue] = React.useState<string>(selectChoices[0]);
221223
const [isSelectOpen, setSelectOpen] = useState(false);
222224
const toggleSelect = () => setSelectOpen(!isSelectOpen);
223225
const closeSelect = () => setSelectOpen(false);
224226
const selectItem = (item: any) => () => {
225227
closeSelect();
226228
setValue(item);
227229
};
230+
const [date, setDate] = useState<Date | undefined>(new Date('2020-05-18'));
231+
228232
return (
229233
<>
230234
{button}
@@ -258,27 +262,41 @@ export const DialogWithFocusableElements = ({ theme }: any) => {
258262
label="Checkbox input"
259263
/>
260264

261-
<Select
262-
className="lumx-spacing-margin-bottom-huge"
263-
isOpen={isSelectOpen}
264-
value={value}
265-
label="Select label"
266-
onInputClick={toggleSelect}
267-
onDropdownClose={closeSelect}
268-
>
269-
<List isClickable>
270-
{selectChoices.map((choice) => (
271-
<ListItem
272-
key={choice}
273-
isSelected={value === choice}
274-
onItemSelected={selectItem(choice)}
275-
size={Size.tiny}
276-
>
277-
{choice}
278-
</ListItem>
279-
))}
280-
</List>
281-
</Select>
265+
<FlexBox orientation="horizontal">
266+
<DatePickerField
267+
locale="fr"
268+
label="Start date"
269+
placeholder="Pick a date"
270+
theme={theme}
271+
onChange={setDate}
272+
value={date}
273+
nextButtonProps={{ label: 'Next month' }}
274+
previousButtonProps={{ label: 'Previous month' }}
275+
/>
276+
277+
<Select
278+
className="lumx-spacing-margin-left-huge"
279+
isOpen={isSelectOpen}
280+
value={value}
281+
label="Select label"
282+
onInputClick={toggleSelect}
283+
onDropdownClose={closeSelect}
284+
>
285+
<List isClickable>
286+
{selectChoices.map((choice) => (
287+
<ListItem
288+
key={choice}
289+
isSelected={value === choice}
290+
onItemSelected={selectItem(choice)}
291+
size={Size.tiny}
292+
>
293+
{choice}
294+
</ListItem>
295+
))}
296+
</List>
297+
</Select>
298+
</FlexBox>
299+
282300
{/* eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex */}
283301
<div tabIndex={0}>Focus div</div>
284302
</div>

packages/lumx-react/src/components/dialog/Dialog.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ export const Dialog: Comp<DialogProps, HTMLDivElement> = forwardRef((props, ref)
160160
// eslint-disable-next-line react-hooks/rules-of-hooks
161161
const isVisible = useDelayedVisibility(Boolean(isOpen), DIALOG_TRANSITION_DURATION);
162162

163+
// eslint-disable-next-line react-hooks/rules-of-hooks
164+
const clickAwayRefs = useRef([wrapperRef]);
165+
163166
return isOpen || isVisible
164167
? createPortal(
165168
<div
@@ -180,7 +183,7 @@ export const Dialog: Comp<DialogProps, HTMLDivElement> = forwardRef((props, ref)
180183
<div className={`${CLASSNAME}__overlay`} />
181184

182185
<section className={`${CLASSNAME}__container`} role="dialog" aria-modal="true" {...dialogProps}>
183-
<ClickAwayProvider callback={!preventAutoClose && handleClose} refs={[wrapperRef]}>
186+
<ClickAwayProvider callback={!preventAutoClose && handleClose} refs={clickAwayRefs}>
184187
<div className={`${CLASSNAME}__wrapper`} ref={wrapperRef}>
185188
{(header || headerChildContent) && (
186189
<header

packages/lumx-react/src/components/dialog/__snapshots__/Dialog.test.tsx.snap

Lines changed: 56 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -75,44 +75,65 @@ exports[`<Dialog> Snapshots and structure should render story DialogWithFocusabl
7575
onChange={[Function]}
7676
theme="light"
7777
/>
78-
<Select
79-
className="lumx-spacing-margin-bottom-huge"
80-
isOpen={false}
81-
label="Select label"
82-
onDropdownClose={[Function]}
83-
onInputClick={[Function]}
84-
selectedValueRender={[Function]}
85-
value=""
78+
<FlexBox
79+
orientation="horizontal"
8680
>
87-
<List
88-
isClickable={true}
81+
<DatePickerField
82+
label="Start date"
83+
locale="fr"
84+
nextButtonProps={
85+
Object {
86+
"label": "Next month",
87+
}
88+
}
89+
onChange={[Function]}
90+
placeholder="Pick a date"
91+
previousButtonProps={
92+
Object {
93+
"label": "Previous month",
94+
}
95+
}
96+
value={2020-05-18T00:00:00.000Z}
97+
/>
98+
<Select
99+
className="lumx-spacing-margin-left-huge"
100+
isOpen={false}
101+
label="Select label"
102+
onDropdownClose={[Function]}
103+
onInputClick={[Function]}
104+
selectedValueRender={[Function]}
105+
value="First item"
89106
>
90-
<ListItem
91-
isSelected={false}
92-
key="First item"
93-
onItemSelected={[Function]}
94-
size="tiny"
95-
>
96-
First item
97-
</ListItem>
98-
<ListItem
99-
isSelected={false}
100-
key="Second item"
101-
onItemSelected={[Function]}
102-
size="tiny"
103-
>
104-
Second item
105-
</ListItem>
106-
<ListItem
107-
isSelected={false}
108-
key="Third item"
109-
onItemSelected={[Function]}
110-
size="tiny"
107+
<List
108+
isClickable={true}
111109
>
112-
Third item
113-
</ListItem>
114-
</List>
115-
</Select>
110+
<ListItem
111+
isSelected={true}
112+
key="First item"
113+
onItemSelected={[Function]}
114+
size="tiny"
115+
>
116+
First item
117+
</ListItem>
118+
<ListItem
119+
isSelected={false}
120+
key="Second item"
121+
onItemSelected={[Function]}
122+
size="tiny"
123+
>
124+
Second item
125+
</ListItem>
126+
<ListItem
127+
isSelected={false}
128+
key="Third item"
129+
onItemSelected={[Function]}
130+
size="tiny"
131+
>
132+
Third item
133+
</ListItem>
134+
</List>
135+
</Select>
136+
</FlexBox>
116137
<div
117138
tabIndex={0}
118139
>

packages/lumx-react/src/components/lightbox/Lightbox.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ export const Lightbox: Comp<LightboxProps, HTMLDivElement> = forwardRef((props,
9898
// eslint-disable-next-line react-hooks/rules-of-hooks
9999
useCallbackOnEscape(onClose);
100100

101+
// eslint-disable-next-line react-hooks/rules-of-hooks
102+
const clickAwayRefs = useRef([wrapperRef]);
103+
101104
if (!isOpen && !isVisible) return null;
102105

103106
return createPortal(
@@ -130,7 +133,7 @@ export const Lightbox: Comp<LightboxProps, HTMLDivElement> = forwardRef((props,
130133
onClick={onClose}
131134
/>
132135
)}
133-
<ClickAwayProvider callback={!preventAutoClose && onClose} refs={[wrapperRef]}>
136+
<ClickAwayProvider callback={!preventAutoClose && onClose} refs={clickAwayRefs}>
134137
<div ref={childrenRef} className={`${CLASSNAME}__wrapper`} role="presentation">
135138
{children}
136139
</div>

packages/lumx-react/src/components/lightbox/__snapshots__/Lightbox.test.tsx.snap

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ exports[`<Lightbox> Snapshots and structure should render story 'Default' 1`] =
2727
<ClickAwayProvider
2828
callback={[Function]}
2929
refs={
30-
Array [
31-
Object {
32-
"current": null,
33-
},
34-
]
30+
Object {
31+
"current": Array [
32+
Object {
33+
"current": null,
34+
},
35+
],
36+
}
3537
}
3638
>
3739
<div

packages/lumx-react/src/components/popover/Popover.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,9 @@ export const Popover: Comp<PopoverProps, HTMLDivElement> = forwardRef((props, re
270270
// eslint-disable-next-line react-hooks/rules-of-hooks
271271
useFocus(focusElement?.current, isOpen && (state?.rects?.popper?.y ?? -1) >= 0);
272272

273+
// eslint-disable-next-line react-hooks/rules-of-hooks
274+
const clickAwayRefs = useRef([clickAwayRef, anchorRef]);
275+
273276
return isOpen
274277
? renderPopover(
275278
<div
@@ -282,7 +285,7 @@ export const Popover: Comp<PopoverProps, HTMLDivElement> = forwardRef((props, re
282285
style={popoverStyle}
283286
{...attributes.popper}
284287
>
285-
<ClickAwayProvider callback={closeOnClickAway && onClose} refs={[clickAwayRef, anchorRef]}>
288+
<ClickAwayProvider callback={closeOnClickAway && onClose} refs={clickAwayRefs}>
286289
{hasArrow && <div ref={setArrowElement} className={`${CLASSNAME}__arrow`} style={styles.arrow} />}
287290
{children}
288291
</ClickAwayProvider>

0 commit comments

Comments
 (0)