diff --git a/packages/react-components/react-list-preview/docs/MIGRATION.md b/packages/react-components/react-list-preview/docs/MIGRATION.md index 7c60cd3f6370d..c82899517708e 100644 --- a/packages/react-components/react-list-preview/docs/MIGRATION.md +++ b/packages/react-components/react-list-preview/docs/MIGRATION.md @@ -174,29 +174,29 @@ We recommend using a component like `Persona` where possible, or creating a cust #### ListItem -| v0 ListItem | v9 ListItem | -| ----------------- | ------------------------------------------------------------------------------------------------------------------------- | -| `accessibility` | N/A | -| `as` | `as` | -| `className` | `className` | -| `content` | N/A - use children | -| `contentMedia` | N/A - use children | -| `debug` | N/A | -| `design` | N/A | -| `endMedia` | N/A - use children | -| `header` | N/A - use children | -| `headerMedia` | N/A - use children | -| `important` | N/A | -| `index` | N/A | -| `media` | N/A - use children | -| `navigable` | N/A - use `tabIndex={0}` or `navigable` on the `List` | -| `onClick` | `onAction` | -| `ref` | ref | -| `selectable` | N/A - use `List` property `selectionMode` for uncontrolled selection, or use `useListSelection` hook to control the state | -| `selected` | N/A - the selection state takes care of this automatically | -| `styles` | N/A - use `className` for any slot | -| `truncateContent` | N/A - the `List` is not concerned about it's content | -| `truncateHeader` | N/A - the `List` is not concerned about it's content | +| v0 ListItem | v9 ListItem | +| ----------------- | ------------------------------------------------------------------------------------- | +| `accessibility` | N/A | +| `as` | `as` | +| `className` | `className` | +| `content` | N/A - use children | +| `contentMedia` | N/A - use children | +| `debug` | N/A | +| `design` | N/A | +| `endMedia` | N/A - use children | +| `header` | N/A - use children | +| `headerMedia` | N/A - use children | +| `important` | N/A | +| `index` | N/A | +| `media` | N/A - use children | +| `navigable` | N/A - use `tabIndex={0}` or `navigable` on the `List` | +| `onClick` | `onAction` | +| `ref` | ref | +| `selectable` | N/A - use `List` props like `selectionMode`, `selectedItems` and `onSelectionChange` | +| `selected` | N/A - use `selectedItems` (or tracked internally when `defaultSelectedItems` is used) | +| `styles` | N/A - use `className` for any slot | +| `truncateContent` | N/A - the `List` is not concerned about it's content | +| `truncateHeader` | N/A - the `List` is not concerned about it's content | #### Other diff --git a/packages/react-components/react-list-preview/etc/react-list-preview.api.md b/packages/react-components/react-list-preview/etc/react-list-preview.api.md index 489d4ad46bf7a..17c8e1518541a 100644 --- a/packages/react-components/react-list-preview/etc/react-list-preview.api.md +++ b/packages/react-components/react-list-preview/etc/react-list-preview.api.md @@ -13,7 +13,6 @@ import type { EventData } from '@fluentui/react-utilities'; import type { EventHandler } from '@fluentui/react-utilities'; import type { ForwardRefComponent } from '@fluentui/react-utilities'; import * as React_2 from 'react'; -import { SelectionHookParams } from '@fluentui/react-utilities'; import { SelectionItemId } from '@fluentui/react-utilities'; import type { SelectionMode as SelectionMode_2 } from '@fluentui/react-utilities'; import type { Slot } from '@fluentui/react-utilities'; @@ -81,9 +80,6 @@ export const useListItem_unstable: (props: ListItemProps, ref: React_2.Ref ListItemState; -// @public (undocumented) -export function useListSelection(options?: SelectionHookParams): ListSelectionState; - // @public export const useListStyles_unstable: (state: ListState) => ListState; diff --git a/packages/react-components/react-list-preview/src/index.ts b/packages/react-components/react-list-preview/src/index.ts index 827896be95a61..fc8c6238bad6a 100644 --- a/packages/react-components/react-list-preview/src/index.ts +++ b/packages/react-components/react-list-preview/src/index.ts @@ -9,5 +9,3 @@ export { useListItem_unstable, } from './ListItem'; export type { ListItemProps, ListItemSlots, ListItemState } from './ListItem'; - -export { useListSelection } from './hooks'; diff --git a/packages/react-components/react-list-preview/stories/List/ListSelectionControlledWithState.stories.tsx b/packages/react-components/react-list-preview/stories/List/ListSelectionControlledWithState.stories.tsx deleted file mode 100644 index 771af783e9078..0000000000000 --- a/packages/react-components/react-list-preview/stories/List/ListSelectionControlledWithState.stories.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { Button, makeStyles, Persona, shorthands } from '@fluentui/react-components'; -import { List, ListItem, useListSelection } from '@fluentui/react-list-preview'; - -import * as React from 'react'; -const names = [ - 'Melda Bevel', - 'Demetra Manwaring', - 'Eusebia Stufflebeam', - 'Israel Rabin', - 'Bart Merrill', - 'Sonya Farner', -]; - -type Item = { - name: string; - id: string; - avatar: string; -}; - -const items: Item[] = names.map(name => ({ - name, - id: name, - avatar: - 'https://res-1.cdn.office.net/files/fabric-cdn-prod_20230815.002/office-ui-fabric-react-assets/persona-male.png', -})); - -const useStyles = makeStyles({ - wrapper: { - maxHeight: '300px', - overflowY: 'auto', - ...shorthands.padding('2px'), - }, - buttonControls: { - display: 'flex', - columnGap: '8px', - marginBottom: '16px', - }, - button: { - ...shorthands.padding(0), - }, -}); - -// Memoizing the ListItem like this allows the unaffected ListItem not to be re-rendered when the selection changes. -const MyListItem = React.memo(({ name, avatar }: { name: string; avatar: string }) => { - return ( - - - - ); -}); - -export const ListSelectionControlledWithState = () => { - const classes = useStyles(); - - const selection = useListSelection({ - selectionMode: 'multiselect', - defaultSelectedItems: ['Demetra Manwaring', 'Bart Merrill'], - }); - - return ( -
-
- - -
- - selection.setSelectedItems(data.selectedItems)} - > - {items.map(({ name, avatar }) => ( - - ))} - -
Selected people: {selection.selectedItems.join(', ')}
-
- ); -}; - -ListSelectionControlledWithState.parameters = { - docs: { - description: { - story: [ - 'This example is an extension of the previous example of controlled selection. ', - 'It shows how to use the `useListSelection` hook to control the selection state. This hook is also used ', - ' internally when the selection is used in uncontrolled mode.', - '', - 'The `useListSelection` hook is by no means required for the selection to work, but it provides a convenient ', - 'way to control the selection state by providing selection specific helper functions.', - '', - 'While only the list of selected items is required to control the selection state, using the hook to ', - 'manage the selection state can be benefitial by providing selection specific helper functions.', - ].join('\n'), - }, - }, -}; diff --git a/packages/react-components/react-list-preview/stories/List/MultipleActionsDifferentPrimary.stories.tsx b/packages/react-components/react-list-preview/stories/List/MultipleActionsDifferentPrimary.stories.tsx index d31e9c0a524ca..b0bad94eb1206 100644 --- a/packages/react-components/react-list-preview/stories/List/MultipleActionsDifferentPrimary.stories.tsx +++ b/packages/react-components/react-list-preview/stories/List/MultipleActionsDifferentPrimary.stories.tsx @@ -67,22 +67,25 @@ const useStyles = makeStyles({ secondaryAction: { ...shorthands.gridArea('secondary_action') }, }); -const CardExample = (props: { title: string; value: string; selected?: boolean }) => { +const CustomListItem = (props: { title: string; value: string; selected?: boolean }) => { const listItemStyles = useListItemRootStyles(); const styles = useStyles(); const { value } = props; + // This will be triggered by user pressing Enter or clicking on the list item + const onAction = React.useCallback(event => { + // This prevents the change in selection on click/Enter + event.preventDefault(); + alert(`Triggered custom action!`); + }, []); + return ( { - // Prevent the default behavior - toggling the selection - e.preventDefault(); - alert('Primary action triggered!'); - }} + onAction={onAction} >
Presentation Preview @@ -97,7 +100,6 @@ const CardExample = (props: { title: string; value: string; selected?: boolean } aria-label="Install" onClick={e => { e.preventDefault(); - e.stopPropagation(); alert('Installing!'); }} > @@ -110,7 +112,6 @@ const CardExample = (props: { title: string; value: string; selected?: boolean }
@@ -75,22 +49,32 @@ export const ListSelectionControlledBasic = () => { onSelectionChange={(_, data) => setSelectedItems(data.selectedItems)} > {items.map(({ name, avatar }) => ( - + + + ))} -
Selected people: {selectedItems.join(', ')}
); }; -ListSelectionControlledBasic.parameters = { +SingleActionSelectionControlled.parameters = { docs: { description: { story: [ 'This example shows how to use the `selectedItems` and `onSelectionChange`', - 'props to control the selection state.', + 'props to control the selection state of the List and keep track of it in the parent component.', '', - 'This is a basic example how selection can be controlled with a simple array of selected values in a state.', + 'This is more in line with how we expect the selection to be used in production environment.', ].join('\n'), }, }, diff --git a/packages/react-components/react-list-preview/stories/List/SingleActionSelectionDifferentPrimary.stories.tsx b/packages/react-components/react-list-preview/stories/List/SingleActionSelectionDifferentPrimary.stories.tsx index 078e46fe40584..8a0a1ffe35112 100644 --- a/packages/react-components/react-list-preview/stories/List/SingleActionSelectionDifferentPrimary.stories.tsx +++ b/packages/react-components/react-list-preview/stories/List/SingleActionSelectionDifferentPrimary.stories.tsx @@ -1,4 +1,4 @@ -import { makeStyles, Persona, shorthands, SelectionItemId } from '@fluentui/react-components'; +import { Persona, SelectionItemId } from '@fluentui/react-components'; import { List, ListItem } from '@fluentui/react-list-preview'; import * as React from 'react'; @@ -24,63 +24,37 @@ const items: Item[] = names.map(name => ({ 'https://res-1.cdn.office.net/files/fabric-cdn-prod_20230815.002/office-ui-fabric-react-assets/persona-male.png', })); -const useStyles = makeStyles({ - wrapper: { - maxHeight: '300px', - overflowY: 'auto', - ...shorthands.padding('2px'), - }, - button: { - ...shorthands.padding(0), - }, -}); - -// Memoizing the ListItem like this allows the unaffected ListItem not to be re-rendered when the selection changes. -const MyListItem = React.memo(({ name, avatar }: { name: string; avatar: string }) => { - const onAction = React.useCallback( - event => { - // This prevents the change in selection on click/Enter - event.preventDefault(); - alert(`Triggered action on ${name}`); - }, - [name], - ); - return ( - - - - ); -}); - export const SingleActionSelectionDifferentPrimary = () => { - const classes = useStyles(); + const [selectedItems, setSelectedItems] = React.useState(['Demetra Manwaring', 'Bart Merrill']); - const defaultSelectedItems = ['Demetra Manwaring', 'Bart Merrill']; - - const [selectedItems, setSelectedItems] = React.useState(defaultSelectedItems); + // This will be triggered by user pressing Enter or clicking on the list item + const onAction = React.useCallback(event => { + // This prevents the change in selection on click/Enter + event.preventDefault(); + alert(`Triggered custom action!`); + }, []); return ( -
- setSelectedItems(data.selectedItems)} - > - {items.map(({ name, avatar }) => ( - - ))} - -
Selected people: {selectedItems.join(', ')}
-
+ setSelectedItems(data.selectedItems)} + > + {items.map(({ name, avatar }) => ( + + + + ))} + ); }; @@ -89,18 +63,18 @@ SingleActionSelectionDifferentPrimary.parameters = { description: { story: [ 'This example is similar to the previous one, but it implements a custom primary action on `ListItem`,', - 'allowing us to trigger a different action than the selection when the user clicks on the list item or ', - 'presses Enter. This is useful when you want to have a primary action on the list item, but still want ', + 'allowing us to trigger a', + '__different action than the selection when the user clicks on the list item or presses Enter__', + '. This is useful when you want to have a primary action on the list item, but still want ', 'to allow the user to select it.', '', 'To change the default action on the `ListItem` (when user clicks on it or presses Enter), you can use the', - '`onAction` prop. By callign `event.preventDefault()` in the `onAction` callback, you can prevent the default', + '`onAction` prop. By calling `event.preventDefault()` in the `onAction` callback, you can prevent the default', 'action (toggling the selection) from happening. This way, you can perform a completely custom action.', 'In this example, the custom action is an alert that triggers when the user', 'clicks on the list item or presses Enter.', '', - '', - 'The selection can still be toggled by clicking on the checkbox or pressing `Space` when the item is focused.', + '__The selection can still be toggled by clicking on the checkbox or pressing `Space` when the item is focused.__', ].join('\n'), }, }, diff --git a/packages/react-components/react-list-preview/stories/List/index.stories.tsx b/packages/react-components/react-list-preview/stories/List/index.stories.tsx index e99fda11c56cb..a90149cbbdc23 100644 --- a/packages/react-components/react-list-preview/stories/List/index.stories.tsx +++ b/packages/react-components/react-list-preview/stories/List/index.stories.tsx @@ -6,12 +6,11 @@ import bestPracticesMd from './ListBestPractices.md'; export { Default } from './ListDefault.stories'; export { SingleAction } from './SingleAction.stories'; export { SingleActionSelection } from './SingleActionSelection.stories'; +export { SingleActionSelectionControlled } from './SingleActionSelectionControlled.stories'; export { SingleActionSelectionDifferentPrimary } from './SingleActionSelectionDifferentPrimary.stories'; export { MultipleActionsWithPrimary } from './MultipleActionsWithPrimary.stories'; export { MultipleActionsSelection } from './MultipleActionsSelection.stories'; export { MultipleActionsDifferentPrimary } from './MultipleActionsDifferentPrimary.stories'; -export { ListSelectionControlledBasic } from './ListSelectionControlledBasic.stories'; -export { ListSelectionControlledWithState } from './ListSelectionControlledWithState.stories'; export { VirtualizedList } from './VirtualizedList.stories'; export { VirtualizedListWithActionableItems } from './VirtualizedListWithActionableItems.stories'; export { ListActiveElement } from './ListActiveElement.stories';