Skip to content

Commit 1fc6151

Browse files
authored
Merge pull request #836 from Konradox/dashboard-error-catching-component
Dashboard widget wrapper for styling and error catching
2 parents 03e1796 + 446f6a8 commit 1fc6151

File tree

3 files changed

+64
-42
lines changed

3 files changed

+64
-42
lines changed

docs/documentation/docs/controls/Dashboard.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { WidgetSize, Dashboard } from '@pnp/spfx-controls-react/lib/Dashboard';
2020

2121
- Use the `Dashboard` control in your code as follows:
2222

23-
```TypeSCript
23+
```TypeScript
2424
const linkExample = { href: "#" };
2525
const calloutItemsExample = [
2626
{
@@ -86,10 +86,11 @@ The Dashboard component can be configured with the following properties:
8686

8787
| Property | Type | Required | Description |
8888
| ---- | ---- | ---- | ---- |
89-
| widgets | IWidget[] | yes | Widgtets collection. |
89+
| widgets | IWidget[] | yes | Widgets collection. |
9090
| allowHidingWidget | boolean | no | Specifies if widgets can be hidden from the dashboard. |
9191
| onWidgetHiding | (widget: IWidget) => void | no | Handler of widget hiding event. |
9292
| toolbarProps | IToolbarProps | no | Dashboard toolbar props. See [Toolbar](./Toolbar). |
93+
| WidgetContentWrapper | React.ComponentType\<React.PropsWithChildren\<any>> | no | Optional component which wraps every Widget component. Useful for a custom error handling or styling. |
9394

9495
Interface `IWidget`
9596

src/controls/dashboard/Dashboard.tsx

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import * as React from "react";
22
import {
33
ProviderConsumer as FluentUIThemeConsumer,
44
Box,
5-
teamsTheme
5+
teamsTheme,
6+
ThemePrepared,
67
} from "@fluentui/react-northstar";
78
import {
89
Widget,
@@ -22,7 +23,7 @@ import { useTelemetry } from "../../common/telemetry";
2223
*/
2324
export interface IDashboardProps {
2425
/**
25-
* Widgtets collection
26+
* Widgets collection
2627
*/
2728
widgets: IWidget[];
2829
/**
@@ -37,16 +38,25 @@ export interface IDashboardProps {
3738
* Dashboard toolbar props
3839
*/
3940
toolbarProps?: IToolbarProps;
41+
/**
42+
* Optional component which wraps every Widget component. Useful for a custom error handling or styling.
43+
*/
44+
WidgetContentWrapper?: React.ComponentType<React.PropsWithChildren<any>>;
4045
}
4146

4247
export function Dashboard({
4348
widgets,
4449
allowHidingWidget,
4550
onWidgetHiding,
46-
toolbarProps }: IDashboardProps) {
51+
toolbarProps,
52+
WidgetContentWrapper: WidgetWrapperComponent,
53+
}: IDashboardProps) {
4754
const [stateWidgets, setWidgets] = React.useState(widgets);
55+
const widgetRenderer = WidgetWrapperComponent
56+
? renderWidgetWithWrappedContent
57+
: renderWidget;
4858

49-
useTelemetry('ReactDashboard', {});
59+
useTelemetry("ReactDashboard", {});
5060

5161
React.useEffect(() => {
5262
setWidgets(widgets);
@@ -57,42 +67,53 @@ export function Dashboard({
5767
if (!globalTheme || globalTheme.fontFaces.length == 0) {
5868
globalTheme = teamsTheme;
5969
}
60-
return <DashboardTheme globalTheme={globalTheme}>
61-
{toolbarProps && <Toolbar {...toolbarProps} />}
62-
<Box className={styles.dashboardBox} >
63-
{stateWidgets &&
64-
stateWidgets.map(
65-
(
66-
widget: IWidget,
67-
key: number
68-
) => (
69-
<Widget key={key} widget={widget}>
70-
<WidgetTitle
71-
widget={widget}
72-
allowHidingWidget={allowHidingWidget}
73-
onWidgetHiding={(hidingWidget: IWidget) => {
74-
if (onWidgetHiding) {
75-
onWidgetHiding(hidingWidget);
76-
}
77-
if (!hidingWidget.controlOptions) {
78-
hidingWidget.controlOptions = {};
79-
}
80-
hidingWidget.controlOptions.isHidden = true;
81-
setWidgets([...widgets]);
82-
}}
83-
globalTheme={globalTheme}
84-
/>
85-
<WidgetBody
86-
widget={widget}
87-
siteVariables={globalTheme.siteVariables}
88-
/>
89-
{widget.link && <WidgetFooter widget={widget} />}
90-
</Widget>
91-
)
92-
)}
93-
</Box>
94-
</DashboardTheme>;
70+
return (
71+
<DashboardTheme globalTheme={globalTheme}>
72+
{toolbarProps && <Toolbar {...toolbarProps} />}
73+
<Box className={styles.dashboardBox}>
74+
{stateWidgets && stateWidgets.map(widgetRenderer(globalTheme))}
75+
</Box>
76+
</DashboardTheme>
77+
);
9578
}}
9679
/>
9780
);
81+
82+
function renderWidgetWithWrappedContent(
83+
globalTheme: ThemePrepared<any>
84+
): (value?: IWidget, index?: number) => JSX.Element {
85+
return (widget: IWidget, key: number) => {
86+
return (
87+
<WidgetWrapperComponent>
88+
{renderWidget(globalTheme)(widget, key)}
89+
</WidgetWrapperComponent>
90+
);
91+
};
92+
}
93+
94+
function renderWidget(
95+
globalTheme: ThemePrepared<any>
96+
): (value?: IWidget, index?: number) => JSX.Element {
97+
return (widget: IWidget, key: number) => (
98+
<Widget key={key} widget={widget}>
99+
<WidgetTitle
100+
widget={widget}
101+
allowHidingWidget={allowHidingWidget}
102+
onWidgetHiding={(hidingWidget: IWidget) => {
103+
if (onWidgetHiding) {
104+
onWidgetHiding(hidingWidget);
105+
}
106+
if (!hidingWidget.controlOptions) {
107+
hidingWidget.controlOptions = {};
108+
}
109+
hidingWidget.controlOptions.isHidden = true;
110+
setWidgets([...widgets]);
111+
}}
112+
globalTheme={globalTheme}
113+
/>
114+
<WidgetBody widget={widget} siteVariables={globalTheme.siteVariables} />
115+
{widget.link && <WidgetFooter widget={widget} />}
116+
</Widget>
117+
);
118+
}
98119
}

src/webparts/controlsTest/components/ControlsTest.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1696,7 +1696,7 @@ export default class ControlsTest extends React.Component<IControlsTestProps, IC
16961696
title: "Card 6",
16971697
size: WidgetSize.Single,
16981698
link: linkExample,
1699-
},]} />
1699+
}]} />
17001700
<Toolbar actionGroups={{
17011701
'group1': {
17021702
'action1': {

0 commit comments

Comments
 (0)