Skip to content

Commit 779106d

Browse files
authored
Fix: IE11 click option in Select, etc (#1602)
1 parent e9ca7a3 commit 779106d

File tree

7 files changed

+68
-29
lines changed

7 files changed

+68
-29
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3333

3434
### Fixed
3535

36+
- `Select` / `SelectMulti` / `InputTimeSelect` click to select option in IE11
3637
- `InputTimeSelect` can accept a time that is not included in the select dropdown options
3738
- `FieldInline` refactored to use MS-compatible grid (IE11 compatibility)
3839
- `FieldCheckbox`

packages/components/src/Form/Inputs/Combobox/utils/useBlur.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
// Much of the following is pulled from https://github.com/reach/reach-ui
2828
// because their work is fantastic (but is not in TypeScript)
2929
import { Context, FocusEvent, useContext } from 'react'
30+
import { getNextFocusTarget } from '../../../../utils'
3031
import {
3132
ComboboxContextProps,
3233
ComboboxMultiContextProps,
@@ -64,10 +65,11 @@ export function useBlur<
6465
return
6566
}
6667
// we on want to close only if focus rests outside the select
68+
const nextFocusTraget = getNextFocusTarget(e)
6769
const popoverCurrent = listRef ? listRef.current : null
6870
if (popoverCurrent) {
6971
const focusInList =
70-
popoverCurrent && popoverCurrent.contains(e.relatedTarget as Node)
72+
popoverCurrent && popoverCurrent.contains(nextFocusTraget as Node)
7173

7274
requestAnimationFrame(() => {
7375
if (focusInList && state !== ComboboxState.INTERACTING) {

packages/components/src/Form/Inputs/InputChips/InputChipsBase.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import { Chip } from '../../../Chip'
4343
import { inputHeight } from '../height'
4444
import { InputTextContent, InputText, InputTextBaseProps } from '../InputText'
4545
import { AdvancedInputControls } from '../AdvancedInputControls'
46-
import { useForkedRef, useWrapEvent } from '../../../utils'
46+
import { useForkedRef, useWrapEvent, getNextFocusTarget } from '../../../utils'
4747
import { visuallyHiddenStyle } from '../../../VisuallyHidden'
4848

4949
export interface InputChipsInputControlProps {
@@ -295,9 +295,10 @@ export const InputChipsBaseInternal = forwardRef(
295295

296296
function handleHiddenInputBlur(e: FocusEvent<HTMLInputElement>) {
297297
// Unless blur event is caused by clicking on a chip, deselect all chips
298+
const nextFocusTarget = getNextFocusTarget(e)
298299
if (
299-
e.relatedTarget &&
300-
(e.relatedTarget as HTMLElement).parentNode !==
300+
nextFocusTarget &&
301+
(nextFocusTarget as HTMLElement).parentNode !==
301302
e.currentTarget.parentNode
302303
) {
303304
deselectAll()
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
3+
MIT License
4+
5+
Copyright (c) 2020 Looker Data Sciences, Inc.
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in all
15+
copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
SOFTWARE.
24+
25+
*/
26+
import { FocusEvent } from 'react'
27+
28+
/**
29+
* ONLY use for blur events, returns the “next focused element” – event.relatedTarget if available
30+
* (modern browsers, where document.activeElement is not updated until after the blur event)
31+
* and document.activeElement as a fallback (IE11, where it’s updated before the blur event).
32+
* @param event the blur event
33+
*/
34+
export const getNextFocusTarget = (event?: FocusEvent) =>
35+
event?.relatedTarget || document.activeElement

packages/components/src/utils/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
2525
*/
2626

27+
export * from './getNextFocusTarget'
2728
export * from './getWindowedListBoundaries'
2829
export * from './HoverDisclosure'
2930
export * from './moveFocus'

playground/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"private": true,
1212
"scripts": {
13-
"start": "webpack-dev-server --host=0.0.0.0 --disable-host-check",
13+
"start": "webpack serve --host=0.0.0.0 --disable-host-check",
1414
"analyze": "webpack --mode=production --profile"
1515
},
1616
"dependencies": {

playground/src/index.tsx

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,36 +27,35 @@ import React, { useState } from 'react'
2727
import { render } from 'react-dom'
2828
import {
2929
ComponentsProvider,
30-
InputTimeSelect,
31-
Card,
32-
CardContent,
33-
Heading,
30+
FieldSelect,
31+
FieldChips,
32+
SpaceVertical,
3433
} from '@looker/components'
3534
import 'core-js/stable'
3635

37-
const TimePlayground = () => {
38-
const [value, setValue] = useState<string | undefined>('07:39')
39-
const handleChange = (newVal?: string) => {
40-
setValue(newVal)
41-
}
36+
const App = () => {
37+
const [values, setValues] = useState(['Cheddar', 'Gouda', 'Swiss'])
4238
return (
43-
<>
44-
<Heading>Selected: {value}</Heading>
45-
<InputTimeSelect value={value} onChange={handleChange} />
46-
</>
39+
<ComponentsProvider loadGoogleFonts>
40+
<SpaceVertical width={500} p="large">
41+
<FieldSelect
42+
label="Select value via click"
43+
options={[
44+
{ label: 'Cheddar', value: 'cheddar' },
45+
{ label: 'Gouda', value: 'gouda' },
46+
{ label: 'Swiss', value: 'swiss' },
47+
]}
48+
/>
49+
<FieldChips
50+
label="Select current values"
51+
description="Then focus out of the field. Confirm values are deselected."
52+
values={values}
53+
onChange={setValues}
54+
/>
55+
</SpaceVertical>
56+
</ComponentsProvider>
4757
)
4858
}
49-
50-
const App = () => (
51-
<ComponentsProvider loadGoogleFonts>
52-
<Card m="large" maxWidth="500px">
53-
<CardContent>
54-
<TimePlayground />
55-
</CardContent>
56-
</Card>
57-
</ComponentsProvider>
58-
)
59-
6059
document.addEventListener('DOMContentLoaded', () => {
6160
render(<App />, document.getElementById('container'))
6261
})

0 commit comments

Comments
 (0)