Skip to content

Commit df57718

Browse files
authored
MenuSearch - fix keyboard shortcuts, Gatsby example improvement (#32)
* update getsby for MenuSearch * MenuSearch using arrow for focus and search workign on getsby * updating index comments for clarification * updating tests
1 parent 9c50231 commit df57718

File tree

6 files changed

+335
-139
lines changed

6 files changed

+335
-139
lines changed

packages/components/src/Form/Inputs/InputSearch/InputSearch.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,12 @@ const InputSearchComponent = forwardRef(
124124
onChange && onChange(event)
125125
}
126126

127-
const controls = !hideControls && inputValue.length > 0 && (
128-
<InputSearchControls onClear={handleClear} summary={summary} />
127+
const controls = !hideControls && (
128+
<InputSearchControls
129+
onClear={handleClear}
130+
showClear={inputValue.length > 0}
131+
summary={summary}
132+
/>
129133
)
130134

131135
return (

packages/components/src/Form/Inputs/InputSearch/InputSearchControls.tsx

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,27 +25,31 @@
2525
*/
2626

2727
import React, { FC } from 'react'
28+
import styled from 'styled-components'
2829
import { palette } from '@looker/design-tokens'
2930
import { Box } from '../../../Layout/Box'
3031
import { IconButton } from '../../../Button'
3132
import { Text } from '../../../Text'
3233

3334
export interface InputSearchControlsProps {
3435
summary?: string
36+
showClear: boolean
3537
onClear: () => void
3638
}
3739

3840
export const InputSearchControls: FC<InputSearchControlsProps> = ({
3941
onClear,
42+
showClear,
4043
summary,
4144
}) => {
4245
const clear = (
43-
<IconButton
46+
<FadeIconButton
4447
color="neutral"
4548
size="xsmall"
4649
icon="Close"
4750
pr="xsmall"
4851
label="Clear Filter"
52+
show={showClear}
4953
onClick={onClear}
5054
/>
5155
)
@@ -77,3 +81,12 @@ export const InputSearchControls: FC<InputSearchControlsProps> = ({
7781
</Box>
7882
)
7983
}
84+
85+
interface FaceIconButtonProps {
86+
show: boolean
87+
}
88+
const FadeIconButton = styled(IconButton)<FaceIconButtonProps>`
89+
opacity: ${props => (props.show ? 1 : 0)};
90+
transition: ${props =>
91+
`opacity ${props.theme.transitions.durationModerate} ${props.theme.easings.ease};`};
92+
`

packages/components/src/Form/Inputs/InputSearch/__snapshots__/InputSearch.test.tsx.snap

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,141 @@ exports[`InputSearch default 1`] = `
2525
outline: none;
2626
}
2727
28+
.c3 {
29+
border: 0;
30+
box-sizing: border-box;
31+
font: inherit;
32+
font-size: 100%;
33+
margin: 0;
34+
padding: 0;
35+
vertical-align: baseline;
36+
height: 100%;
37+
display: -webkit-box;
38+
display: -webkit-flex;
39+
display: -ms-flexbox;
40+
display: flex;
41+
margin-left: 0.25rem;
42+
margin-right: 0.25rem;
43+
-webkit-align-items: center;
44+
-webkit-box-align: center;
45+
-ms-flex-align: center;
46+
align-items: center;
47+
}
48+
49+
.c8 {
50+
border: 0;
51+
box-sizing: border-box;
52+
font: inherit;
53+
font-size: 100%;
54+
margin: 0;
55+
padding: 0;
56+
vertical-align: baseline;
57+
width: 16px;
58+
height: 16px;
59+
-webkit-align-items: center;
60+
-webkit-box-align: center;
61+
-ms-flex-align: center;
62+
align-items: center;
63+
display: -webkit-inline-box;
64+
display: -webkit-inline-flex;
65+
display: -ms-inline-flexbox;
66+
display: inline-flex;
67+
}
68+
69+
.c4 {
70+
border: 0;
71+
box-sizing: border-box;
72+
font: inherit;
73+
font-size: 100%;
74+
margin: 0;
75+
padding: 0;
76+
vertical-align: baseline;
77+
width: 22px;
78+
-webkit-align-items: center;
79+
-webkit-box-align: center;
80+
-ms-flex-align: center;
81+
align-items: center;
82+
border-radius: 0.25rem;
83+
cursor: pointer;
84+
display: -webkit-inline-box;
85+
display: -webkit-inline-flex;
86+
display: -ms-inline-flexbox;
87+
display: inline-flex;
88+
font-weight: 600;
89+
-webkit-box-pack: center;
90+
-webkit-justify-content: center;
91+
-ms-flex-pack: center;
92+
justify-content: center;
93+
outline: none;
94+
-webkit-transition: border 80ms;
95+
transition: border 80ms;
96+
vertical-align: middle;
97+
white-space: nowrap;
98+
font-size: 0.75rem;
99+
height: 22px;
100+
padding-left: 0.5rem;
101+
padding-right: 0.5rem;
102+
padding: 0rem;
103+
padding-right: 0.5rem;
104+
}
105+
106+
.c4[disabled] {
107+
cursor: default;
108+
-webkit-filter: grayscale(0.3);
109+
filter: grayscale(0.3);
110+
opacity: 0.25;
111+
}
112+
113+
.c5 {
114+
background: transparent;
115+
border: 1px solid transparent;
116+
color: #939BA5;
117+
}
118+
119+
.c5:active,
120+
.c5.active {
121+
background: #F5F6F7;
122+
border-color: #F5F6F7;
123+
color: #4C535B;
124+
}
125+
126+
.c5:hover,
127+
.c5:focus,
128+
.c5.hover {
129+
background: #FBFBFC;
130+
border-color: #FBFBFC;
131+
color: #4C535B;
132+
}
133+
134+
.c5[disabled]:hover,
135+
.c5[disabled]:active,
136+
.c5[disabled]:focus {
137+
color: #939BA5;
138+
}
139+
140+
.c7 {
141+
position: absolute;
142+
height: 1px;
143+
width: 1px;
144+
overflow: hidden;
145+
-webkit-clip: rect(1px,1px,1px,1px);
146+
clip: rect(1px,1px,1px,1px);
147+
}
148+
149+
.c6 {
150+
border: 0;
151+
box-sizing: border-box;
152+
font: inherit;
153+
font-size: 100%;
154+
margin: 0;
155+
padding: 0;
156+
vertical-align: baseline;
157+
padding-right: 0.5rem;
158+
opacity: 0;
159+
-webkit-transition: opacity 300ms cubic-bezier(0.86,0,0.07,1);
160+
transition: opacity 300ms cubic-bezier(0.86,0,0.07,1);
161+
}
162+
28163
.c0 {
29164
border: 0;
30165
box-sizing: border-box;
@@ -81,5 +216,46 @@ exports[`InputSearch default 1`] = `
81216
type="search"
82217
value=""
83218
/>
219+
<div
220+
className="c3"
221+
display="flex"
222+
height="100%"
223+
>
224+
<button
225+
className="c4 c5 c6"
226+
color="neutral"
227+
onBlur={[Function]}
228+
onClick={[Function]}
229+
onKeyUp={[Function]}
230+
size="xsmall"
231+
width={22}
232+
>
233+
<div
234+
className="c7"
235+
>
236+
Clear Filter
237+
</div>
238+
<div
239+
aria-hidden={true}
240+
className="c8 "
241+
size={16}
242+
>
243+
<svg
244+
fill="currentColor"
245+
height="100%"
246+
viewBox="0 0 24 24"
247+
width="100%"
248+
>
249+
<title>
250+
Close
251+
</title>
252+
<path
253+
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"
254+
fill="currentColor"
255+
/>
256+
</svg>
257+
</div>
258+
</button>
259+
</div>
84260
</div>
85261
`;

packages/components/src/Menu/MenuList.tsx

Lines changed: 56 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ import { Placement } from 'popper.js'
2929
import React, {
3030
Children,
3131
cloneElement,
32-
FC,
3332
ReactNode,
34-
RefObject,
33+
Ref,
3534
useRef,
35+
forwardRef,
3636
} from 'react'
3737
import { HotKeys } from 'react-hotkeys'
3838
import styled, { css } from 'styled-components'
@@ -71,8 +71,6 @@ export interface MenuListProps
7171
compact?: boolean
7272
groupDividers?: boolean
7373

74-
ref?: RefObject<HTMLUListElement>
75-
7674
/**
7775
* Can be one of: top, bottom, left, right, auto, with the modifiers: start,
7876
* end. This value comes directly from popper.js. See
@@ -106,57 +104,60 @@ export function cloneMenuListChildren(
106104
})
107105
}
108106

109-
export const MenuListInternal: FC<MenuListProps> = ({
110-
children,
111-
className,
112-
compact,
113-
customizationProps,
114-
disabled,
115-
isOpen,
116-
pin,
117-
placement,
118-
setOpen,
119-
triggerRef,
120-
ref,
121-
}) => {
122-
const innerRef = useRef<null | HTMLElement>(null)
123-
124-
const clonedChildren = cloneMenuListChildren(children as JSX.Element[], {
125-
compact,
126-
customizationProps,
127-
})
128-
129-
const menuList = (
130-
<HotKeys
131-
innerRef={innerRef}
132-
keyMap={{ MOVE_DOWN: 'down', MOVE_UP: 'up' }}
133-
handlers={{
134-
MOVE_DOWN: () => moveFocus(1, 0, innerRef),
135-
MOVE_UP: () => moveFocus(-1, -1, innerRef),
136-
}}
137-
>
138-
<ul className={className} ref={ref} tabIndex={-1} role="menu">
139-
{clonedChildren}
140-
</ul>
141-
</HotKeys>
142-
)
143-
144-
const isMenu = isOpen !== undefined
145-
const { popover } = usePopover({
146-
content: menuList,
147-
isOpen,
148-
pin,
149-
placement,
150-
setOpen,
151-
triggerRef,
152-
})
153-
154-
if (disabled) return null
155-
156-
if (isMenu) return popover || null
157-
158-
return menuList
159-
}
107+
export const MenuListInternal = forwardRef(
108+
(props: MenuListProps, ref: Ref<HTMLUListElement>) => {
109+
const {
110+
children,
111+
className,
112+
compact,
113+
customizationProps,
114+
disabled,
115+
isOpen,
116+
pin,
117+
placement,
118+
setOpen,
119+
triggerRef,
120+
} = props
121+
122+
const innerRef = useRef<null | HTMLElement>(null)
123+
124+
const clonedChildren = cloneMenuListChildren(children as JSX.Element[], {
125+
compact,
126+
customizationProps,
127+
})
128+
129+
const menuList = (
130+
<HotKeys
131+
innerRef={innerRef}
132+
keyMap={{ MOVE_DOWN: 'down', MOVE_UP: 'up' }}
133+
handlers={{
134+
MOVE_DOWN: () => moveFocus(1, 0, innerRef),
135+
MOVE_UP: () => moveFocus(-1, -1, innerRef),
136+
}}
137+
>
138+
<ul className={className} ref={ref} tabIndex={-1} role="menu">
139+
{clonedChildren}
140+
</ul>
141+
</HotKeys>
142+
)
143+
144+
const isMenu = isOpen !== undefined
145+
const { popover } = usePopover({
146+
content: menuList,
147+
isOpen,
148+
pin,
149+
placement,
150+
setOpen,
151+
triggerRef,
152+
})
153+
154+
if (disabled) return null
155+
156+
if (isMenu) return popover || null
157+
158+
return menuList
159+
}
160+
)
160161

161162
const dividersStyle = css`
162163
${MenuGroup} ~ ${MenuGroup} { /* stylelint-disable-line */

0 commit comments

Comments
 (0)