Skip to content

Commit 28d189a

Browse files
author
Elliot
authored
Elliot/tree focus visible (#1159)
* Added focusVisible behavior to TreeItem border * Added focusVisible behavior to AccordionDisclosure border * Added onClick to FieldPicker.stories items - Makes items tabbable for testing purposes
1 parent 147ba1b commit 28d189a

File tree

5 files changed

+66
-27
lines changed

5 files changed

+66
-27
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
### Changed
1717

1818
- `Fieldset` accordion mode auto-indents elements in the inner `AccordionContent`
19+
- `AccordionDisclosure`, `TreeItem` no longer display purple border on click
20+
- This purple border will only display when tabbing onto a `TreeItem` or `AccordionDisclosure` (`Tree`)
1921

2022
### Fixed
2123

packages/components/src/Accordion/AccordionDisclosure.tsx

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,64 +24,81 @@
2424
2525
*/
2626

27-
import React, { FC, ReactNode, useContext } from 'react'
27+
import React, { FC, useContext, useState } from 'react'
2828
import styled from 'styled-components'
2929
import { TypographyProps, typography } from '@looker/design-tokens'
3030
import { AccordionContext } from './AccordionContext'
3131
import { AccordionDisclosureGrid } from './AccordionDisclosureGrid'
3232

3333
export interface AccordionDisclosureProps extends TypographyProps {
34-
children: ReactNode
3534
className?: string
35+
focusVisible?: boolean
3636
}
3737

3838
export const AccordionDisclosureLayout: FC<AccordionDisclosureProps> = ({
3939
children,
4040
className,
4141
}) => {
42+
const [isFocusVisible, setFocusVisible] = useState(false)
4243
const { isOpen, toggleOpen, onClose, onOpen, ...props } = useContext(
4344
AccordionContext
4445
)
4546
const handleOpen = () => onOpen && onOpen()
4647
const handleClose = () => onClose && onClose()
48+
const handleToggle = () => {
49+
isOpen ? handleClose() : handleOpen()
50+
toggleOpen(!isOpen)
51+
}
52+
53+
const handleKeyUp = (event: React.KeyboardEvent<HTMLDivElement>) => {
54+
if (event.keyCode === 9 && event.currentTarget === event.target)
55+
setFocusVisible(true)
4756

48-
const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
4957
if (event.keyCode === 13) {
50-
event.currentTarget.click()
58+
handleToggle()
5159
}
5260
}
61+
5362
const handleClick = () => {
54-
isOpen ? handleClose() : handleOpen()
55-
toggleOpen(!isOpen)
63+
setFocusVisible(false)
64+
handleToggle()
65+
}
66+
67+
const handleBlur = () => {
68+
setFocusVisible(false)
5669
}
5770

5871
return (
59-
<div
72+
<AccordionDisclosureStyle
6073
className={className}
74+
onBlur={handleBlur}
6175
onClick={handleClick}
62-
onKeyDown={handleKeyDown}
76+
onKeyUp={handleKeyUp}
6377
tabIndex={0}
78+
focusVisible={isFocusVisible}
6479
>
6580
<AccordionDisclosureGrid {...props} isOpen={isOpen}>
6681
{children}
6782
</AccordionDisclosureGrid>
68-
</div>
83+
</AccordionDisclosureStyle>
6984
)
7085
}
7186

72-
export const AccordionDisclosure = styled(AccordionDisclosureLayout)`
73-
${typography}
74-
87+
export const AccordionDisclosureStyle = styled.div<{ focusVisible: boolean }>`
7588
align-items: center;
7689
border: 1px solid transparent;
90+
border-color: ${({ focusVisible, theme }) =>
91+
focusVisible && theme.colors.keyFocus};
7792
cursor: pointer;
7893
display: flex;
94+
height: 100%;
7995
outline: none;
8096
padding: ${({ theme: { space } }) => `${space.xsmall} ${space.none}`};
97+
width: 100%;
98+
`
8199

82-
&:focus-within {
83-
border-color: ${({ theme }) => theme.colors.keyFocus};
84-
}
100+
export const AccordionDisclosure = styled(AccordionDisclosureLayout)`
101+
${typography}
85102
`
86103

87104
AccordionDisclosure.defaultProps = {

packages/components/src/Tree/Tree.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
Accordion,
3232
AccordionContent,
3333
AccordionDisclosure,
34+
AccordionDisclosureStyle,
3435
AccordionProps,
3536
AccordionIndicatorProps,
3637
} from '../Accordion'
@@ -188,6 +189,9 @@ const TreeStyle = styled.div<TreeStyleProps>`
188189
189190
${AccordionDisclosure} {
190191
height: 25px;
192+
}
193+
194+
${AccordionDisclosureStyle} {
191195
padding: ${({ theme }) => theme.space.xxsmall};
192196
${({ depth, theme }) => generateIndent(depth, theme)}
193197
}

packages/components/src/Tree/TreeItem.tsx

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import React, {
3131
ReactNode,
3232
useContext,
3333
useRef,
34+
useState,
3435
} from 'react'
3536
import styled from 'styled-components'
3637
import { SpacingSizes, uiTransparencyBlend } from '@looker/design-tokens'
@@ -89,26 +90,34 @@ const TreeItemLayout: FC<TreeItemProps> = ({
8990
const itemRef = useRef<HTMLDivElement>(null)
9091
const detailRef = useRef<HTMLDivElement>(null)
9192
const [isHovered] = useHovered(itemRef)
93+
const [isFocusVisible, setFocusVisible] = useState(false)
9294

9395
const handleClick = (event: MouseEvent<HTMLElement>) => {
9496
if (detailRef.current && detailRef.current.contains(event.target as Node)) {
9597
event.stopPropagation()
9698
return
9799
}
98100

101+
setFocusVisible(false)
99102
onClick && onClick()
100103
}
101104

102-
const handleKeyDown = (event: KeyboardEvent<HTMLElement>) => {
105+
const handleKeyUp = (event: KeyboardEvent<HTMLElement>) => {
103106
if (detailRef.current && detailRef.current.contains(event.target as Node)) {
104107
event.stopPropagation()
105108
return
106109
}
107110

108-
if (event.keyCode === 13) {
109-
event.currentTarget.click()
110-
}
111+
if (event.keyCode === 9 && event.currentTarget === event.target)
112+
setFocusVisible(true)
113+
114+
if (event.keyCode === 13) onClick && onClick()
111115
}
116+
117+
const handleBlur = () => {
118+
setFocusVisible(false)
119+
}
120+
112121
const defaultIconSize = 12
113122

114123
const detailAccessory =
@@ -129,11 +138,13 @@ const TreeItemLayout: FC<TreeItemProps> = ({
129138

130139
return (
131140
<HoverDisclosureContext.Provider value={{ visible: isHovered }}>
132-
<Space
141+
<TreeItemSpace
133142
className={props.className}
143+
focusVisible={isFocusVisible}
134144
gap="none"
145+
onBlur={handleBlur}
135146
onClick={handleClick}
136-
onKeyDown={handleKeyDown}
147+
onKeyUp={handleKeyUp}
137148
ref={itemRef}
138149
tabIndex={onClick ? 0 : -1}
139150
>
@@ -143,22 +154,26 @@ const TreeItemLayout: FC<TreeItemProps> = ({
143154
{!detailAccessory && detail}
144155
</TreeItemLabel>
145156
{detailAccessory && detail}
146-
</Space>
157+
</TreeItemSpace>
147158
</HoverDisclosureContext.Provider>
148159
)
149160
}
150161

151-
export const TreeItem = styled(TreeItemLayout)`
162+
export const TreeItem = styled(TreeItemLayout)``
163+
164+
interface TreeItemSpace {
165+
focusVisible: boolean
166+
}
167+
168+
export const TreeItemSpace = styled(Space)<TreeItemSpace>`
152169
align-items: center;
153170
border: 1px solid transparent;
171+
border-color: ${({ focusVisible, theme }) =>
172+
focusVisible && theme.colors.keyFocus};
154173
cursor: ${({ onClick }) => onClick && 'pointer'};
155174
display: flex;
156175
height: 25px;
157176
outline: none;
158-
159-
&:focus-within {
160-
border-color: ${({ theme: { colors } }) => colors.keyFocus};
161-
}
162177
`
163178

164179
interface TreeItemLabelProps {

storybook/src/Tree/FieldPicker.stories.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ const PickerItem = () => {
119119
</>
120120
}
121121
detailHoverDisclosure={!overlay}
122+
onClick={() => alert('Clicked on cost!')}
122123
selected={!!overlay}
123124
icon="FieldNumber"
124125
>

0 commit comments

Comments
 (0)