Skip to content
This repository has been archived by the owner on Mar 4, 2020. It is now read-only.

fix(SelectableList): Items in list should be selectable #566

Merged
merged 39 commits into from
Dec 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c191063
Reflect which item is selected in list
Dec 4, 2018
ed57ca8
Make list derived from autocontrolled component
Dec 5, 2018
29a06d2
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 5, 2018
9488202
small fix
Dec 5, 2018
dfce6d2
Merge branch 'fix/selectable-list-aria-selected' of https://github.co…
Dec 5, 2018
011a389
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 5, 2018
4a39717
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 6, 2018
29abd1a
Update ListExampleSelection.tsx
sophieH29 Dec 6, 2018
12d5caa
Update ListExampleSelection.shorthand.tsx
sophieH29 Dec 6, 2018
1242d6c
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 6, 2018
8238840
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 7, 2018
058d4d9
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 11, 2018
be1d9a8
Small improvement
Dec 11, 2018
d3070b6
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 11, 2018
4518290
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 11, 2018
f546086
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 11, 2018
0957b11
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 12, 2018
a45567e
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 12, 2018
75ab184
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 13, 2018
f1b2ae4
Rename *ItemIndex -> *Index
Dec 13, 2018
381d1e6
Merge latest master
Dec 13, 2018
46c33ac
Names refactoring
Dec 13, 2018
0daeb91
Minor improvements
Dec 13, 2018
bc74670
update changelog
Dec 13, 2018
727a066
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 14, 2018
e589ca9
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 14, 2018
a1f4fa0
Add onSelectedIndexChange
Dec 17, 2018
f794140
Add some tests
Dec 17, 2018
a45a820
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 18, 2018
c6e7435
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 18, 2018
72f3853
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 18, 2018
7937b90
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 18, 2018
450ad62
Small improvements afer CR
Dec 18, 2018
91ba179
Small improvements afer CR
Dec 18, 2018
9851381
Small improvements afer CR
Dec 18, 2018
5dc1695
Merge branch 'master' into fix/selectable-list-aria-selected
sophieH29 Dec 18, 2018
f1cc12c
create focus handler when List is constructed
kuzhelov-ms Dec 19, 2018
e0da352
fix changelog
kuzhelov-ms Dec 19, 2018
e093f16
changelog
kuzhelov-ms Dec 19, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ const items = [
},
]

const ListExample = () => <List items={items} selection />
const ListExample = () => <List items={items} selectable />

export default ListExample
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ const ListExample = () => (
<List.Item
content="Program the sensor to the SAS alarm through the haptic SQL card!"
endMedia={ellipsis}
selection
selectable
/>
<List.Item
content="Use the online FTP application to input the multi-byte application!"
endMedia={ellipsis}
selection
selectable
/>
<List.Item
content="The GB pixel is down, navigate the virtual interface!"
endMedia={ellipsis}
selection
selectable
/>
</List>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ const items = [
},
]

const ListExampleSelection = ({ knobs }) => <List debug={knobs.debug} items={items} />
const ListExampleSelectable = ({ knobs }) => <List debug={knobs.debug} items={items} />

export default ListExampleSelection
export default ListExampleSelectable
4 changes: 2 additions & 2 deletions docs/src/examples/components/List/Types/ListExample.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import { List, Image } from '@stardust-ui/react'

