- Developers
- Tutorials
- Create an Analytical Dashboard via UI5 Web Components for React
Create an Analytical Dashboard via UI5 Web Components for React
-
Join the conversation on Facebook
-
Join the conversation on Twitter
-
Subscribe to the YouTube Channel
-
Join the conversation on LinkedIn
-
View our projects on GitHub
-
Share via email
Create an Analytical Dashboard via UI5 Web Components for React
You will learn
- How to use the
ShellBar
component - How to use the
AnalyticalTable
component - How to style components
So far, you have built your first Card
component. Now to take things further, it’s time to build something bigger. In this step, you will learn how different components work together by building an analytical dashboard.
To make things easier, first import all the components you will need in this step. Just copy the code below and replace the previous imported components in MyApp.jsx
.
import {
Avatar,
Card,
Text,
ShellBar,
ShellBarItem,
List,
StandardListItem,
ValueState,
ProgressIndicator,
Title,
TitleLevel,
FlexBox,
FlexBoxJustifyContent,
FlexBoxWrap,
FlexBoxDirection,
AnalyticalTable,
Icon
} from "@ui5/webcomponents-react";
The ShellBar
is the central navigation element in your Web Application and should therefore be visible on all pages.
Again, you can try it out on the Storybook.
-
Start with adding the
ShellBar
above yourCard
component and add aprimaryTitle
prop.<ShellBar primaryTitle="My App" />
-
Add some more properties
The logo of the application should be displayed and also a profile picture would be nice.
Use the
logo
andprofile
prop to achieve this. Thelogo
prop accepts either animg
tag or theAvatar
component, theprofile
prop only accepts theAvatar
component. First add thelogo
prop like this:<ShellBar logo={<img src="" />} primaryTitle="My App" />
Then pass the
profile
prop like this:<ShellBar logo={<img src="" />} profile={<Avatar image="" />} primaryTitle="My App" />
You can use your own image, use an URL to an image or simply download the images below and add them to your
public
folder in your project.<ShellBar logo={<img src="reactLogo.png" />} profile={<Avatar image="profilePictureExample.png" />} primaryTitle={"My App"} />
-
Add custom elements
By passing a
ShellBarItem
aschild
you are able to add custom elements to yourShellBar
. The element is basically aButton
with responsive behavior and styling adapted to theShellBar
.<ShellBar logo={<img src="reactLogo.png" />} profile={<Avatar image="profilePictureExample.png" />} primaryTitle={"My App"}> <ShellBarItem icon="add" text="Add" /> </ShellBar>
That is strange – when you render your component, the
ShellBarItem
is not shown.Every
Icon
that is used in a component has to be imported manually. All available icons can be found here.Add this line to your imports:
import "@ui5/webcomponents-icons/dist/add.js";
Now your
ShellBarItem
shows up at the right side of theShellBar
.
Your component should look like this:
import React, { useState } from "react";
import {
Avatar,
Card,
Text,
ShellBar,
ShellBarItem,
List,
StandardListItem,
ValueState,
ProgressIndicator,
Title,
TitleLevel,
FlexBox,
FlexBoxJustifyContent,
FlexBoxWrap,
FlexBoxDirection,
AnalyticalTable,
Icon
} from "@ui5/webcomponents-react";
import { spacing } from "@ui5/webcomponents-react-base";
import { BarChart, LineChart } from "@ui5/webcomponents-react-charts";
import "@ui5/webcomponents-icons/dist/line-chart.js";
import "@ui5/webcomponents-icons/dist/horizontal-bar-chart.js";
import "@ui5/webcomponents-icons/dist/add.js";
import "@ui5/webcomponents-icons/dist/list.js";
import "@ui5/webcomponents-icons/dist/table-view.js";
export function MyApp() {
const [toggleCharts, setToggleCharts] = useState("lineChart");
const [loading, setLoading] = useState(false);
const contentTitle =
toggleCharts === "lineChart" ? "Line Chart" : "Bar Chart";
const switchToChart =
toggleCharts === "lineChart" ? "Bar Chart" : "Line Chart";
const handleHeaderClick = () => {
if (toggleCharts === "lineChart") {
setLoading(true);
setTimeout(() => {
setLoading(false);
setToggleCharts("barChart");
}, 2000);
} else {
setLoading(true);
setTimeout(() => {
setLoading(false);
setToggleCharts("lineChart");
}, 2000);
}
};
const dataset = [
{
month: "January",
data: 65
},
{
month: "February",
data: 59
},
{
month: "March",
data: 80
},
{
month: "April",
data: 81
},
{
month: "May",
data: 56
},
{
month: "June",
data: 55
},
{
month: "July",
data: 40
}
];
return (
<div>
<ShellBar
logo={<img src="reactLogo.png" />}
profile={<Avatar image="profilePictureExample.png" />}
primaryTitle="My App"
>
<ShellBarItem icon="add" text="Add" />
</ShellBar>
<Card
avatar={
<Icon
name={
toggleCharts === "lineChart"
? "line-chart"
: "horizontal-bar-chart"
}
/>
}
heading="Stock Price"
style={{ width: "300px" }}
headerInteractive
onHeaderClick={handleHeaderClick}
subheading={`Click here to switch to ${switchToChart}`}
>
<Text style={spacing.sapUiContentPadding}>{contentTitle}</Text>
{toggleCharts === "lineChart" ? (
<LineChart
dimensions={[{ accessor: "month" }]}
measures={[{ accessor: "data", label: "Stock Price" }]}
dataset={dataset}
loading={loading}
/>
) : (
<BarChart
dimensions={[{ accessor: "month" }]}
measures={[{ accessor: "data", label: "Stock Price" }]}
dataset={dataset}
loading={loading}
/>
)}
</Card>
</div>
);
}
-
To wrap the
List
add aCard
(right after the first one).<Card heading="Progress" subheading="List" style={{ width: "300px" }} avatar={<Icon name="list" />} > </Card>
-
Add the list
Icon
to your imports.import "@ui5/webcomponents-icons/dist/list.js";
-
Add the
List
component as child of theCard
.<List></List>
-
To render elements of the list, use the
StandardListItem
and pass astring
as child.<List> <StandardListItem>Activity 1</StandardListItem> </List>
-
The user should know the status of the activities. Add the
info
prop to theStandardListItem
. To visualize if the status is neutral, positive or negative, also add theinfoState
prop.<StandardListItem info="finished" infoState={ValueState.Success}> Activity 1 </StandardListItem> <StandardListItem info="failed" infoState={ValueState.Error}> Activity 2 </StandardListItem>
-
Now add two more activities, one that is almost finished and one which has just started.
For this we need the
ProgressIndicator
andTitle
component. TheTitle
receives thelevel
prop, it is working like the corresponding HTML elements.The
ProgressIndicator
is given two props:value
: The value, which indicates the progressvalueState
: The value-state (color) of the indicator
<StandardListItem info="in progress" infoState={ValueState.Warning}> <Title level={TitleLevel.H5}>Activity 3</Title> <ProgressIndicator value={89} valueState={ValueState.Success} /> </StandardListItem> <StandardListItem info="in progress" infoState={ValueState.Warning}> <Title level={TitleLevel.H5}>Activity 4</Title> <ProgressIndicator value={5} valueState={ValueState.Error} /> </StandardListItem>
-
The components are shown but they don’t fit inside the row and overflow.
To fix this, first adjust the height of the
StandardListItem
. Then, pass astyle
prop to the component to use the default ReactinlineStyle
syntax and then wrap yourTitle
andProgressIndicator
inside of aFlexBox
component.
TheFlexBox
implements most of theCSS Flexbox
behavior without being forced to actually use CSS or other styling methods.The finished
List
component should now look like this:<List> <StandardListItem info="finished" infoState={ValueState.Success}> Activity 1 </StandardListItem> <StandardListItem info="failed" infoState={ValueState.Error}> Activity 2 </StandardListItem> <StandardListItem info="in progress" infoState={ValueState.Warning} style={{ height: "80px" }} > <FlexBox direction={FlexBoxDirection.Column}> <Title level={TitleLevel.H5}>Activity 3</Title> <ProgressIndicator value={89} valueState={ValueState.Success} /> </FlexBox> </StandardListItem> <StandardListItem info="in progress" infoState={ValueState.Warning} style={{ height: "80px" }} > <FlexBox direction={FlexBoxDirection.Column}> <Title level={TitleLevel.H5}>Activity 4</Title> <ProgressIndicator value={5} valueState={ValueState.Error} /> </FlexBox> </StandardListItem> </List>
Now the components inside the card fit (we’ll arrange the cards themselves later):

