Skip to content

Commit 6fa5513

Browse files
authored
Merge pull request #2088 from pnp/feature/listToolbar
New Control ListToolbar
2 parents 5e68eec + be85910 commit 6fa5513

20 files changed

Lines changed: 1630 additions & 26 deletions
160 KB
Loading
213 KB
Loading
482 KB
Loading
28.8 KB
Loading
201 KB
Loading
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# ListToolbar control
2+
3+
This control renders a flexible toolbar for building list and data grid command bars. It is built with Fluent UI 9 components and supports item grouping, left/right aligned items, dividers, tooltips, automatic overflow menu, responsive design, loading states, and custom rendering.
4+
5+
Here is an example of the control in action:
6+
7+
![ListToolbar control](../assets/ListToolbar.png)
8+
![ListToolbar control1](../assets/ListToolbar0.png)
9+
![ListToolbar control](../assets/ListToolBarMultiple.png)
10+
![ListToolbar control](../assets/ListToolBarMobile.png)
11+
12+
## How to use this control in your solutions
13+
14+
- Check that you installed the `@pnp/spfx-controls-react` dependency. Check out the [getting started](../../#getting-started) page for more information about installing the dependency.
15+
- Import the following modules to your component:
16+
17+
```TypeScript
18+
import { ListToolbar } from '@pnp/spfx-controls-react/lib/controls/ListToolbar';
19+
import { IToolbarItem } from '@pnp/spfx-controls-react/lib/controls/ListToolbar';
20+
```
21+
22+
- Use the `ListToolbar` control in your code as follows:
23+
24+
```TypeScript
25+
<ListToolbar
26+
items={[
27+
{ key: 'new', label: 'New', icon: <AddRegular />, onClick: () => console.log('New') },
28+
{ key: 'edit', label: 'Edit', icon: <EditRegular />, onClick: () => console.log('Edit') },
29+
{ key: 'delete', label: 'Delete', icon: <DeleteRegular />, onClick: () => console.log('Delete') },
30+
]}
31+
context={this.props.context}
32+
ariaLabel="Document toolbar"
33+
/>
34+
```
35+
36+
- With the `farItems` property you can add items that are aligned to the right side of the toolbar:
37+
38+
```TypeScript
39+
<ListToolbar
40+
items={[
41+
{ key: 'new', label: 'New', icon: <AddRegular />, onClick: () => {} },
42+
]}
43+
farItems={[
44+
{ key: 'filter', label: 'Filter', icon: <FilterRegular />, onClick: () => {} },
45+
{ key: 'settings', icon: <SettingsRegular />, tooltip: 'Settings', onClick: () => {} },
46+
]}
47+
context={this.props.context}
48+
/>
49+
```
50+
51+
- Use the `group` property on items to visually group them with dividers:
52+
53+
```TypeScript
54+
<ListToolbar
55+
items={[
56+
{ key: 'new', label: 'New', icon: <AddRegular />, group: 'file', onClick: () => {} },
57+
{ key: 'edit', label: 'Edit', icon: <EditRegular />, group: 'file', onClick: () => {} },
58+
{ key: 'copy', label: 'Copy', icon: <CopyRegular />, group: 'clipboard', onClick: () => {} },
59+
{ key: 'paste', label: 'Paste', icon: <ClipboardPasteRegular />, group: 'clipboard', onClick: () => {} },
60+
]}
61+
showGroupDividers={true}
62+
context={this.props.context}
63+
/>
64+
```
65+
66+
- Use the `totalCount` property to display a count badge in the toolbar:
67+
68+
```TypeScript
69+
<ListToolbar
70+
items={items}
71+
totalCount={42}
72+
context={this.props.context}
73+
/>
74+
```
75+
76+
- Use the `isLoading` property to disable all toolbar items during a loading state:
77+
78+
```TypeScript
79+
<ListToolbar
80+
items={items}
81+
isLoading={true}
82+
context={this.props.context}
83+
/>
84+
```
85+
86+
- Use the `onRender` property on an item for complete custom rendering:
87+
88+
```TypeScript
89+
const farItems: IToolbarItem[] = [
90+
{
91+
key: 'search',
92+
onRender: () => (
93+
<SearchBox placeholder="Search..." onChange={(e, data) => onSearch(data.value)} />
94+
),
95+
},
96+
];
97+
98+
<ListToolbar items={items} farItems={farItems} context={this.props.context} />
99+
```
100+
101+
### Overflow & responsive behavior
102+
103+
When the toolbar is too narrow to show all left-side items, they automatically collapse into a **"..."** overflow menu. The overflow menu appears right next to the last visible item and lists every hidden action.
104+
105+
On small screens (≤ 768 px), far-item **labels are hidden** and only their icons are shown. The total-count badge is also hidden at that breakpoint.
106+
107+
## Implementation
108+
109+
The `ListToolbar` control can be configured with the following properties:
110+
111+
| Property | Type | Required | Description | Default |
112+
| ---- | ---- | ---- | ---- | ---- |
113+
| items | IToolbarItem[] | yes | Array of toolbar items to display on the left side. | |
114+
| farItems | IToolbarItem[] | no | Items that appear on the right side of the toolbar. | `[]` |
115+
| isLoading | boolean | no | When `true`, all items are disabled. | `false` |
116+
| ariaLabel | string | no | Accessibility label for the toolbar. | `'Toolbar'` |
117+
| totalCount | number | no | Displays a count badge in the toolbar. | |
118+
| className | string | no | Additional CSS class name to apply to the toolbar. | |
119+
| showGroupDividers | boolean | no | Whether to show dividers between item groups. | `true` |
120+
| theme | Theme | no | Fluent UI v8 theme (auto-converted to v9 via `createV9Theme`). | |
121+
| context | BaseComponentContext | no | SPFx component context. Enables automatic Teams theme detection (dark, high-contrast). | |
122+
123+
### IToolbarItem
124+
125+
| Property | Type | Required | Description | Default |
126+
| ---- | ---- | ---- | ---- | ---- |
127+
| key | string | yes | Unique identifier for the item. | |
128+
| label | string | no | Button text label. | |
129+
| tooltip | string | no | Tooltip content shown on hover. | |
130+
| icon | ReactElement | no | Icon element to display. | |
131+
| onClick | () => void | no | Click handler for the item. | |
132+
| disabled | boolean | no | Whether the item is disabled. | `false` |
133+
| visible | boolean | no | Whether to show or hide the item. | `true` |
134+
| group | string | no | Group name — items with the same group are grouped together with dividers between groups. | `'default'` |
135+
| isFarItem | boolean | no | Place the item on the right side of the toolbar. | `false` |
136+
| appearance | ToolbarButtonProps['appearance'] | no | Button appearance style. | |
137+
| onRender | () => ReactElement | no | Custom render function for complete control over item rendering. | |
138+
| dividerAfter | boolean | no | Add a divider after this item. | `false` |
139+
| dividerBefore | boolean | no | Add a divider before this item. | `false` |
140+
| ariaLabel | string | no | Accessibility label override. | |
141+
142+
![](https://telemetry.sharepointpnp.com/sp-dev-fx-controls-react/wiki/controls/ListToolbar)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { IToolbarItem } from './IToolbarItem';
2+
3+
/* ------------------------------------------------------------------ */
4+
/* FlatItem — a union type for rendered toolbar elements */
5+
/* ------------------------------------------------------------------ */
6+
7+
export interface FlatItem {
8+
type: 'item' | 'divider';
9+
item?: IToolbarItem;
10+
key: string;
11+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { BaseComponentContext } from "@microsoft/sp-component-base";
2+
import { IToolbarItem } from "./IToolbarItem";
3+
import { Theme } from "@fluentui/react";
4+
5+
/**
6+
* Props for the ListToolbar component
7+
*/
8+
9+
export interface IListToolbarProps {
10+
/** Array of toolbar items */
11+
items: IToolbarItem[];
12+
/** Far items that appear on the right side of the toolbar */
13+
farItems?: IToolbarItem[];
14+
/** Whether the toolbar is in a loading state */
15+
isLoading?: boolean;
16+
/** Aria label for the toolbar */
17+
ariaLabel?: string;
18+
/** Total count to display (optional) */
19+
totalCount?: number;
20+
/** Custom class name */
21+
className?: string;
22+
/** Whether to show dividers between groups */
23+
showGroupDividers?: boolean;
24+
/** Theme for the toolbar */
25+
theme?: Theme;
26+
/** Context for the web part */
27+
context?: BaseComponentContext;
28+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { ToolbarButtonProps } from "@fluentui/react-components";
2+
import * as React from "react";
3+
4+
/**
5+
* Extended toolbar item interface
6+
*/
7+
8+
export interface IToolbarItem {
9+
/** Unique key for the item */
10+
key: string;
11+
/** Label text for the button */
12+
label?: string;
13+
/** Tooltip content */
14+
tooltip?: string;
15+
/** Icon element */
16+
icon?: React.ReactElement;
17+
/** Click handler */
18+
onClick?: () => void;
19+
/** Whether the item is disabled */
20+
disabled?: boolean;
21+
/** Whether to show the item */
22+
visible?: boolean;
23+
/** Group name - items with the same group will be grouped together */
24+
group?: string;
25+
/** Whether this is a far item (appears on the right side) */
26+
isFarItem?: boolean;
27+
/** Button appearance */
28+
appearance?: ToolbarButtonProps["appearance"];
29+
/** Custom render function for complete control */
30+
onRender?: () => React.ReactElement;
31+
/** Whether to add a divider after this item */
32+
dividerAfter?: boolean;
33+
/** Whether to add a divider before this item */
34+
dividerBefore?: boolean;
35+
/** Aria label override */
36+
ariaLabel?: string;
37+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { IToolbarItem } from './IToolbarItem';
2+
3+
4+
export interface IToolbarItemRendererProps {
5+
/** The toolbar item to render */
6+
item: IToolbarItem;
7+
/** Whether the toolbar is in a loading state */
8+
isLoading?: boolean;
9+
/** CSS class applied to the button root */
10+
itemClass?: string;
11+
/** CSS class applied to the label text (e.g. to hide on mobile) */
12+
labelClass?: string;
13+
}

0 commit comments

Comments
 (0)