const ListExampleSelection = ({ knobs }) => (
const ListExampleSelectable = ({ knobs }) => (
<List debug={knobs.debug}>
<List.Item
media={<Image src="public/images/avatar/small/matt.jpg" avatar />}
Expand All @@ -24,4 +24,4 @@ const ListExampleSelection = ({ knobs }) => (
</List>
)

export default ListExampleSelection
export default ListExampleSelectable
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ const items = [
},
]

const selection = knobs => (knobs === undefined ? true : knobs.selection)
const ListExampleSelectable = () => <List selectable defaultSelectedIndex={0} items={items} />

const ListExampleSelection = ({ knobs }) => <List selection={selection(knobs)} items={items} />

export default ListExampleSelection
export default ListExampleSelectable
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
import React from 'react'
import { List, Image } from '@stardust-ui/react'

const selection = knobs => (knobs === undefined ? true : knobs.selection)

const ListExampleSelection = ({ knobs }) => (
<List selection={selection(knobs)}>
const ListExampleSelectable = () => (
<List selectable>
<List.Item
media={<Image src="public/images/avatar/small/matt.jpg" avatar />}
header="Irving Kuhic"
headerMedia="7:26:56 AM"
content="Program the sensor to the SAS alarm through the haptic SQL card!"
selection={selection(knobs)}
selectable
/>
<List.Item
media={<Image src="public/images/avatar/small/steve.jpg" avatar />}
header="Skyler Parks"
headerMedia="11:30:17 PM"
content="Use the online FTP application to input the multi-byte application!"
selection={selection(knobs)}
selectable
/>
<List.Item
media={<Image src="public/images/avatar/small/nom.jpg" avatar />}
header="Dante Schneider"
headerMedia="5:22:40 PM"
content="The GB pixel is down, navigate the virtual interface!"
selection={selection(knobs)}
selectable
/>
</List>
)

export default ListExampleSelection
export default ListExampleSelectable
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as React from 'react'
import { List, Image } from '@stardust-ui/react'

class SelectableListControlledExample extends React.Component<any, any> {
state = { selectedIndex: -1 }

items = [
{
key: 'irving',
media: <Image src="public/images/avatar/small/matt.jpg" avatar />,
header: 'Irving Kuhic',
headerMedia: '7:26:56 AM',
content: 'Program the sensor to the SAS alarm through the haptic SQL card!',
},
{
key: 'skyler',
media: <Image src="public/images/avatar/small/steve.jpg" avatar />,
header: 'Skyler Parks',
headerMedia: '11:30:17 PM',
content: 'Use the online FTP application to input the multi-byte application!',
},
{
key: 'dante',
media: <Image src="public/images/avatar/small/nom.jpg" avatar />,
header: 'Dante Schneider',
headerMedia: '5:22:40 PM',
content: 'The GB pixel is down, navigate the virtual interface!',
},
]

render() {
return (
<List
selectable
selectedIndex={this.state.selectedIndex}
onSelectedIndexChange={(e, newProps) => {
alert(
`List is requested to change its selectedIndex state to "${newProps.selectedIndex}"`,
)
this.setState({ selectedIndex: newProps.selectedIndex })
}}
items={this.items}
/>
)
}
}

export default SelectableListControlledExample

This file was deleted.

11 changes: 8 additions & 3 deletions docs/src/examples/components/List/Types/index.tsx
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -10,9 +10,14 @@ const Types = () => (
examplePath="components/List/Types/ListExample"
/>
<ComponentExample
title="Selection"
title="Selectable list"
description="A list can be formatted to indicate that its items can be selected."
examplePath="components/List/Types/ListExampleSelection"
examplePath="components/List/Types/ListExampleSelectable"
/>
<ComponentExample
title="Controlled selectable list"
description="List can handle selected index in controlled mode."
examplePath="components/List/Types/ListExampleSelectableControlled"
/>
</ExampleSection>
)
Expand Down
2 changes: 1 addition & 1 deletion docs/src/prototypes/SearchPage/SearchPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ class SearchPage extends React.Component<SearchPageState, any> {
Results <strong>{results.length}</strong> of <strong>{DATA_RECORDS.length}</strong>
</small>
</p>
<List selection items={results.map(dataRecordToListItem)} />
<List selectable items={results.map(dataRecordToListItem)} />
</div>
)}
</Segment>
Expand Down
90 changes: 60 additions & 30 deletions src/components/List/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as PropTypes from 'prop-types'
import {
customPropTypes,
childrenExist,
UIComponent,
AutoControlledComponent,
UIComponentProps,
ChildrenComponentProps,
commonPropTypes,
Expand All @@ -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 {
/**
Expand All @@ -30,8 +30,21 @@ 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

/** 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<ListProps>

/** Truncates content */
truncateContent?: boolean
Expand All @@ -41,13 +54,14 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps {
}

export interface ListState {
selectedItemIndex: number
focusedIndex: number
selectedIndex?: number
}

/**
* A list displays a group of related content.
*/
class List extends UIComponent<Extendable<ListProps>, ListState> {
class List extends AutoControlledComponent<Extendable<ListProps>, ListState> {
static displayName = 'List'

static className = 'ui-list'
Expand All @@ -59,24 +73,28 @@ class List extends UIComponent<Extendable<ListProps>, 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,
sophieH29 marked this conversation as resolved.
Show resolved Hide resolved
defaultSelectedIndex: PropTypes.number,
onSelectedIndexChange: PropTypes.func,
}

static defaultProps = {
as: 'ul',
accessibility: listBehavior as Accessibility,
}

static autoControlledProps = ['selectedIndex']
getInitialAutoControlledState() {
return { selectedIndex: -1, focusedIndex: 0 }
}

static Item = ListItem

// List props that are passed to each individual Item props
static itemProps = ['debug', 'selection', 'truncateContent', 'truncateHeader', 'variables']

public state = {
selectedItemIndex: 0,
}
static itemProps = ['debug', 'selectable', 'truncateContent', 'truncateHeader', 'variables']

private focusHandler: ContainerFocusHandler = null
private itemRefs = []
Expand All @@ -100,6 +118,22 @@ class List extends UIComponent<Extendable<ListProps>, 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

Expand All @@ -115,36 +149,32 @@ class List extends UIComponent<Extendable<ListProps>, ListState> {
)
}

componentDidMount() {
this.focusHandler = new ContainerFocusHandler(
() => this.props.items.length,
index => {
this.setState({ selectedItemIndex: 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 { selectedItemIndex } = this.state
const { focusedIndex, selectedIndex } = this.state

this.focusHandler.syncFocusedIndex(focusedIndex)

this.itemRefs = []

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.tabIndex = idx === selectedItemIndex ? 0 : -1
maybeSelectableItemProps.ref = ref
maybeSelectableItemProps.onFocus = () => this.focusHandler.syncFocusedItemIndex(idx)
maybeSelectableItemProps.onFocus = () => this.setState({ focusedIndex: idx })
maybeSelectableItemProps.onClick = e => {
this.trySetState({ selectedIndex: idx })
_.invoke(this.props, 'onSelectedIndexChange', e, {
...this.props,
...{ selectedIndex: idx },
})
}
maybeSelectableItemProps.selected = idx === selectedIndex
maybeSelectableItemProps.tabIndex = idx === focusedIndex ? 0 : -1
}

const itemProps = {
Expand Down
Loading