Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.
Merged
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Fixes
- Fix focus outline visible only during keyboard navigation in `ListItem` @layershifter ([#727](https://github.com/stardust-ui/react/pull/727))

<!--------------------------------[ v0.17.0 ]------------------------------- -->
## [v0.17.0](https://github.com/stardust-ui/react/tree/v0.17.0) (2019-01-17)
[Compare changes](https://github.com/stardust-ui/react/compare/v0.16.2...v0.17.0)
Expand All @@ -35,7 +38,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Fix remove empty item in docs sidebar @layershifter ([#728](https://github.com/stardust-ui/react/pull/728))

### Features
- Add accessibility for submenu in toolbar and menu behavior @kolaps33 ([#686] (https://github.com/stardust-ui/react/pull/686))
- Add accessibility for submenu in toolbar and menu behavior @kolaps33 ([#686](https://github.com/stardust-ui/react/pull/686))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


<!--------------------------------[ v0.16.2 ]------------------------------- -->
## [v0.16.2](https://github.com/stardust-ui/react/tree/v0.16.2) (2019-01-14)
Expand Down
25 changes: 24 additions & 1 deletion src/components/List/ListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
UIComponentProps,
commonPropTypes,
ContentComponentProps,
isFromKeyboard,
} from '../../lib'
import ItemLayout from '../ItemLayout/ItemLayout'
import { listItemBehavior } from '../../lib/accessibility'
Expand Down Expand Up @@ -44,12 +45,23 @@ export interface ListItemProps extends UIComponentProps, ContentComponentProps<a
* @param {object} data - All props.
*/
onClick?: ComponentEventHandler<ListItemProps>

/**
* Called after user's focus.
* @param {SyntheticEvent} event - React's original SyntheticEvent.
* @param {object} data - All props.
*/
onFocus?: ComponentEventHandler<ListItemProps>
}

export interface ListItemState {
isFromKeyboard: boolean
}

/**
* A list item contains a single piece of content within a list.
*/
class ListItem extends UIComponent<ReactProps<ListItemProps>> {
class ListItem extends UIComponent<ReactProps<ListItemProps>, ListItemState> {
static create: Function

static displayName = 'ListItem'
Expand Down Expand Up @@ -81,13 +93,18 @@ class ListItem extends UIComponent<ReactProps<ListItemProps>> {

accessibility: PropTypes.func,
onClick: PropTypes.func,
onFocus: PropTypes.func,
}

static defaultProps = {
as: 'li',
accessibility: listItemBehavior as Accessibility,
}

state = {
isFromKeyboard: false,
}

protected actionHandlers: AccessibilityActionHandlers = {
performClick: event => {
this.handleClick(event)
Expand All @@ -99,6 +116,11 @@ class ListItem extends UIComponent<ReactProps<ListItemProps>> {
_.invoke(this.props, 'onClick', e, this.props)
}

handleFocus = (e: React.SyntheticEvent) => {
this.setState({ isFromKeyboard: isFromKeyboard() })
_.invoke(this.props, 'onFocus', e, this.props)
}

renderComponent({ classes, accessibility, unhandledProps, styles }) {
const {
as,
Expand Down Expand Up @@ -132,6 +154,7 @@ class ListItem extends UIComponent<ReactProps<ListItemProps>> {
headerMediaCSS={styles.headerMedia}
contentCSS={styles.content}
onClick={this.handleClick}
onFocus={this.handleFocus}
{...accessibility.attributes.root}
{...accessibility.keyHandlers.root}
{...unhandledProps}
Expand Down
79 changes: 44 additions & 35 deletions src/themes/teams/components/List/listItemStyles.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { pxToRem } from '../../../../lib'
import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types'
import { ListItemProps } from '../../../../components/List/ListItem'
import { ListItemProps, ListItemState } from '../../../../components/List/ListItem'

const hoverFocusStyle = variables => ({
background: variables.selectableFocusHoverBackgroundColor,
color: variables.selectableFocusHoverColor,
type ListItemPropsAndState = ListItemProps & ListItemState

const selectableHoverStyle = (p: ListItemPropsAndState, v): ICSSInJSStyle => ({
background: v.selectableFocusHoverBackgroundColor,
color: v.selectableFocusHoverColor,
cursor: 'pointer',

'& .ui-item-layout__header': { color: 'inherit' },
Expand All @@ -18,53 +20,60 @@ const hoverFocusStyle = variables => ({
'& .ui-item-layout__endMedia': { display: 'block', color: 'inherit' },
})

const selectableFocusStyle = (p: ListItemPropsAndState, v): ICSSInJSStyle => ({
...selectableHoverStyle(p, v),
outline: 0,

...(p.isFromKeyboard && {
outline: `.2rem solid ${v.selectedFocusOutlineColor}`,
zIndex: 1,
}),
})

const selectedStyle = variables => ({
background: variables.selectedBackgroundColor,
color: variables.selectedColor,
})

const listItemStyles: ComponentSlotStylesInput<ListItemProps, any> = {
root: ({ props: { selectable, selected, important }, variables }): ICSSInJSStyle => ({
...(selectable && {
const listItemStyles: ComponentSlotStylesInput<ListItemPropsAndState, any> = {
root: ({ props: p, variables: v }): ICSSInJSStyle => ({
...(p.selectable && {
position: 'relative',

// hide the end media by default
'& .ui-item-layout__endMedia': { display: 'none' },

'&:hover': hoverFocusStyle(variables),
'&:focus': hoverFocusStyle(variables),
...(selected && selectedStyle(variables)),
'&:hover': selectableHoverStyle(p, v),
'&:focus': selectableFocusStyle(p, v),
...(p.selected && selectedStyle(v)),
}),
...(important && {
...(p.important && {
fontWeight: 'bold',
}),
}),
media: ({ props }): ICSSInJSStyle => {
const { important } = props
return {
...(important && {
'::before': {
content: '""',
position: 'absolute',
left: pxToRem(8),
width: pxToRem(2),
height: pxToRem(2),
background: '#000',
},
}),
}
},
header: ({ variables }): ICSSInJSStyle => ({
fontSize: variables.headerFontSize,
lineHeight: variables.headerLineHeight,
media: ({ props: p }): ICSSInJSStyle => ({
...(p.important && {
'::before': {
content: '""',
position: 'absolute',
left: pxToRem(8),
width: pxToRem(2),
height: pxToRem(2),
background: '#000',
},
}),
}),
header: ({ variables: v }): ICSSInJSStyle => ({
fontSize: v.headerFontSize,
lineHeight: v.headerLineHeight,
}),
headerMedia: ({ variables }): ICSSInJSStyle => ({
fontSize: variables.headerMediaFontSize,
lineHeight: variables.headerMediaLineHeight,
headerMedia: ({ variables: v }): ICSSInJSStyle => ({
fontSize: v.headerMediaFontSize,
lineHeight: v.headerMediaLineHeight,
}),
content: ({ variables }) => ({
fontSize: variables.contentFontSize,
lineHeight: variables.contentLineHeight,
content: ({ variables: v }) => ({
fontSize: v.contentFontSize,
lineHeight: v.contentLineHeight,
}),
}

Expand Down
1 change: 1 addition & 0 deletions src/themes/teams/components/List/listItemVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export default siteVariables => ({
selectableFocusHoverBackgroundColor: siteVariables.brand08,
selectedColor: siteVariables.black,
selectedBackgroundColor: siteVariables.gray10,
selectedFocusOutlineColor: siteVariables.brand,
})