- The last tile should contain a
AnalyticalTable
component. Again, create aCard
to wrap the Table and set themax-width
to900px
.<Card heading="AnalyticalTable" style={{maxWidth: "900px"}} avatar={<Icon name="table-view" />}> <AnalyticalTable /> </Card>
Also import the
table-view
Icon
.import "@ui5/webcomponents-icons/dist/table-view.js";
- Add data and columns to the table. The
columns
prop expects an array of objects that include at least theaccessor
to the data. The value ofHeader
will be shown as column header.You can create your own data or just use the code below and paste it right after the definition of the
labels
of the chart.const tableData = new Array(500).fill(null).map((_, index) => { return { name: `name${index}`, age: Math.floor(Math.random() * 100), friend: { name: `friend.Name${index}`, age: Math.floor(Math.random() * 100) } }; }); const tableColumns = [ { Header: "Name", accessor: "name" // String-based value accessors! }, { Header: "Age", accessor: "age" }, { Header: "Friend Name", accessor: "friend.name" }, { Header: "Friend Age", accessor: "friend.age" } ];
-
Display the data by replacing the current table with.
<AnalyticalTable data={tableData} columns={tableColumns} />
-
Add more properties
You can add many more properties to the
AnalyticalTable
component. For example you can group the columns withgroupable
, search for entries in a column withfilterable
, and change the row height withrowHeight
.The default visible rows count is at 15. This number is a bit to high for a dashboard table. Reduce the
visibleRows
count to 5 by setting the corresponding prop.<AnalyticalTable data={tableData} columns={tableColumns} visibleRows={5}/>
At the moment, the dashboard doesn’t really look like a dashboard. The components are way too close to each other and not aligned correctly. Let’s change that.
-
Add padding to each
Card
and theShellBar
To add a padding to the cards, you can use
spacing
again. Inside of the style property spread thespacing
object and append the style. Do this for eachCard
component.<Card style={{ width: "300px", ...spacing.sapUiContentPadding }} avatar={<Icon name={toggleCharts === "lineChart" ? "line-chart" : "horizontal-bar-chart"} />} heading="Stock Price" headerInteractive onHeaderClick={handleHeaderClick} subheading={`Click here to switch to ${switchToChart}`} >
<Card heading="Progress" subheading="List" style={{ width: "300px", ...spacing.sapUiContentPadding }} avatar={<Icon name="list" />}>
<Card heading="AnalyticalTable" subheading="List" style={{ width: "900px", ...spacing.sapUiContentPadding }} avatar={<Icon name="table-view" />}>
-
Align the elements
To properly align the tiles, use a
FlexBox
component and wrap yourCards
inside of it. Use thejustifyContent
prop to center align all elements andwrap
to make them move to the next line if not enough space is available, also apply thestyle
prop to add a padding to the whole content area.<FlexBox justifyContent={FlexBoxJustifyContent.Center} wrap={FlexBoxWrap.Wrap} > style={spacing.sapUiContentPadding} ... </FlexBox>
Your component should now look like this:

import React, { useState } from "react";
import {
Avatar,
Card,
Text,
ShellBar,
ShellBarItem,
List,
StandardListItem,
ValueState,
ProgressIndicator,
Title,
TitleLevel,
FlexBox,
FlexBoxJustifyContent,
FlexBoxWrap,
FlexBoxDirection,
AnalyticalTable,
Icon
} from "@ui5/webcomponents-react";
import { spacing } from "@ui5/webcomponents-react-base";
import { BarChart, LineChart } from "@ui5/webcomponents-react-charts";
import "@ui5/webcomponents-icons/dist/line-chart.js";
import "@ui5/webcomponents-icons/dist/horizontal-bar-chart.js";
import "@ui5/webcomponents-icons/dist/add.js";
import "@ui5/webcomponents-icons/dist/list.js";
import "@ui5/webcomponents-icons/dist/table-view.js";
export function MyApp() {
const [toggleCharts, setToggleCharts] = useState("lineChart");
const [loading, setLoading] = useState(false);
const contentTitle =
toggleCharts === "lineChart" ? "Line Chart" : "Bar Chart";
const switchToChart =
toggleCharts === "lineChart" ? "Bar Chart" : "Line Chart";
const tableData = new Array(500).fill(null).map((_, index) => {
return {
name: `name${index}`,
age: Math.floor(Math.random() * 100),
friend: {
name: `friend.Name${index}`,
age: Math.floor(Math.random() * 100)
}
};
});
const tableColumns = [
{
Header: "Name",
accessor: "name" // String-based value accessors!
},
{
Header: "Age",
accessor: "age"
},
{
Header: "Friend Name",
accessor: "friend.name"
},
{
Header: "Friend Age",
accessor: "friend.age"
}
];
const handleHeaderClick = () => {
if (toggleCharts === "lineChart") {
setLoading(true);
setTimeout(() => {
setLoading(false);
setToggleCharts("barChart");
}, 2000);
} else {
setLoading(true);
setTimeout(() => {
setLoading(false);
setToggleCharts("lineChart");
}, 2000);
}
};
const dataset = [
{
month: "January",
data: 65
},
{
month: "February",
data: 59
},
{
month: "March",
data: 80
},
{
month: "April",
data: 81
},
{
month: "May",
data: 56
},
{
month: "June",
data: 55
},
{
month: "July",
data: 40
}
];
return (
<div>
<ShellBar
logo={<img src="reactLogo.png" />}
profile={<Avatar image="profilePictureExample.png" />}
primaryTitle="My App"
>
<ShellBarItem icon="add" text="Add" />
</ShellBar>
<FlexBox
justifyContent={FlexBoxJustifyContent.Center}
wrap={FlexBoxWrap.Wrap}
style={spacing.sapUiContentPadding}
>
<Card
avatar={
<Icon
name={
toggleCharts === "lineChart"
? "line-chart"
: "horizontal-bar-chart"
}
/>
}
heading="Stock Price"
style={{ width: "300px" }}
headerInteractive
onHeaderClick={handleHeaderClick}
subheading={`Click here to switch to ${switchToChart}`}
>
<Text style={spacing.sapUiContentPadding}>{contentTitle}</Text>
{toggleCharts === "lineChart" ? (
<LineChart
dimensions={[{ accessor: "month" }]}
measures={[{ accessor: "data", label: "Stock Price" }]}
dataset={dataset}
loading={loading}
/>
) : (
<BarChart
dimensions={[{ accessor: "month" }]}
measures={[{ accessor: "data", label: "Stock Price" }]}
dataset={dataset}
loading={loading}
/>
)}
</Card>
<Card
heading="Progress"
subheading="List"
style={{ width: "300px", ...spacing.sapUiContentPadding }}
avatar={<Icon name="list" />}
>
<List>
<StandardListItem info="finished" infoState={ValueState.Success}>
Activity 1
</StandardListItem>
<StandardListItem info="failed" infoState={ValueState.Error}>
Activity 2
</StandardListItem>
<StandardListItem
info="in progress"
infoState={ValueState.Warning}
style={{ height: "80px" }}
>
<FlexBox direction={FlexBoxDirection.Column}>
<Title level={TitleLevel.H5}>Activity 3</Title>
<ProgressIndicator value={89} valueState={ValueState.Success} />
</FlexBox>
</StandardListItem>
<StandardListItem
info="in progress"
infoState={ValueState.Warning}
style={{ height: "80px" }}
>
<FlexBox direction={FlexBoxDirection.Column}>
<Title level={TitleLevel.H5}>Activity 4</Title>
<ProgressIndicator value={5} valueState={ValueState.Error} />
</FlexBox>
</StandardListItem>
</List>
</Card>
<Card
heading="AnalyticalTable"
style={{ maxWidth: "900px", ...spacing.sapUiContentPadding }}
avatar={<Icon name="table-view" />}
>
<AnalyticalTable
data={tableData}
columns={tableColumns}
visibleRows={5}
/>
</Card>
</FlexBox>
</div>
);
}