From c191063ebdc473e57de95dd9f8871f7172cbe77f Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Tue, 4 Dec 2018 14:10:57 -0800 Subject: [PATCH 01/18] Reflect which item is selected in list --- .../List/Types/ListExampleSelection.knobs.tsx | 24 -------------- .../Types/ListExampleSelection.shorthand.tsx | 4 +-- .../List/Types/ListExampleSelection.tsx | 12 +++---- src/components/List/List.tsx | 21 +++++++++--- src/components/List/ListItem.tsx | 33 +++++++++++-------- .../List/selectableListItemBehavior.ts | 13 +++++--- .../teams/components/List/listItemStyles.ts | 18 ++++++---- .../components/List/listItemVariables.ts | 6 ++-- 8 files changed, 67 insertions(+), 64 deletions(-) delete mode 100644 docs/src/examples/components/List/Types/ListExampleSelection.knobs.tsx diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.knobs.tsx b/docs/src/examples/components/List/Types/ListExampleSelection.knobs.tsx deleted file mode 100644 index f8494fc867..0000000000 --- a/docs/src/examples/components/List/Types/ListExampleSelection.knobs.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import Knobs from 'docs/src/components/Knobs/Knobs' - -const ListExampleSelectionKnobs: any = props => { - const { onKnobChange, selection } = props - - return ( - - - - ) -} - -ListExampleSelectionKnobs.propTypes = { - onKnobChange: PropTypes.func.isRequired, - selection: PropTypes.bool, -} - -ListExampleSelectionKnobs.defaultProps = { - selection: true, -} - -export default ListExampleSelectionKnobs diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx b/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx index fe7842da57..35ddecb65f 100644 --- a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx +++ b/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx @@ -25,8 +25,6 @@ const items = [ }, ] -const selection = knobs => (knobs === undefined ? true : knobs.selection) - -const ListExampleSelection = ({ knobs }) => +const ListExampleSelection = () => export default ListExampleSelection diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.tsx b/docs/src/examples/components/List/Types/ListExampleSelection.tsx index ff00e6ef0c..7599d1d1be 100644 --- a/docs/src/examples/components/List/Types/ListExampleSelection.tsx +++ b/docs/src/examples/components/List/Types/ListExampleSelection.tsx @@ -1,30 +1,28 @@ import React from 'react' import { List, Image } from '@stardust-ui/react' -const selection = knobs => (knobs === undefined ? true : knobs.selection) - -const ListExampleSelection = ({ knobs }) => ( - +const ListExampleSelection = () => ( + } header="Irving Kuhic" headerMedia="7:26:56 AM" content="Program the sensor to the SAS alarm through the haptic SQL card!" - selection={selection(knobs)} + selection={true} /> } header="Skyler Parks" headerMedia="11:30:17 PM" content="Use the online FTP application to input the multi-byte application!" - selection={selection(knobs)} + selection={true} /> } header="Dante Schneider" headerMedia="5:22:40 PM" content="The GB pixel is down, navigate the virtual interface!" - selection={selection(knobs)} + selection={true} /> ) diff --git a/src/components/List/List.tsx b/src/components/List/List.tsx index cee47f7d2e..a62488716d 100644 --- a/src/components/List/List.tsx +++ b/src/components/List/List.tsx @@ -42,6 +42,7 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps { export interface ListState { selectedItemIndex: number + focusedItemIndex: number } /** @@ -75,7 +76,8 @@ class List extends UIComponent, ListState> { static itemProps = ['debug', 'selection', 'truncateContent', 'truncateHeader', 'variables'] public state = { - selectedItemIndex: 0, + focusedItemIndex: 0, + selectedItemIndex: -1, } private focusHandler: ContainerFocusHandler = null @@ -119,7 +121,7 @@ class List extends UIComponent, ListState> { this.focusHandler = new ContainerFocusHandler( () => this.props.items.length, index => { - this.setState({ selectedItemIndex: index }, () => { + this.setState({ focusedItemIndex: index }, () => { const targetComponent = this.itemRefs[index] && this.itemRefs[index].current const targetDomNode = ReactDOM.findDOMNode(targetComponent) as any @@ -131,7 +133,16 @@ class List extends UIComponent, ListState> { renderItems() { const { items } = this.props - const { selectedItemIndex } = this.state + const { focusedItemIndex, selectedItemIndex } = this.state + + const setTabIndex = (idx): number => { + // try set tabindex=0 for the selected item + if (selectedItemIndex !== -1) { + return idx === selectedItemIndex ? 0 : -1 + } + + return idx === focusedItemIndex ? 0 : -1 + } this.itemRefs = [] @@ -142,9 +153,11 @@ class List extends UIComponent, ListState> { const ref = React.createRef() this.itemRefs[idx] = ref - maybeSelectableItemProps.tabIndex = idx === selectedItemIndex ? 0 : -1 maybeSelectableItemProps.ref = ref maybeSelectableItemProps.onFocus = () => this.focusHandler.syncFocusedItemIndex(idx) + maybeSelectableItemProps.onClick = () => this.setState({ selectedItemIndex: idx }) + maybeSelectableItemProps.selected = idx === selectedItemIndex + maybeSelectableItemProps.tabIndex = setTabIndex(idx) } const itemProps = { diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 513e1bef91..e7567b1bf7 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -1,5 +1,5 @@ import * as React from 'react' - +import * as _ from 'lodash' import * as PropTypes from 'prop-types' import { createShorthandFactory, @@ -10,7 +10,7 @@ import { } from '../../lib' import ItemLayout from '../ItemLayout/ItemLayout' import { listItemBehavior } from '../../lib/accessibility' -import { Accessibility } from '../../lib/accessibility/types' +import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types' import { Extendable } from '../../../types/utils' export interface ListItemProps extends UIComponentProps, ContentComponentProps { @@ -32,18 +32,16 @@ export interface ListItemProps extends UIComponentProps, ContentComponentProps, ListItemState> { +class ListItem extends UIComponent> { static create: Function static displayName = 'ListItem' @@ -68,6 +66,8 @@ class ListItem extends UIComponent, ListItemState> { media: PropTypes.any, selection: PropTypes.bool, + selected: PropTypes.bool, + truncateContent: PropTypes.bool, truncateHeader: PropTypes.bool, @@ -79,17 +79,20 @@ class ListItem extends UIComponent, ListItemState> { accessibility: listItemBehavior as Accessibility, } - constructor(props: ListItemProps) { - super(props, null) + private itemRef = React.createRef() - this.state = { - isHovering: false, - } + protected actionHandlers: AccessibilityActionHandlers = { + performClick: event => { + this.handleClick(event) + event.preventDefault() + }, } - private itemRef = React.createRef() + handleClick = e => { + _.invoke(this.props, 'onClick', e, this.props) + } - renderComponent({ ElementType, classes, accessibility, rest, styles }) { + renderComponent({ classes, accessibility, rest, styles }) { const { as, debug, @@ -122,7 +125,9 @@ class ListItem extends UIComponent, ListItemState> { headerMediaCSS={styles.headerMedia} contentCSS={styles.content} ref={this.itemRef} + onClick={this.handleClick} {...accessibility.attributes.root} + {...accessibility.keyHandlers.root} {...rest} /> ) diff --git a/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts b/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts index 31f88d0b33..2391075745 100644 --- a/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts +++ b/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts @@ -1,4 +1,5 @@ import { Accessibility } from '../../types' +import * as keyboardKey from 'keyboard-key' /** * @specification @@ -10,10 +11,14 @@ const selectableListItemBehavior: Accessibility = (props: any) => ({ attributes: { root: { role: 'option', - 'aria-selected': !!props['active'], - ...(props.focusableItemProps && { - tabIndex: props.focusableItemProps.isFocused ? '0' : '-1', - }), + 'aria-selected': !!props['selected'], + }, + }, + keyActions: { + root: { + performClick: { + keyCombinations: [{ keyCode: keyboardKey.Enter }, { keyCode: keyboardKey.Spacebar }], + }, }, }, }) diff --git a/src/themes/teams/components/List/listItemStyles.ts b/src/themes/teams/components/List/listItemStyles.ts index fe9e0f0298..4885fa6f7f 100644 --- a/src/themes/teams/components/List/listItemStyles.ts +++ b/src/themes/teams/components/List/listItemStyles.ts @@ -2,9 +2,9 @@ import { pxToRem } from '../../../../lib' import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' import { ListItemProps } from '../../../../components/List/ListItem' -const hoverStyle = variables => ({ - background: variables.selectionHoverBackgroundColor, - color: variables.selectionHoverColor, +const hoverFocusStyle = variables => ({ + background: variables.selectionFocusHoverBackgroundColor, + color: variables.selectionFocusHoverColor, cursor: 'pointer', '& .ui-item-layout__header': { color: 'inherit' }, @@ -18,16 +18,22 @@ const hoverStyle = variables => ({ '& .ui-item-layout__endMedia': { display: 'block', color: 'inherit' }, }) +const selectedStyle = variables => ({ + background: variables.selectedBackgroundColor, + color: variables.selectedColor, +}) + const listItemStyles: ComponentSlotStylesInput = { - root: ({ props: { selection, important }, variables }): ICSSInJSStyle => ({ + root: ({ props: { selection, selected, important }, variables }): ICSSInJSStyle => ({ ...(selection && { position: 'relative', // hide the end media by default '& .ui-item-layout__endMedia': { display: 'none' }, - '&:hover': hoverStyle(variables), - '&:focus': hoverStyle(variables), + '&:hover': hoverFocusStyle(variables), + '&:focus': hoverFocusStyle(variables), + ...(selected && selectedStyle(variables)), }), ...(important && { fontWeight: 'bold', diff --git a/src/themes/teams/components/List/listItemVariables.ts b/src/themes/teams/components/List/listItemVariables.ts index 5edc096d03..18213ccfdc 100644 --- a/src/themes/teams/components/List/listItemVariables.ts +++ b/src/themes/teams/components/List/listItemVariables.ts @@ -14,6 +14,8 @@ export default siteVariables => ({ contentLineHeight: siteVariables.lineHeightSmall, // Selection - selectionHoverColor: siteVariables.white, - selectionHoverBackgroundColor: siteVariables.brand08, + selectionFocusHoverColor: siteVariables.white, + selectionFocusHoverBackgroundColor: siteVariables.brand08, + selectedColor: siteVariables.black, + selectedBackgroundColor: siteVariables.gray10, }) From ed57ca8b0567f55aa42ae463655d635a7ee0329b Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Wed, 5 Dec 2018 13:42:31 -0800 Subject: [PATCH 02/18] Make list derived from autocontrolled component --- .../Types/ListExampleSelection.shorthand.tsx | 4 ++- src/components/List/List.tsx | 26 ++++++++++++------- src/index.ts | 2 +- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx b/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx index 35ddecb65f..88b15a2ffc 100644 --- a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx +++ b/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx @@ -25,6 +25,8 @@ const items = [ }, ] -const ListExampleSelection = () => +const ListExampleSelection = () => ( + +) export default ListExampleSelection diff --git a/src/components/List/List.tsx b/src/components/List/List.tsx index a62488716d..6fbb7c9a33 100644 --- a/src/components/List/List.tsx +++ b/src/components/List/List.tsx @@ -6,7 +6,7 @@ import * as PropTypes from 'prop-types' import { customPropTypes, childrenExist, - UIComponent, + AutoControlledComponent, UIComponentProps, ChildrenComponentProps, commonPropTypes, @@ -33,6 +33,12 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps { /** A selection list formats list items as possible choices. */ selection?: boolean + /** Index of the currently selected item. */ + selectedItemIndex?: number + + /** Initial selectedItemIndex value. */ + defaultSelectedItemIndex?: number + /** Truncates content */ truncateContent?: boolean @@ -41,14 +47,14 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps { } export interface ListState { - selectedItemIndex: number focusedItemIndex: number + selectedItemIndex?: number } /** * A list displays a group of related content. */ -class List extends UIComponent, ListState> { +class List extends AutoControlledComponent, ListState> { static displayName = 'List' static className = 'ui-list' @@ -63,6 +69,8 @@ class List extends UIComponent, ListState> { selection: PropTypes.bool, truncateContent: PropTypes.bool, truncateHeader: PropTypes.bool, + selectedItemIndex: PropTypes.number, + defaultSelectedItemIndex: PropTypes.number, } static defaultProps = { @@ -70,16 +78,16 @@ class List extends UIComponent, ListState> { accessibility: listBehavior as Accessibility, } + static autoControlledProps = ['selectedItemIndex'] + getInitialAutoControlledState() { + return { selectedItemIndex: -1, focusedItemIndex: 0 } + } + static Item = ListItem // List props that are passed to each individual Item props static itemProps = ['debug', 'selection', 'truncateContent', 'truncateHeader', 'variables'] - public state = { - focusedItemIndex: 0, - selectedItemIndex: -1, - } - private focusHandler: ContainerFocusHandler = null private itemRefs = [] @@ -155,7 +163,7 @@ class List extends UIComponent, ListState> { maybeSelectableItemProps.ref = ref maybeSelectableItemProps.onFocus = () => this.focusHandler.syncFocusedItemIndex(idx) - maybeSelectableItemProps.onClick = () => this.setState({ selectedItemIndex: idx }) + maybeSelectableItemProps.onClick = () => this.trySetState({ selectedItemIndex: idx }) maybeSelectableItemProps.selected = idx === selectedItemIndex maybeSelectableItemProps.tabIndex = setTabIndex(idx) } diff --git a/src/index.ts b/src/index.ts index e37aa998e0..d29ae00831 100644 --- a/src/index.ts +++ b/src/index.ts @@ -57,7 +57,7 @@ export { default as Label, LabelProps } from './components/Label/Label' export { default as Layout, LayoutPropsWithDefaults, LayoutProps } from './components/Layout/Layout' export { default as List, ListProps } from './components/List/List' -export { default as ListItem, ListItemState, ListItemProps } from './components/List/ListItem' +export { default as ListItem, ListItemProps } from './components/List/ListItem' export { default as Menu, MenuProps } from './components/Menu/Menu' export { default as MenuItem, MenuItemState, MenuItemProps } from './components/Menu/MenuItem' From 94882026e289da097b059cf9c3253f21eee35f40 Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Wed, 5 Dec 2018 14:01:41 -0800 Subject: [PATCH 03/18] small fix --- .../accessibility/Behaviors/List/selectableListItemBehavior.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts b/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts index 2391075745..ae946d29a2 100644 --- a/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts +++ b/src/lib/accessibility/Behaviors/List/selectableListItemBehavior.ts @@ -4,7 +4,7 @@ import * as keyboardKey from 'keyboard-key' /** * @specification * Adds role='option'. This role is used for a selectable item in a list. - * Adds attribute 'aria-selected=true' based on the property 'active'. Based on this screen readers will recognize the selected state of the item. + * Adds attribute 'aria-selected=true' based on the property 'selected'. Based on this screen readers will recognize the selected state of the item. */ const selectableListItemBehavior: Accessibility = (props: any) => ({ From 29abd1ad8d073f0e1793aa7b83826618545598a6 Mon Sep 17 00:00:00 2001 From: Sofiya Huts <8460706+sophieH29@users.noreply.github.com> Date: Thu, 6 Dec 2018 08:49:58 -0800 Subject: [PATCH 04/18] Update ListExampleSelection.tsx --- .../components/List/Types/ListExampleSelection.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.tsx b/docs/src/examples/components/List/Types/ListExampleSelection.tsx index 7599d1d1be..bb40552c15 100644 --- a/docs/src/examples/components/List/Types/ListExampleSelection.tsx +++ b/docs/src/examples/components/List/Types/ListExampleSelection.tsx @@ -2,27 +2,27 @@ import React from 'react' import { List, Image } from '@stardust-ui/react' const ListExampleSelection = () => ( - + } header="Irving Kuhic" headerMedia="7:26:56 AM" content="Program the sensor to the SAS alarm through the haptic SQL card!" - selection={true} + selection /> } header="Skyler Parks" headerMedia="11:30:17 PM" content="Use the online FTP application to input the multi-byte application!" - selection={true} + selection /> } header="Dante Schneider" headerMedia="5:22:40 PM" content="The GB pixel is down, navigate the virtual interface!" - selection={true} + selection /> ) From 12d5caa097a38960fe7b26c2b55abec3e330a611 Mon Sep 17 00:00:00 2001 From: Sofiya Huts <8460706+sophieH29@users.noreply.github.com> Date: Thu, 6 Dec 2018 08:50:32 -0800 Subject: [PATCH 05/18] Update ListExampleSelection.shorthand.tsx --- .../components/List/Types/ListExampleSelection.shorthand.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx b/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx index 88b15a2ffc..a6ce8fee37 100644 --- a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx +++ b/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx @@ -26,7 +26,7 @@ const items = [ ] const ListExampleSelection = () => ( - + ) export default ListExampleSelection From be1d9a89df002850008385ecac7a0991bb6c9637 Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Tue, 11 Dec 2018 13:37:51 +0100 Subject: [PATCH 06/18] Small improvement --- src/components/List/ListItem.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index e7567b1bf7..4709447a03 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -11,7 +11,7 @@ import { import ItemLayout from '../ItemLayout/ItemLayout' import { listItemBehavior } from '../../lib/accessibility' import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types' -import { Extendable } from '../../../types/utils' +import { Extendable, ComponentEventHandler } from '../../../types/utils' export interface ListItemProps extends UIComponentProps, ContentComponentProps { /** @@ -20,7 +20,7 @@ export interface ListItemProps extends UIComponentProps, ContentComponentProps } /** @@ -72,6 +79,7 @@ class ListItem extends UIComponent> { truncateHeader: PropTypes.bool, accessibility: PropTypes.func, + onClick: PropTypes.func, } static defaultProps = { From f1b2ae4ab5c1a9d1b74b68dfea726421aa2d64f6 Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Thu, 13 Dec 2018 15:44:56 +0100 Subject: [PATCH 07/18] Rename *ItemIndex -> *Index --- .../Types/ListExampleSelection.shorthand.tsx | 4 +- src/components/List/List.tsx | 40 +++++++++---------- .../FocusHandling/FocusContainer.ts | 40 +++++++++---------- 3 files changed, 41 insertions(+), 43 deletions(-) diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx b/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx index a6ce8fee37..96c0c9af88 100644 --- a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx +++ b/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx @@ -25,8 +25,6 @@ const items = [ }, ] -const ListExampleSelection = () => ( - -) +const ListExampleSelection = () => export default ListExampleSelection diff --git a/src/components/List/List.tsx b/src/components/List/List.tsx index 6fbb7c9a33..02f96b55c6 100644 --- a/src/components/List/List.tsx +++ b/src/components/List/List.tsx @@ -34,10 +34,10 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps { selection?: boolean /** Index of the currently selected item. */ - selectedItemIndex?: number + selectedIndex?: number - /** Initial selectedItemIndex value. */ - defaultSelectedItemIndex?: number + /** Initial selectedIndex value. */ + defaultSelectedIndex?: number /** Truncates content */ truncateContent?: boolean @@ -47,8 +47,8 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps { } export interface ListState { - focusedItemIndex: number - selectedItemIndex?: number + focusedIndex: number + selectedIndex?: number } /** @@ -69,8 +69,8 @@ class List extends AutoControlledComponent, ListState> { selection: PropTypes.bool, truncateContent: PropTypes.bool, truncateHeader: PropTypes.bool, - selectedItemIndex: PropTypes.number, - defaultSelectedItemIndex: PropTypes.number, + selectedIndex: PropTypes.number, + defaultSelectedIndex: PropTypes.number, } static defaultProps = { @@ -78,9 +78,9 @@ class List extends AutoControlledComponent, ListState> { accessibility: listBehavior as Accessibility, } - static autoControlledProps = ['selectedItemIndex'] + static autoControlledProps = ['selectedIndex'] getInitialAutoControlledState() { - return { selectedItemIndex: -1, focusedItemIndex: 0 } + return { selectedIndex: -1, focusedIndex: 0 } } static Item = ListItem @@ -129,7 +129,7 @@ class List extends AutoControlledComponent, ListState> { this.focusHandler = new ContainerFocusHandler( () => this.props.items.length, index => { - this.setState({ focusedItemIndex: index }, () => { + this.setState({ focusedIndex: index }, () => { const targetComponent = this.itemRefs[index] && this.itemRefs[index].current const targetDomNode = ReactDOM.findDOMNode(targetComponent) as any @@ -141,15 +141,15 @@ class List extends AutoControlledComponent, ListState> { renderItems() { const { items } = this.props - const { focusedItemIndex, selectedItemIndex } = this.state + const { focusedIndex, selectedIndex } = this.state - const setTabIndex = (idx): number => { - // try set tabindex=0 for the selected item - if (selectedItemIndex !== -1) { - return idx === selectedItemIndex ? 0 : -1 + const getTabIndex = (idx): number => { + // return tabindex=0 if the item was selected + if (selectedIndex !== -1) { + return idx === selectedIndex ? 0 : -1 } - return idx === focusedItemIndex ? 0 : -1 + return idx === focusedIndex ? 0 : -1 } this.itemRefs = [] @@ -162,10 +162,10 @@ class List extends AutoControlledComponent, ListState> { this.itemRefs[idx] = ref maybeSelectableItemProps.ref = ref - maybeSelectableItemProps.onFocus = () => this.focusHandler.syncFocusedItemIndex(idx) - maybeSelectableItemProps.onClick = () => this.trySetState({ selectedItemIndex: idx }) - maybeSelectableItemProps.selected = idx === selectedItemIndex - maybeSelectableItemProps.tabIndex = setTabIndex(idx) + maybeSelectableItemProps.onFocus = () => this.focusHandler.syncfocusedIndex(idx) + maybeSelectableItemProps.onClick = () => this.trySetState({ selectedIndex: idx }) + maybeSelectableItemProps.selected = idx === selectedIndex + maybeSelectableItemProps.tabIndex = getTabIndex(idx) } const itemProps = { diff --git a/src/lib/accessibility/FocusHandling/FocusContainer.ts b/src/lib/accessibility/FocusHandling/FocusContainer.ts index 14238fdb47..99ca9a17eb 100644 --- a/src/lib/accessibility/FocusHandling/FocusContainer.ts +++ b/src/lib/accessibility/FocusHandling/FocusContainer.ts @@ -1,29 +1,29 @@ import * as _ from 'lodash' export class ContainerFocusHandler { - private focusedItemIndex = 0 + private focusedIndex = 0 constructor(private getItemsCount: () => number, private readonly setFocusAt: (number) => void) {} private noItems = (): boolean => this.getItemsCount() === 0 - private constrainFocusedItemIndex(): void { - if (this.focusedItemIndex < 0) { - this.focusedItemIndex = 0 + private constrainfocusedIndex(): void { + if (this.focusedIndex < 0) { + this.focusedIndex = 0 } const itemsCount = this.getItemsCount() - if (this.focusedItemIndex >= itemsCount) { - this.focusedItemIndex = itemsCount - 1 + if (this.focusedIndex >= itemsCount) { + this.focusedIndex = itemsCount - 1 } } - public getFocusedItemIndex(): number { - return this.focusedItemIndex + public getfocusedIndex(): number { + return this.focusedIndex } - public syncFocusedItemIndex(withCurrentIndex: number) { - this.focusedItemIndex = withCurrentIndex + public syncfocusedIndex(withCurrentIndex: number) { + this.focusedIndex = withCurrentIndex } public movePrevious(): void { @@ -31,10 +31,10 @@ export class ContainerFocusHandler { return } - this.focusedItemIndex -= 1 - this.constrainFocusedItemIndex() + this.focusedIndex -= 1 + this.constrainfocusedIndex() - this.setFocusAt(this.focusedItemIndex) + this.setFocusAt(this.focusedIndex) } public moveNext(): void { @@ -42,10 +42,10 @@ export class ContainerFocusHandler { return } - this.focusedItemIndex += 1 - this.constrainFocusedItemIndex() + this.focusedIndex += 1 + this.constrainfocusedIndex() - this.setFocusAt(this.focusedItemIndex) + this.setFocusAt(this.focusedIndex) } public moveFirst(): void { @@ -53,8 +53,8 @@ export class ContainerFocusHandler { return } - this.focusedItemIndex = 0 - this.setFocusAt(this.focusedItemIndex) + this.focusedIndex = 0 + this.setFocusAt(this.focusedIndex) } public moveLast(): void { @@ -62,7 +62,7 @@ export class ContainerFocusHandler { return } - this.focusedItemIndex = this.getItemsCount() - 1 - this.setFocusAt(this.focusedItemIndex) + this.focusedIndex = this.getItemsCount() - 1 + this.setFocusAt(this.focusedIndex) } } From 46c33aca413483bd2c226e872a86685f3e7c602d Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Thu, 13 Dec 2018 16:16:21 +0100 Subject: [PATCH 08/18] Names refactoring --- .../Content/ListExampleEndMedia.shorthand.tsx | 2 +- .../List/Content/ListExampleEndMedia.tsx | 6 ++-- .../List/Types/ListExample.shorthand.tsx | 4 +-- .../components/List/Types/ListExample.tsx | 4 +-- ...sx => ListExampleSelectable.shorthand.tsx} | 4 +-- ...election.tsx => ListExampleSelectable.tsx} | 12 +++---- .../examples/components/List/Types/index.tsx | 4 +-- docs/src/prototypes/SearchPage/SearchPage.tsx | 2 +- src/components/List/List.tsx | 12 +++---- src/components/List/ListItem.tsx | 4 +-- .../Behaviors/List/listBehavior.ts | 4 +-- .../Behaviors/List/listItemBehavior.ts | 4 +-- .../FocusHandling/FocusContainer.ts | 4 +-- .../teams/components/List/listItemStyles.ts | 8 ++--- .../components/List/listItemVariables.ts | 6 ++-- test/specs/behaviors/listBehavior-test.tsx | 6 ++-- .../specs/behaviors/listItemBehavior-test.tsx | 6 ++-- .../lib/accessibility/FocusContainer-test.ts | 32 +++++++++---------- 18 files changed, 62 insertions(+), 62 deletions(-) rename docs/src/examples/components/List/Types/{ListExampleSelection.shorthand.tsx => ListExampleSelectable.shorthand.tsx} (85%) rename docs/src/examples/components/List/Types/{ListExampleSelection.tsx => ListExampleSelectable.tsx} (84%) diff --git a/docs/src/examples/components/List/Content/ListExampleEndMedia.shorthand.tsx b/docs/src/examples/components/List/Content/ListExampleEndMedia.shorthand.tsx index 3955f2f76a..6915275332 100644 --- a/docs/src/examples/components/List/Content/ListExampleEndMedia.shorthand.tsx +++ b/docs/src/examples/components/List/Content/ListExampleEndMedia.shorthand.tsx @@ -21,6 +21,6 @@ const items = [ }, ] -const ListExample = () => +const ListExample = () => export default ListExample diff --git a/docs/src/examples/components/List/Content/ListExampleEndMedia.tsx b/docs/src/examples/components/List/Content/ListExampleEndMedia.tsx index 6b7a375791..3b99d18770 100644 --- a/docs/src/examples/components/List/Content/ListExampleEndMedia.tsx +++ b/docs/src/examples/components/List/Content/ListExampleEndMedia.tsx @@ -8,17 +8,17 @@ const ListExample = () => ( ) diff --git a/docs/src/examples/components/List/Types/ListExample.shorthand.tsx b/docs/src/examples/components/List/Types/ListExample.shorthand.tsx index 866bfe7a56..10f591fff3 100644 --- a/docs/src/examples/components/List/Types/ListExample.shorthand.tsx +++ b/docs/src/examples/components/List/Types/ListExample.shorthand.tsx @@ -25,6 +25,6 @@ const items = [ }, ] -const ListExampleSelection = ({ knobs }) => +const ListExampleSelectable = ({ knobs }) => -export default ListExampleSelection +export default ListExampleSelectable diff --git a/docs/src/examples/components/List/Types/ListExample.tsx b/docs/src/examples/components/List/Types/ListExample.tsx index da2e3c1f4a..250ab8ef2a 100644 --- a/docs/src/examples/components/List/Types/ListExample.tsx +++ b/docs/src/examples/components/List/Types/ListExample.tsx @@ -1,7 +1,7 @@ import React from 'react' import { List, Image } from '@stardust-ui/react' -const ListExampleSelection = ({ knobs }) => ( +const ListExampleSelectable = ({ knobs }) => ( } @@ -24,4 +24,4 @@ const ListExampleSelection = ({ knobs }) => ( ) -export default ListExampleSelection +export default ListExampleSelectable diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx b/docs/src/examples/components/List/Types/ListExampleSelectable.shorthand.tsx similarity index 85% rename from docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx rename to docs/src/examples/components/List/Types/ListExampleSelectable.shorthand.tsx index 96c0c9af88..49a526710e 100644 --- a/docs/src/examples/components/List/Types/ListExampleSelection.shorthand.tsx +++ b/docs/src/examples/components/List/Types/ListExampleSelectable.shorthand.tsx @@ -25,6 +25,6 @@ const items = [ }, ] -const ListExampleSelection = () => +const ListExampleSelectable = () => -export default ListExampleSelection +export default ListExampleSelectable diff --git a/docs/src/examples/components/List/Types/ListExampleSelection.tsx b/docs/src/examples/components/List/Types/ListExampleSelectable.tsx similarity index 84% rename from docs/src/examples/components/List/Types/ListExampleSelection.tsx rename to docs/src/examples/components/List/Types/ListExampleSelectable.tsx index bb40552c15..55029d1256 100644 --- a/docs/src/examples/components/List/Types/ListExampleSelection.tsx +++ b/docs/src/examples/components/List/Types/ListExampleSelectable.tsx @@ -1,30 +1,30 @@ import React from 'react' import { List, Image } from '@stardust-ui/react' -const ListExampleSelection = () => ( - +const ListExampleSelectable = () => ( + } header="Irving Kuhic" headerMedia="7:26:56 AM" content="Program the sensor to the SAS alarm through the haptic SQL card!" - selection + selectable /> } header="Skyler Parks" headerMedia="11:30:17 PM" content="Use the online FTP application to input the multi-byte application!" - selection + selectable /> } header="Dante Schneider" headerMedia="5:22:40 PM" content="The GB pixel is down, navigate the virtual interface!" - selection + selectable /> ) -export default ListExampleSelection +export default ListExampleSelectable diff --git a/docs/src/examples/components/List/Types/index.tsx b/docs/src/examples/components/List/Types/index.tsx index 0d78cbf48a..e4f6a7f980 100644 --- a/docs/src/examples/components/List/Types/index.tsx +++ b/docs/src/examples/components/List/Types/index.tsx @@ -10,9 +10,9 @@ const Types = () => ( examplePath="components/List/Types/ListExample" /> ) diff --git a/docs/src/prototypes/SearchPage/SearchPage.tsx b/docs/src/prototypes/SearchPage/SearchPage.tsx index c655a20ad5..fa784abf6c 100644 --- a/docs/src/prototypes/SearchPage/SearchPage.tsx +++ b/docs/src/prototypes/SearchPage/SearchPage.tsx @@ -97,7 +97,7 @@ class SearchPage extends React.Component { Results {results.length} of {DATA_RECORDS.length}

- + )} diff --git a/src/components/List/List.tsx b/src/components/List/List.tsx index 02f96b55c6..a830e13bf4 100644 --- a/src/components/List/List.tsx +++ b/src/components/List/List.tsx @@ -30,8 +30,8 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps { /** Shorthand array of props for ListItem. */ items?: ShorthandValue[] - /** A selection list formats list items as possible choices. */ - selection?: boolean + /** A selectable list formats list items as possible choices. */ + selectable?: boolean /** Index of the currently selected item. */ selectedIndex?: number @@ -66,7 +66,7 @@ class List extends AutoControlledComponent, ListState> { accessibility: PropTypes.func, debug: PropTypes.bool, items: customPropTypes.collectionShorthand, - selection: PropTypes.bool, + selectable: PropTypes.bool, truncateContent: PropTypes.bool, truncateHeader: PropTypes.bool, selectedIndex: PropTypes.number, @@ -86,7 +86,7 @@ class List extends AutoControlledComponent, ListState> { static Item = ListItem // List props that are passed to each individual Item props - static itemProps = ['debug', 'selection', 'truncateContent', 'truncateHeader', 'variables'] + static itemProps = ['debug', 'selectable', 'truncateContent', 'truncateHeader', 'variables'] private focusHandler: ContainerFocusHandler = null private itemRefs = [] @@ -157,12 +157,12 @@ class List extends AutoControlledComponent, ListState> { return _.map(items, (item, idx) => { const maybeSelectableItemProps = {} as any - if (this.props.selection) { + if (this.props.selectable) { const ref = React.createRef() this.itemRefs[idx] = ref maybeSelectableItemProps.ref = ref - maybeSelectableItemProps.onFocus = () => this.focusHandler.syncfocusedIndex(idx) + maybeSelectableItemProps.onFocus = () => this.focusHandler.syncFocusedIndex(idx) maybeSelectableItemProps.onClick = () => this.trySetState({ selectedIndex: idx }) maybeSelectableItemProps.selected = idx === selectedIndex maybeSelectableItemProps.tabIndex = getTabIndex(idx) diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 4709447a03..a7283fb118 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -31,7 +31,7 @@ export interface ListItemProps extends UIComponentProps, ContentComponentProps
> { important: PropTypes.bool, media: PropTypes.any, - selection: PropTypes.bool, + selectable: PropTypes.bool, selected: PropTypes.bool, truncateContent: PropTypes.bool, diff --git a/src/lib/accessibility/Behaviors/List/listBehavior.ts b/src/lib/accessibility/Behaviors/List/listBehavior.ts index 360e89f59e..54f91bf632 100644 --- a/src/lib/accessibility/Behaviors/List/listBehavior.ts +++ b/src/lib/accessibility/Behaviors/List/listBehavior.ts @@ -4,10 +4,10 @@ import basicListBehavior from './basicListBehavior' /** * @description - * Defines a behavior 'BasicListBehavior' or 'SelectableListBehavior' based on property 'selection'. + * Defines a behavior 'BasicListBehavior' or 'SelectableListBehavior' based on property 'selectable'. */ const ListBehavior: Accessibility = (props: any) => - props.selection ? selectableListBehavior(props) : basicListBehavior(props) + props.selectable ? selectableListBehavior(props) : basicListBehavior(props) export default ListBehavior diff --git a/src/lib/accessibility/Behaviors/List/listItemBehavior.ts b/src/lib/accessibility/Behaviors/List/listItemBehavior.ts index 09292f93ff..714bf96c3f 100644 --- a/src/lib/accessibility/Behaviors/List/listItemBehavior.ts +++ b/src/lib/accessibility/Behaviors/List/listItemBehavior.ts @@ -4,10 +4,10 @@ import { Accessibility } from '../../types' /** * @description - * Defines a behavior "BasicListItemBehavior" or "SelectableListItemBehavior" based on "selection" property. + * Defines a behavior "BasicListItemBehavior" or "SelectableListItemBehavior" based on "selectable" property. */ const listItemBehavior: Accessibility = (props: any) => - props.selection ? selectableListItemBehavior(props) : basicListItemBehavior(props) + props.selectable ? selectableListItemBehavior(props) : basicListItemBehavior(props) export default listItemBehavior diff --git a/src/lib/accessibility/FocusHandling/FocusContainer.ts b/src/lib/accessibility/FocusHandling/FocusContainer.ts index 99ca9a17eb..be35d31f10 100644 --- a/src/lib/accessibility/FocusHandling/FocusContainer.ts +++ b/src/lib/accessibility/FocusHandling/FocusContainer.ts @@ -18,11 +18,11 @@ export class ContainerFocusHandler { } } - public getfocusedIndex(): number { + public getFocusedIndex(): number { return this.focusedIndex } - public syncfocusedIndex(withCurrentIndex: number) { + public syncFocusedIndex(withCurrentIndex: number) { this.focusedIndex = withCurrentIndex } diff --git a/src/themes/teams/components/List/listItemStyles.ts b/src/themes/teams/components/List/listItemStyles.ts index 0e4429fd4e..34721950c3 100644 --- a/src/themes/teams/components/List/listItemStyles.ts +++ b/src/themes/teams/components/List/listItemStyles.ts @@ -3,8 +3,8 @@ import { ComponentSlotStylesInput, ICSSInJSStyle } from '../../../types' import { ListItemProps } from '../../../../components/List/ListItem' const hoverFocusStyle = variables => ({ - background: variables.selectionFocusHoverBackgroundColor, - color: variables.selectionFocusHoverColor, + background: variables.selectableFocusHoverBackgroundColor, + color: variables.selectableFocusHoverColor, cursor: 'pointer', '& .ui-item-layout__header': { color: 'inherit' }, @@ -24,8 +24,8 @@ const selectedStyle = variables => ({ }) const listItemStyles: ComponentSlotStylesInput = { - root: ({ props: { selection, selected, important }, variables }): ICSSInJSStyle => ({ - ...(selection && { + root: ({ props: { selectable, selected, important }, variables }): ICSSInJSStyle => ({ + ...(selectable && { position: 'relative', // hide the end media by default diff --git a/src/themes/teams/components/List/listItemVariables.ts b/src/themes/teams/components/List/listItemVariables.ts index 18213ccfdc..e7a622765c 100644 --- a/src/themes/teams/components/List/listItemVariables.ts +++ b/src/themes/teams/components/List/listItemVariables.ts @@ -13,9 +13,9 @@ export default siteVariables => ({ contentFontSize: siteVariables.fontSizes.small, contentLineHeight: siteVariables.lineHeightSmall, - // Selection - selectionFocusHoverColor: siteVariables.white, - selectionFocusHoverBackgroundColor: siteVariables.brand08, + // Selectable + selectableFocusHoverColor: siteVariables.white, + selectableFocusHoverBackgroundColor: siteVariables.brand08, selectedColor: siteVariables.black, selectedBackgroundColor: siteVariables.gray10, }) diff --git a/test/specs/behaviors/listBehavior-test.tsx b/test/specs/behaviors/listBehavior-test.tsx index faed2115ef..eada8135d1 100644 --- a/test/specs/behaviors/listBehavior-test.tsx +++ b/test/specs/behaviors/listBehavior-test.tsx @@ -1,15 +1,15 @@ import { listBehavior } from 'src/lib/accessibility' describe('ListBehavior.ts', () => { - test('use SelectableListBehavior if selection prop is defined', () => { + test('use SelectableListBehavior if selectable prop is defined', () => { const property = { - selection: true, + selectable: true, } const expectedResult = listBehavior(property) expect(expectedResult.attributes.root.role).toEqual('listbox') }) - test('use BasicListItemBehavior if selection prop is NOT defined', () => { + test('use BasicListItemBehavior if selectable prop is NOT defined', () => { const property = {} const expectedResult = listBehavior(property) expect(expectedResult.attributes.root.role).toEqual('list') diff --git a/test/specs/behaviors/listItemBehavior-test.tsx b/test/specs/behaviors/listItemBehavior-test.tsx index 261ba29fe3..752000a625 100644 --- a/test/specs/behaviors/listItemBehavior-test.tsx +++ b/test/specs/behaviors/listItemBehavior-test.tsx @@ -1,15 +1,15 @@ import { listItemBehavior } from 'src/lib/accessibility' describe('ListItemBehavior.ts', () => { - test('use SelectableListItemBehavior if selection prop is defined', () => { + test('use SelectableListItemBehavior if selectable prop is defined', () => { const property = { - selection: true, + selectable: true, } const expectedResult = listItemBehavior(property) expect(expectedResult.attributes.root.role).toEqual('option') }) - test('use BasicListBehavior if selection prop is NOT defined', () => { + test('use BasicListBehavior if selectable prop is NOT defined', () => { const property = {} const expectedResult = listItemBehavior(property) expect(expectedResult.attributes.root.role).toEqual('listitem') diff --git a/test/specs/lib/accessibility/FocusContainer-test.ts b/test/specs/lib/accessibility/FocusContainer-test.ts index bed2ab14da..58c98d18ff 100644 --- a/test/specs/lib/accessibility/FocusContainer-test.ts +++ b/test/specs/lib/accessibility/FocusContainer-test.ts @@ -11,22 +11,22 @@ describe('Focus Container', () => { const focusContainer = createFocusContainer() expect(focusContainer).toBeDefined() - expect(focusContainer.getFocusedItemIndex()).toBe(0) + expect(focusContainer.getFocusedIndex()).toBe(0) }) describe('sync item index', () => { test('should set focus item index', () => { const focusContainer = createFocusContainer({ itemsCount: 5 }) - focusContainer.syncFocusedItemIndex(4) + focusContainer.syncFocusedIndex(4) - expect(focusContainer.getFocusedItemIndex()).toBe(4) + expect(focusContainer.getFocusedIndex()).toBe(4) }) test('should not set focus index function', () => { const setFocusAt = jest.fn() const focusContainer = createFocusContainer({ itemsCount: 5, setFocusAtFn: setFocusAt }) - focusContainer.syncFocusedItemIndex(4) + focusContainer.syncFocusedIndex(4) expect(setFocusAt).not.toBeCalled() }) }) @@ -34,10 +34,10 @@ describe('Focus Container', () => { describe('move previous', () => { test('should decrement index of focused item', () => { const focusContainer = createFocusContainer({ itemsCount: 5 }) - focusContainer.syncFocusedItemIndex(4) + focusContainer.syncFocusedIndex(4) focusContainer.movePrevious() - expect(focusContainer.getFocusedItemIndex()).toBe(3) + expect(focusContainer.getFocusedIndex()).toBe(3) }) test('should call set focus index function if there are any items', () => { @@ -58,21 +58,21 @@ describe('Focus Container', () => { test('focused item index should not ever become less than 0', () => { const focusContainer = createFocusContainer({ itemsCount: 5 }) - focusContainer.syncFocusedItemIndex(0) + focusContainer.syncFocusedIndex(0) focusContainer.movePrevious() - expect(focusContainer.getFocusedItemIndex()).toBe(0) + expect(focusContainer.getFocusedIndex()).toBe(0) }) }) describe('move next', () => { test('should increment index of focused item', () => { const focusContainer = createFocusContainer({ itemsCount: 5 }) - focusContainer.syncFocusedItemIndex(3) + focusContainer.syncFocusedIndex(3) focusContainer.moveNext() - expect(focusContainer.getFocusedItemIndex()).toBe(4) + expect(focusContainer.getFocusedIndex()).toBe(4) }) test('should call set focus index function if there are any items', () => { @@ -93,21 +93,21 @@ describe('Focus Container', () => { test('focused item index should not exceed range of valid indexes', () => { const focusContainer = createFocusContainer({ itemsCount: 5 }) - focusContainer.syncFocusedItemIndex(4) + focusContainer.syncFocusedIndex(4) focusContainer.moveNext() - expect(focusContainer.getFocusedItemIndex()).toBe(4) + expect(focusContainer.getFocusedIndex()).toBe(4) }) }) describe('move first', () => { test('should set focused item index to 0', () => { const focusContainer = createFocusContainer({ itemsCount: 5 }) - focusContainer.syncFocusedItemIndex(3) + focusContainer.syncFocusedIndex(3) focusContainer.moveFirst() - expect(focusContainer.getFocusedItemIndex()).toBe(0) + expect(focusContainer.getFocusedIndex()).toBe(0) }) test('should call set focus index function if there are any items', () => { @@ -130,10 +130,10 @@ describe('Focus Container', () => { describe('move last', () => { test('should set focused item index to last index of valid range', () => { const focusContainer = createFocusContainer({ itemsCount: 5 }) - focusContainer.syncFocusedItemIndex(2) + focusContainer.syncFocusedIndex(2) focusContainer.moveLast() - expect(focusContainer.getFocusedItemIndex()).toBe(4) + expect(focusContainer.getFocusedIndex()).toBe(4) }) test('should call set focus index function if there are any items', () => { From 0daeb913727e6f78aab33d6f5476982c1fc2c2a9 Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Thu, 13 Dec 2018 17:29:51 +0100 Subject: [PATCH 09/18] Minor improvements --- src/components/List/List.tsx | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/components/List/List.tsx b/src/components/List/List.tsx index a830e13bf4..2ff35c3c31 100644 --- a/src/components/List/List.tsx +++ b/src/components/List/List.tsx @@ -143,15 +143,6 @@ class List extends AutoControlledComponent, ListState> { const { items } = this.props const { focusedIndex, selectedIndex } = this.state - const getTabIndex = (idx): number => { - // return tabindex=0 if the item was selected - if (selectedIndex !== -1) { - return idx === selectedIndex ? 0 : -1 - } - - return idx === focusedIndex ? 0 : -1 - } - this.itemRefs = [] return _.map(items, (item, idx) => { @@ -163,9 +154,12 @@ class List extends AutoControlledComponent, ListState> { maybeSelectableItemProps.ref = ref maybeSelectableItemProps.onFocus = () => this.focusHandler.syncFocusedIndex(idx) - maybeSelectableItemProps.onClick = () => this.trySetState({ selectedIndex: idx }) + maybeSelectableItemProps.onClick = () => { + this.trySetState({ selectedIndex: idx }) + this.setState({ focusedIndex: idx }) + } maybeSelectableItemProps.selected = idx === selectedIndex - maybeSelectableItemProps.tabIndex = getTabIndex(idx) + maybeSelectableItemProps.tabIndex = idx === focusedIndex ? 0 : -1 } const itemProps = { From bc7467044064bd985f08ebd4f5c1051eaf3d6868 Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Thu, 13 Dec 2018 17:32:31 +0100 Subject: [PATCH 10/18] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5c8b50b22..7eb9d74820 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix `Popup` not closing on outside click @kuzhelov ([#598](https://github.com/stardust-ui/react/pull/598)) - Fix multiple React's warnings about keys in docs @layershifter ([#602](https://github.com/stardust-ui/react/pull/602)) - Fix incorrect handling of `isFromKeyboard` in `Menu` @layershifter ([#596](https://github.com/stardust-ui/react/pull/596)) +- Fix `List` - items should be selectable @sophieH29 ([#566](https://github.com/stardust-ui/react/pull/566)) ### Features - `Ref` components uses `forwardRef` API by default @layershifter ([#491](https://github.com/stardust-ui/react/pull/491)) From a1f4fa09d620212de39172ee47b700cb64134e9b Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Mon, 17 Dec 2018 13:41:08 +0100 Subject: [PATCH 11/18] Add onSelectedIndexChange --- ...tExampleSelectableControlled.shorthand.tsx | 48 +++++++++++++++++++ .../examples/components/List/Types/index.tsx | 7 ++- src/components/List/List.tsx | 16 ++++++- 3 files changed, 68 insertions(+), 3 deletions(-) create mode 100644 docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx diff --git a/docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx b/docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx new file mode 100644 index 0000000000..1519572b6e --- /dev/null +++ b/docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx @@ -0,0 +1,48 @@ +import * as React from 'react' +import { List, Image } from '@stardust-ui/react' + +class SelectableListControlledExample extends React.Component { + state = { selectedItemIndex: -1 } + + items = [ + { + key: 'irving', + media: , + header: 'Irving Kuhic', + headerMedia: '7:26:56 AM', + content: 'Program the sensor to the SAS alarm through the haptic SQL card!', + }, + { + key: 'skyler', + media: , + header: 'Skyler Parks', + headerMedia: '11:30:17 PM', + content: 'Use the online FTP application to input the multi-byte application!', + }, + { + key: 'dante', + media: , + header: 'Dante Schneider', + headerMedia: '5:22:40 PM', + content: 'The GB pixel is down, navigate the virtual interface!', + }, + ] + + render() { + return ( + { + alert( + `List is requested to change its selectedIndex state to "${newProps.selectedIndex}"`, + ) + this.setState({ selectedItemIndex: newProps.selectedIndex }) + }} + items={this.items} + /> + ) + } +} + +export default SelectableListControlledExample diff --git a/docs/src/examples/components/List/Types/index.tsx b/docs/src/examples/components/List/Types/index.tsx index e4f6a7f980..b46d5f494c 100644 --- a/docs/src/examples/components/List/Types/index.tsx +++ b/docs/src/examples/components/List/Types/index.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import * as React from 'react' import ComponentExample from 'docs/src/components/ComponentDoc/ComponentExample' import ExampleSection from 'docs/src/components/ComponentDoc/ExampleSection' @@ -14,6 +14,11 @@ const Types = () => ( description="A list can be formatted to indicate that its items can be selected." examplePath="components/List/Types/ListExampleSelectable" /> + ) diff --git a/src/components/List/List.tsx b/src/components/List/List.tsx index 2ff35c3c31..106aa06097 100644 --- a/src/components/List/List.tsx +++ b/src/components/List/List.tsx @@ -15,7 +15,7 @@ import ListItem from './ListItem' import { listBehavior } from '../../lib/accessibility' import { Accessibility, AccessibilityActionHandlers } from '../../lib/accessibility/types' import { ContainerFocusHandler } from '../../lib/accessibility/FocusHandling/FocusContainer' -import { Extendable, ShorthandValue } from '../../../types/utils' +import { Extendable, ShorthandValue, ComponentEventHandler } from '../../../types/utils' export interface ListProps extends UIComponentProps, ChildrenComponentProps { /** @@ -39,6 +39,13 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps { /** Initial selectedIndex value. */ defaultSelectedIndex?: number + /** + * Event for request to change 'selectedIndex' value. + * @param {SyntheticEvent} event - React's original SyntheticEvent. + * @param {object} data - All props and proposed value. + */ + onSelectedIndexChange?: ComponentEventHandler + /** Truncates content */ truncateContent?: boolean @@ -71,6 +78,7 @@ class List extends AutoControlledComponent, ListState> { truncateHeader: PropTypes.bool, selectedIndex: PropTypes.number, defaultSelectedIndex: PropTypes.number, + onSelectedIndexChange: PropTypes.func, } static defaultProps = { @@ -154,8 +162,12 @@ class List extends AutoControlledComponent, ListState> { maybeSelectableItemProps.ref = ref maybeSelectableItemProps.onFocus = () => this.focusHandler.syncFocusedIndex(idx) - maybeSelectableItemProps.onClick = () => { + maybeSelectableItemProps.onClick = e => { this.trySetState({ selectedIndex: idx }) + _.invoke(this.props, 'onSelectedIndexChange', e, { + ...this.props, + ...{ selectedIndex: idx }, + }) this.setState({ focusedIndex: idx }) } maybeSelectableItemProps.selected = idx === selectedIndex From f7941405c165a40740d244221e5836b17f7a668c Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Mon, 17 Dec 2018 14:33:45 +0100 Subject: [PATCH 12/18] Add some tests --- test/specs/components/List/List-test.ts | 13 ---- test/specs/components/List/List-test.tsx | 76 ++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 13 deletions(-) delete mode 100644 test/specs/components/List/List-test.ts create mode 100644 test/specs/components/List/List-test.tsx diff --git a/test/specs/components/List/List-test.ts b/test/specs/components/List/List-test.ts deleted file mode 100644 index 60f4471d91..0000000000 --- a/test/specs/components/List/List-test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { isConformant, handlesAccessibility } from 'test/specs/commonTests' - -import List from 'src/components/List/List' -import implementsCollectionShorthandProp from '../../commonTests/implementsCollectionShorthandProp' -import ListItem from 'src/components/List/ListItem' - -const listImplementsCollectionShorthandProp = implementsCollectionShorthandProp(List) - -describe('List', () => { - isConformant(List) - handlesAccessibility(List, { defaultRootRole: 'list' }) - listImplementsCollectionShorthandProp('items', ListItem, { mapsValueToProp: 'content' }) -}) diff --git a/test/specs/components/List/List-test.tsx b/test/specs/components/List/List-test.tsx new file mode 100644 index 0000000000..ae7fe610bf --- /dev/null +++ b/test/specs/components/List/List-test.tsx @@ -0,0 +1,76 @@ +import * as React from 'react' + +import { isConformant, handlesAccessibility } from 'test/specs/commonTests' +import { mountWithProvider } from 'test/utils' + +import List from 'src/components/List/List' +import implementsCollectionShorthandProp from '../../commonTests/implementsCollectionShorthandProp' +import ListItem from 'src/components/List/ListItem' + +const listImplementsCollectionShorthandProp = implementsCollectionShorthandProp(List) + +describe('List', () => { + isConformant(List) + handlesAccessibility(List, { defaultRootRole: 'list' }) + listImplementsCollectionShorthandProp('items', ListItem, { mapsValueToProp: 'content' }) + + const getItems = () => [ + { key: 'irving', content: 'Irving', onClick: jest.fn() }, + { key: 'skyler', content: 'Skyler' }, + { key: 'dante', content: 'Dante' }, + ] + + describe('items', () => { + it('renders children', () => { + const listItems = mountWithProvider().find('ListItem') + expect(listItems.length).toBe(3) + expect(listItems.first().props().content).toBe('Irving') + expect(listItems.last().props().content).toBe('Dante') + }) + + it('calls onClick handler for item', () => { + const items = getItems() + const listItems = mountWithProvider().find('ListItem') + + listItems + .first() + .find('li') + .first() + .simulate('click') + expect(items[0].onClick).toHaveBeenCalled() + }) + }) + + describe('selectedIndex', () => { + it('should not be set by default', () => { + const listItems = mountWithProvider().find('ListItem') + expect(listItems.everyWhere(item => !item.props().selected)).toBe(true) + }) + + it('can be set a default value', () => { + const listItems = mountWithProvider( + , + ).find('ListItem') + expect(listItems.first().props().selected).toBe(true) + }) + + it('should be set when item is clicked', () => { + const wrapper = mountWithProvider( + , + ) + const listItems = wrapper.find('ListItem') + expect(listItems.at(0).props().selected).toBe(true) + + listItems + .at(1) + .find('li') + .first() + .simulate('click') + + const updatedItems = wrapper.find('ListItem') + + expect(updatedItems.at(0).props().selected).toBe(false) + expect(updatedItems.at(1).props().selected).toBe(true) + }) + }) +}) From 450ad62afecbc53d9f0016ba7ef9df915364ebb4 Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Tue, 18 Dec 2018 17:21:38 +0100 Subject: [PATCH 13/18] Small improvements afer CR --- .../Types/ListExampleSelectableControlled.shorthand.tsx | 6 +++--- docs/src/examples/components/List/Types/index.tsx | 2 +- src/lib/accessibility/FocusHandling/FocusContainer.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx b/docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx index 1519572b6e..f493b03105 100644 --- a/docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx +++ b/docs/src/examples/components/List/Types/ListExampleSelectableControlled.shorthand.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { List, Image } from '@stardust-ui/react' class SelectableListControlledExample extends React.Component { - state = { selectedItemIndex: -1 } + state = { selectedIndex: -1 } items = [ { @@ -32,12 +32,12 @@ class SelectableListControlledExample extends React.Component { return ( { alert( `List is requested to change its selectedIndex state to "${newProps.selectedIndex}"`, ) - this.setState({ selectedItemIndex: newProps.selectedIndex }) + this.setState({ selectedIndex: newProps.selectedIndex }) }} items={this.items} /> diff --git a/docs/src/examples/components/List/Types/index.tsx b/docs/src/examples/components/List/Types/index.tsx index b46d5f494c..7483e6ff44 100644 --- a/docs/src/examples/components/List/Types/index.tsx +++ b/docs/src/examples/components/List/Types/index.tsx @@ -16,7 +16,7 @@ const Types = () => ( /> diff --git a/src/lib/accessibility/FocusHandling/FocusContainer.ts b/src/lib/accessibility/FocusHandling/FocusContainer.ts index be35d31f10..ef2aea39bd 100644 --- a/src/lib/accessibility/FocusHandling/FocusContainer.ts +++ b/src/lib/accessibility/FocusHandling/FocusContainer.ts @@ -7,7 +7,7 @@ export class ContainerFocusHandler { private noItems = (): boolean => this.getItemsCount() === 0 - private constrainfocusedIndex(): void { + private constrainFocusedIndex(): void { if (this.focusedIndex < 0) { this.focusedIndex = 0 } @@ -32,7 +32,7 @@ export class ContainerFocusHandler { } this.focusedIndex -= 1 - this.constrainfocusedIndex() + this.constrainFocusedIndex() this.setFocusAt(this.focusedIndex) } @@ -43,7 +43,7 @@ export class ContainerFocusHandler { } this.focusedIndex += 1 - this.constrainfocusedIndex() + this.constrainFocusedIndex() this.setFocusAt(this.focusedIndex) } From 91ba179b0fd3f7561bb8a0aa3f04fd8175c56de5 Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Tue, 18 Dec 2018 18:04:13 +0100 Subject: [PATCH 14/18] Small improvements afer CR --- src/components/List/ListItem.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/List/ListItem.tsx b/src/components/List/ListItem.tsx index 5514bdcf1e..b6af86fd62 100644 --- a/src/components/List/ListItem.tsx +++ b/src/components/List/ListItem.tsx @@ -32,6 +32,7 @@ export interface ListItemProps extends UIComponentProps, ContentComponentProps> { accessibility: listItemBehavior as Accessibility, } - private itemRef = React.createRef() - protected actionHandlers: AccessibilityActionHandlers = { performClick: event => { this.handleClick(event) @@ -132,7 +131,6 @@ class ListItem extends UIComponent> { headerCSS={styles.header} headerMediaCSS={styles.headerMedia} contentCSS={styles.content} - ref={this.itemRef} onClick={this.handleClick} {...accessibility.attributes.root} {...accessibility.keyHandlers.root} From 9851381a519e3586cda1681c4653ab0a82094f65 Mon Sep 17 00:00:00 2001 From: Sofiya Huts Date: Tue, 18 Dec 2018 18:32:33 +0100 Subject: [PATCH 15/18] Small improvements afer CR --- src/components/List/List.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/List/List.tsx b/src/components/List/List.tsx index 106aa06097..456b19d06e 100644 --- a/src/components/List/List.tsx +++ b/src/components/List/List.tsx @@ -150,6 +150,7 @@ class List extends AutoControlledComponent, ListState> { renderItems() { const { items } = this.props const { focusedIndex, selectedIndex } = this.state + this.focusHandler && this.focusHandler.syncFocusedIndex(focusedIndex) this.itemRefs = [] @@ -161,14 +162,13 @@ class List extends AutoControlledComponent, ListState> { this.itemRefs[idx] = ref maybeSelectableItemProps.ref = ref - maybeSelectableItemProps.onFocus = () => this.focusHandler.syncFocusedIndex(idx) + maybeSelectableItemProps.onFocus = () => this.setState({ focusedIndex: idx }) maybeSelectableItemProps.onClick = e => { this.trySetState({ selectedIndex: idx }) _.invoke(this.props, 'onSelectedIndexChange', e, { ...this.props, ...{ selectedIndex: idx }, }) - this.setState({ focusedIndex: idx }) } maybeSelectableItemProps.selected = idx === selectedIndex maybeSelectableItemProps.tabIndex = idx === focusedIndex ? 0 : -1 From f1cc12c143cba6873b91b06a65ad7d51ade109fc Mon Sep 17 00:00:00 2001 From: kuzhelov Date: Wed, 19 Dec 2018 01:08:35 +0100 Subject: [PATCH 16/18] create focus handler when List is constructed --- src/components/List/List.tsx | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/components/List/List.tsx b/src/components/List/List.tsx index 456b19d06e..009b69940e 100644 --- a/src/components/List/List.tsx +++ b/src/components/List/List.tsx @@ -118,6 +118,22 @@ class List extends AutoControlledComponent, ListState> { }, } + constructor(props, context) { + super(props, context) + + this.focusHandler = new ContainerFocusHandler( + () => this.props.items.length, + index => { + this.setState({ focusedIndex: index }, () => { + const targetComponent = this.itemRefs[index] && this.itemRefs[index].current + const targetDomNode = ReactDOM.findDOMNode(targetComponent) as any + + targetDomNode && targetDomNode.focus() + }) + }, + ) + } + renderComponent({ ElementType, classes, accessibility, rest }) { const { children } = this.props @@ -133,24 +149,11 @@ class List extends AutoControlledComponent, ListState> { ) } - componentDidMount() { - this.focusHandler = new ContainerFocusHandler( - () => this.props.items.length, - index => { - this.setState({ focusedIndex: index }, () => { - const targetComponent = this.itemRefs[index] && this.itemRefs[index].current - const targetDomNode = ReactDOM.findDOMNode(targetComponent) as any - - targetDomNode && targetDomNode.focus() - }) - }, - ) - } - renderItems() { const { items } = this.props const { focusedIndex, selectedIndex } = this.state - this.focusHandler && this.focusHandler.syncFocusedIndex(focusedIndex) + + this.focusHandler.syncFocusedIndex(focusedIndex) this.itemRefs = [] From e0da3524629ab2fb427dc142631ab19995999b08 Mon Sep 17 00:00:00 2001 From: kuzhelov Date: Wed, 19 Dec 2018 01:11:20 +0100 Subject: [PATCH 17/18] fix changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d58f559cb..87e660b123 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixes - Ensure `Popup` properly flips values of `offset` prop in RTL @kuzhelov ([#612](https://github.com/stardust-ui/react/pull/612)) +- Fix `List` - items should be selectable @sophieH29 ([#566](https://github.com/stardust-ui/react/pull/566)) ### Features - Add `color` prop to `Text` component @Bugaa92 ([#597](https://github.com/stardust-ui/react/pull/597)) @@ -51,7 +52,6 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix `Popup` not closing on outside click @kuzhelov ([#598](https://github.com/stardust-ui/react/pull/598)) - Fix multiple React's warnings about keys in docs @layershifter ([#602](https://github.com/stardust-ui/react/pull/602)) - Fix incorrect handling of `isFromKeyboard` in `Menu` @layershifter ([#596](https://github.com/stardust-ui/react/pull/596)) -- Fix `List` - items should be selectable @sophieH29 ([#566](https://github.com/stardust-ui/react/pull/566)) ### Features - `Ref` components uses `forwardRef` API by default @layershifter ([#491](https://github.com/stardust-ui/react/pull/491)) From e093f16d14b66dc256447343586c830f918ac0c1 Mon Sep 17 00:00:00 2001 From: kuzhelov Date: Wed, 19 Dec 2018 01:12:18 +0100 Subject: [PATCH 18/18] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87e660b123..7006d2dd84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - Fix `Popup` not closing on outside click @kuzhelov ([#598](https://github.com/stardust-ui/react/pull/598)) - Fix multiple React's warnings about keys in docs @layershifter ([#602](https://github.com/stardust-ui/react/pull/602)) - Fix incorrect handling of `isFromKeyboard` in `Menu` @layershifter ([#596](https://github.com/stardust-ui/react/pull/596)) +- Fix property names used in shorthand factories @kuzhelov ([#591](https://github.com/stardust-ui/react/pull/591)) ### Features - `Ref` components uses `forwardRef` API by default @layershifter ([#491](https://github.com/stardust-ui/react/pull/491))