Skip to content
This repository was archived by the owner on Mar 4, 2020. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -30,6 +30,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Features
- Add `TextArea` component @lucivpav ([#1897](https://github.com/stardust-ui/react/pull/1897))
- Export `bell-slash` and `bell-snooze` icon to Teams theme @musingh ([#1921](https://github.com/stardust-ui/react/pull/1921))
- Add `navigable` `List` variant @jurokapsiar ([#1904](https://github.com/stardust-ui/react/pull/1904))
- Add the `SplitButton` component @silviuavram ([#1789](https://github.com/stardust-ui/react/pull/1798))

### Documentation
Expand Down
5 changes: 5 additions & 0 deletions docs/src/components/Sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ class Sidebar extends React.Component<any, any> {
title: { content: 'Mentions', as: NavLink, to: '/prototype-mentions' },
public: true,
},
{
key: 'participants-list',
title: { content: 'Participants list', as: NavLink, to: '/prototype-participants-list' },
public: true,
},
{
key: 'searchpage',
title: { content: 'Search Page', as: NavLink, to: '/prototype-search-page' },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { List } from '@stardust-ui/react'

const selectors = {
list: `.${List.className}`,
item: (itemIndex: number) =>
`.${List.className} .${List.Item.className}:nth-of-type(${itemIndex})`,
}

const config: ScreenerTestsConfig = {
themes: ['teams', 'teamsDark', 'teamsHighContrast'],
steps: [
(builder, keys) =>
builder
.hover(selectors.item(2))
.snapshot('Highlights an item')
.click(selectors.item(2))
.snapshot('Clicks on an item')
.hover(selectors.item(3))
.snapshot('Highlights another item')
.keys(selectors.item(2), keys.downArrow)
.snapshot('Focuses last item using keyboard'),
(builder, keys) => builder.keys('body', keys.tab).snapshot('Focuses item'),
],
}

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

const 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!',
},
]

const ListExampleNavigable = () => <List navigable items={items} />

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

const ListExampleNavigable = () => (
<List navigable>
<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!"
navigable
/>
<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!"
navigable
/>
<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!"
navigable
/>
</List>
)

export default ListExampleNavigable
5 changes: 5 additions & 0 deletions docs/src/examples/components/List/Types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ const Types = () => (
description="List can handle selected index in controlled mode."
examplePath="components/List/Types/ListExampleSelectableControlled"
/>
<ComponentExample
title="Navigable list"
description="List can allow the user to navigate through the list items"
examplePath="components/List/Types/ListExampleNavigable"
/>
</ExampleSection>
)

Expand Down
56 changes: 56 additions & 0 deletions docs/src/prototypes/ParticipantsList/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from 'react'
import { List, Avatar, Flex, Text, MenuButton, Icon } from '@stardust-ui/react'
import { PrototypeSection, ComponentPrototype } from '../Prototypes'

const menu = ['Open', 'Remove from list']

const ActiveBarItem = props => (
<Flex column hAlign="center" padding="padding.medium">
<Avatar name={props.name} size="largest" />
<Flex gap="gap.small">
<Text content={props.name} />
<MenuButton trigger={<Icon name="more" />} menu={menu} />
</Flex>
</Flex>
)

const addContextMenu = item => render =>
render(item, (Component, props) => {
return <MenuButton contextMenu trigger={<Component {...props} />} menu={menu} />
})

const items3 = [
{
key: 'irving',
content: <ActiveBarItem name="Irving Kuhic" />,
},
{
key: 'skyler',
content: <ActiveBarItem name="Skyler Parks" />,
},
{
key: 'dante',
content: <ActiveBarItem name="Dante Schneider" />,
},
].map(addContextMenu)

const ParticipantsList = () => (
<>
<List navigable items={items3} horizontal />
</>
)

const ParticipantsListPrototype: React.FC = () => {
return (
<PrototypeSection title="Participants list">
<ComponentPrototype
title="List with context menu"
description="Context menu can be opened by clicking on the more button or by right mouse button"
>
<ParticipantsList />
</ComponentPrototype>
</PrototypeSection>
)
}

export default ParticipantsListPrototype
2 changes: 2 additions & 0 deletions docs/src/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import MenuButtonPrototype from './prototypes/MenuButton'
import AlertsPrototype from './prototypes/alerts'
import NestedPopupsAndDialogsPrototype from './prototypes/NestedPopupsAndDialogs'
import CopyToClipboardPrototype from './prototypes/CopyToClipboard'
import ParticipantsListPrototype from './prototypes/ParticipantsList'

const Routes = () => (
<BrowserRouter basename={__BASENAME__}>
Expand All @@ -63,6 +64,7 @@ const Routes = () => (
<Route exact path="/prototype-async-shorthand" component={AsyncShorthandPrototype} />
<Route exact path="/prototype-employee-card" component={EmployeeCardPrototype} />
<Route exact path="/prototype-meeting-options" component={MeetingOptionsPrototype} />
<Route exact path="/prototype-participants-list" component={ParticipantsListPrototype} />
<Route exact path="/prototype-search-page" component={SearchPagePrototype} />
<Route exact path="/prototype-mentions" component={MentionsPrototype} />
<Route exact path="/prototype-dropdowns" component={DropdownsPrototype} />
Expand Down
28 changes: 15 additions & 13 deletions packages/react/src/components/List/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps {
/** A selectable list formats list items as possible choices. */
selectable?: boolean

/** A navigable list allows user to navigate through items. */
navigable?: boolean

/** Index of the currently selected item. */
selectedIndex?: number

Expand All @@ -63,7 +66,6 @@ export interface ListProps extends UIComponentProps, ChildrenComponentProps {
}

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

Expand All @@ -82,7 +84,8 @@ class List extends AutoControlledComponent<WithAsProp<ListProps>, ListState> {
}),
debug: PropTypes.bool,
items: customPropTypes.collectionShorthand,
selectable: PropTypes.bool,
selectable: customPropTypes.every([customPropTypes.disallow(['navigable']), PropTypes.bool]),
navigable: customPropTypes.every([customPropTypes.disallow(['selectable']), PropTypes.bool]),
truncateContent: PropTypes.bool,
truncateHeader: PropTypes.bool,
selectedIndex: PropTypes.number,
Expand All @@ -99,25 +102,25 @@ class List extends AutoControlledComponent<WithAsProp<ListProps>, ListState> {
static autoControlledProps = ['selectedIndex']

getInitialAutoControlledState() {
return { selectedIndex: -1, focusedIndex: 0 }
return { selectedIndex: -1 }
}

static Item = ListItem

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

handleItemOverrides = (predefinedProps: ListItemProps) => {
const { selectable } = this.props

return {
onFocus: (e: React.SyntheticEvent, itemProps: ListItemProps) => {
_.invoke(predefinedProps, 'onFocus', e, itemProps)

if (selectable) {
this.setState({ focusedIndex: itemProps.index })
}
},
onClick: (e: React.SyntheticEvent, itemProps: ListItemProps) => {
_.invoke(predefinedProps, 'onClick', e, itemProps)

Expand Down Expand Up @@ -150,14 +153,13 @@ class List extends AutoControlledComponent<WithAsProp<ListProps>, ListState> {

renderItems() {
const { items, selectable } = this.props
const { focusedIndex, selectedIndex } = this.state
const { selectedIndex } = this.state

return _.map(items, (item, index) => {
const maybeSelectableItemProps = {} as any

if (selectable) {
maybeSelectableItemProps.selected = index === selectedIndex
maybeSelectableItemProps.tabIndex = index === focusedIndex ? 0 : -1
}

const itemProps = {
Expand Down
4 changes: 4 additions & 0 deletions packages/react/src/components/List/ListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export interface ListItemProps
/** A list item can indicate that it can be selected. */
selectable?: boolean

/** A list item can indicate that it can be navigable. */
navigable?: boolean

/** Indicates if the current list item is selected. */
selected?: boolean
truncateContent?: boolean
Expand Down Expand Up @@ -85,6 +88,7 @@ class ListItem extends UIComponent<WithAsProp<ListItemProps>> {
media: PropTypes.any,

selectable: PropTypes.bool,
navigable: PropTypes.bool,
index: PropTypes.number,
selected: PropTypes.bool,

Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/MenuButton/MenuButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@ export default class MenuButton extends AutoControlledComponent<MenuButtonProps,
})

const overrideProps: PopupProps = {
accessibility: () => accessibility,
open: this.state.open,
onOpenChange: this.handleOpenChange,
content: {
Expand All @@ -280,7 +281,6 @@ export default class MenuButton extends AutoControlledComponent<MenuButtonProps,
tabbableTrigger: false,
}
: {
accessibility: () => accessibility,
inline: true,
autoFocus: true,
}),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import { Accessibility } from '../../types'
import selectableListBehavior from './selectableListBehavior'
import basicListBehavior from './basicListBehavior'
import navigableListBehavior from './navigableListBehavior'

/**
* @description
* Defines a behavior 'BasicListBehavior' or 'SelectableListBehavior' based on property 'selectable'.
*/
const ListBehavior: Accessibility<ListBehaviorProps> = props =>
props.selectable ? selectableListBehavior(props) : basicListBehavior(props)
props.selectable
? selectableListBehavior(props)
: props.navigable
? navigableListBehavior(props)
: basicListBehavior(props)

export default ListBehavior

export type ListBehaviorProps = {
/** Indicates if a list is a selectable list. */
selectable?: boolean

/** Indicates if a list is a navigable list. */
navigable?: boolean

/** Indicates if the list is horizontal. */
horizontal?: boolean
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
import selectableListItemBehavior from './selectableListItemBehavior'
import basicListItemBehavior from './basicListItemBehavior'
import { Accessibility } from '../../types'
import navigableListItemBehavior from './navigableListItemBehavior'

/**
* @description
* Defines a behavior "BasicListItemBehavior" or "SelectableListItemBehavior" based on "selectable" property.
*/
const listItemBehavior: Accessibility<ListItemBehaviorProps> = props =>
props.selectable ? selectableListItemBehavior(props) : basicListItemBehavior(props)
props.selectable
? selectableListItemBehavior(props)
: props.navigable
? navigableListItemBehavior(props)
: basicListItemBehavior(props)

export default listItemBehavior

export type ListItemBehaviorProps = {
/** Indicates if a list is a selectable list. */
selectable?: boolean
/** Indicates if a list is a navigable list. */
navigable?: boolean
/** Indicates if the current list item is selected. */
selected?: boolean
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Accessibility, FocusZoneMode } from '../../types'
import { ListBehaviorProps } from './listBehavior'
import { FocusZoneDirection } from '../../FocusZone'

/**
* @specification
* Adds role='menu'.
* Embeds component into FocusZone.
* Provides arrow key navigation in bidirectionalDomOrder direction.
*/
const navigableListBehavior: Accessibility<ListBehaviorProps> = props => ({
attributes: {
root: {
role: 'menu',
},
},
focusZone: {
mode: FocusZoneMode.Embed,
props: {
shouldFocusInnerElementWhenReceivedFocus: true,
direction: FocusZoneDirection.bidirectionalDomOrder,
},
},
})

export default navigableListBehavior
Loading