Report Template
#Composition
The Report Template creates the structure of a Page Report, PDF Report, App Report, or the like.
It consists of the following components:
TitleHeaderActionButtons
LeftSidebarWidgetsIssuesListsIssue
Footer
ReportContentRightSidebar
The Report Template is designed to segment the content into a grid structure that takes care of overflowing and resizing.
It is mostly agnostic about its content, meaning that it is up to the ones implementing it to fill it with whatever content and utility they need.
The only exceptions are the IssuesLists and Issue components, which offer a common way to display lists of issues and their occurrences.
#Examples
#Basic Usage
The Report Template needs a set height to resize correctly. If the height has not been set, it will defalut to 100vh.
It can still be inferred by adjusting the height on the parent, along with display: flex and flex-direction: column.
This way, the Report Template could easily be combined with a HorizontalNavigation without having to worry about setting the right height for the Report Template.
Product name
Example Page
https://www.siteimprove.com
Example Page
https://www.siteimprove.com
const { param, theme } = props;
const [currentIssue, setCurrentIssue] = useState<IssueItem & { description: string }>();
const [highlightedOccurrence, setHighlightedOccurrence] = useState<number>();
const issues = baseIssues.map((issue) => ({
...issue,
onClick: () => setCurrentIssue(issue),
}));
const [breadCrumbItems, setBreadCrumbItems] = useState<Breadcrumb[]>([]);
useEffect(() => {
if (currentIssue) {
setBreadCrumbItems([
{
title: "Overview",
url: "#",
onClick: (e) => {
e.preventDefault();
setCurrentIssue(undefined);
setHighlightedOccurrence(undefined);
},
},
{ title: currentIssue.text, url: null },
]);
}
}, [currentIssue]);
return (
<div style={{ height: param, display: "flex", flexDirection: "column" }}>
<HorizontalNavigation
appLogo={{
icon: theme === "agentic-ai-2025" ? siteImproveLogoAgenticAi2025 : siteImproveLogo,
href: "https://my2.siteimprove.com",
title: "Go to main page",
"aria-label": "Siteimprove",
"data-observe-key": "top-nav-company-link",
}}
actions={actionsDummyData}
data-observe-key="top-nav"
/>
<ReportTemplate isRightSidebarVisible={false}>
<ReportTemplate.Title>
<Icon>
<IconAssistiveTechnology />
</Icon>
Product name
</ReportTemplate.Title>
<ReportTemplate.Header title="Example Page" url="https://www.siteimprove.com" />
<ReportTemplate.Widgets>
{currentIssue === undefined ? (
<ReportTemplate.Widgets.IssuesLists issueItemLists={[{ title: "Issues", issues }]} />
) : (
<ReportTemplate.Widgets.Issue
breadcrumbs={breadCrumbItems}
title={currentIssue.text}
description={currentIssue.description}
occurrences={{
items: baseOccurrences.map((occurrence, index) => ({
...occurrence,
onClick: () => setHighlightedOccurrence(index),
selected: index === highlightedOccurrence,
})),
emptyListMessage: "No occurrences",
isLoading: false,
}}
/>
)}
</ReportTemplate.Widgets>
<ReportTemplate.Footer>
Page last checked:{" "}
<FormattedDate date={new Date(Date.now())} format="datetime" locale="en-us" />
</ReportTemplate.Footer>
<ReportTemplate.ReportContent>
{yourContentRenderer(highlightedOccurrence)}
</ReportTemplate.ReportContent>
</ReportTemplate>
</div>
);#Product Selection
The Report Template has a Title component that can be used to display a title or a Select component (e.g. showing various Siteimprove products).
Example Page
https://www.siteimprove.com
Example Page
https://www.siteimprove.com
const { param, theme } = props;
const [selectedProduct, setSelectedProduct] = useState<string | undefined>("a11y");
const productSelection = {
items: [
{
title: "Accessibility",
value: "a11y",
},
{
title: "QA",
value: "qa",
},
{
title: "SEO",
value: "seo",
},
],
value: selectedProduct,
onChange: (value?: string) => setSelectedProduct(value),
};
const [currentIssue, setCurrentIssue] = useState<IssueItem & { description: string }>();
const [highlightedOccurrence, setHighlightedOccurrence] = useState<number>();
const issues = baseIssues.map((issue) => ({
...issue,
onClick: () => setCurrentIssue(issue),
}));
const [breadCrumbItems, setBreadCrumbItems] = useState<Breadcrumb[]>([]);
useEffect(() => {
if (currentIssue) {
setBreadCrumbItems([
{
title: "Overview",
url: "#",
onClick: (e) => {
e.preventDefault();
setCurrentIssue(undefined);
setHighlightedOccurrence(undefined);
},
},
{ title: currentIssue.text, url: null },
]);
}
}, [currentIssue]);
return (
<div style={{ height: param, display: "flex", flexDirection: "column" }}>
<HorizontalNavigation
appLogo={{
icon: theme === "agentic-ai-2025" ? siteImproveLogoAgenticAi2025 : siteImproveLogo,
href: "https://my2.siteimprove.com",
title: "Go to main page",
"aria-label": "Siteimprove",
"data-observe-key": "top-nav-company-link",
}}
actions={actionsDummyData}
data-observe-key="top-nav"
/>
<ReportTemplate isRightSidebarVisible={false}>
<ReportTemplate.Title select={productSelection} />
<ReportTemplate.Header title="Example Page" url="https://www.siteimprove.com" />
<ReportTemplate.Widgets>
{currentIssue === undefined ? (
<ReportTemplate.Widgets.IssuesLists issueItemLists={[{ title: "Issues", issues }]} />
) : (
<ReportTemplate.Widgets.Issue
breadcrumbs={breadCrumbItems}
title={currentIssue.text}
description={currentIssue.description}
occurrences={{
items: baseOccurrences.map((occurrence, index) => ({
...occurrence,
onClick: () => setHighlightedOccurrence(index),
selected: index === highlightedOccurrence,
})),
emptyListMessage: "No occurrences",
isLoading: false,
}}
/>
)}
</ReportTemplate.Widgets>
<ReportTemplate.Footer>
Page last checked:{" "}
<FormattedDate date={new Date(Date.now())} format="datetime" locale="en-us" />
</ReportTemplate.Footer>
<ReportTemplate.ReportContent>
{yourContentRenderer(highlightedOccurrence)}
</ReportTemplate.ReportContent>
</ReportTemplate>
</div>
);#Share Report
The Report Template has a built-in share modal that can be triggered by passing down a boolean state to the ReportTemplate via the showShareModalState prop.
Product name
Example Page
https://www.siteimprove.com
Example Page
https://www.siteimprove.com
const { param, theme } = props;
const [showShare, setShowShare] = useState(false);
const shareButton = {
tooltip: "Share report",
children: (
<Icon fill={ColorWhite}>
<IconShare />
</Icon>
),
onClick: () => setShowShare(!showShare),
"data-observe-key": "app-report-share-button",
};
const [currentIssue, setCurrentIssue] = useState<IssueItem & { description: string }>();
const [highlightedOccurrence, setHighlightedOccurrence] = useState<number>();
const issues = baseIssues.map((issue) => ({
...issue,
onClick: () => setCurrentIssue(issue),
}));
const [breadCrumbItems, setBreadCrumbItems] = useState<Breadcrumb[]>([]);
useEffect(() => {
if (currentIssue) {
setBreadCrumbItems([
{
title: "Overview",
url: "#",
onClick: (e) => {
e.preventDefault();
setCurrentIssue(undefined);
setHighlightedOccurrence(undefined);
},
},
{ title: currentIssue.text, url: null },
]);
}
}, [currentIssue]);
return (
<div style={{ height: param, display: "flex", flexDirection: "column" }}>
<HorizontalNavigation
appLogo={{
icon: theme === "agentic-ai-2025" ? siteImproveLogoAgenticAi2025 : siteImproveLogo,
href: "https://my2.siteimprove.com",
title: "Go to main page",
"aria-label": "Siteimprove",
"data-observe-key": "top-nav-company-link",
}}
actions={[shareButton, ...actionsDummyData]}
data-observe-key="top-nav"
/>
<ReportTemplate
isRightSidebarVisible={false}
shareURL={"your-url-here.com"}
showShareModal={showShare}
setShowShareModal={setShowShare}
>
<ReportTemplate.Title>
<Icon>
<IconAssistiveTechnology />
</Icon>
Product name
</ReportTemplate.Title>
<ReportTemplate.Header title="Example Page" url="https://www.siteimprove.com" />
<ReportTemplate.Widgets>
{currentIssue === undefined ? (
<ReportTemplate.Widgets.IssuesLists issueItemLists={[{ title: "Issues", issues }]} />
) : (
<ReportTemplate.Widgets.Issue
breadcrumbs={breadCrumbItems}
title={currentIssue.text}
description={currentIssue.description}
occurrences={{
items: baseOccurrences.map((occurrence, index) => ({
...occurrence,
onClick: () => setHighlightedOccurrence(index),
selected: index === highlightedOccurrence,
})),
emptyListMessage: "No occurrences",
isLoading: false,
}}
/>
)}
</ReportTemplate.Widgets>
<ReportTemplate.Footer>
Page last checked:{" "}
<FormattedDate date={new Date(Date.now())} format="datetime" locale="en-us" />
</ReportTemplate.Footer>
<ReportTemplate.ReportContent>
{yourContentRenderer(highlightedOccurrence)}
</ReportTemplate.ReportContent>
</ReportTemplate>
</div>
);#Action Bar
The Report Template has an ActionBar component that can be used to display content in a bar on top of the ReportContent. The ActionBar features a toggle button to show or hide itself.
Product name
Example Page
https://www.siteimprove.com
Example Page
https://www.siteimprove.com
const { param, theme } = props;
const [currentIssue, setCurrentIssue] = useState<IssueItem & { description: string }>();
const [highlightedOccurrence, setHighlightedOccurrence] = useState<number>();
const issues = baseIssues.map((issue) => ({
...issue,
onClick: () => setCurrentIssue(issue),
}));
const [breadCrumbItems, setBreadCrumbItems] = useState<Breadcrumb[]>([]);
useEffect(() => {
if (currentIssue) {
setBreadCrumbItems([
{
title: "Overview",
url: "#",
onClick: (e) => {
e.preventDefault();
setCurrentIssue(undefined);
setHighlightedOccurrence(undefined);
},
},
{ title: currentIssue.text, url: null },
]);
}
}, [currentIssue]);
const [checkedHTML, setCheckedHTML] = React.useState(false);
const [checkedJS, setCheckedJS] = React.useState(false);
const [checkedCSS, setCheckedCSS] = React.useState(false);
return (
<div style={{ height: param, display: "flex", flexDirection: "column" }}>
<HorizontalNavigation
appLogo={{
icon: theme === "agentic-ai-2025" ? siteImproveLogoAgenticAi2025 : siteImproveLogo,
href: "https://my2.siteimprove.com",
title: "Go to main page",
"aria-label": "Siteimprove",
"data-observe-key": "top-nav-company-link",
}}
actions={actionsDummyData}
data-observe-key="top-nav"
/>
<ReportTemplate isRightSidebarVisible={false}>
<ReportTemplate.Title>
<Icon>
<IconAssistiveTechnology />
</Icon>
Product name
</ReportTemplate.Title>
<ReportTemplate.Header title="Example Page" url="https://www.siteimprove.com" />
<ReportTemplate.Widgets>
{currentIssue === undefined ? (
<ReportTemplate.Widgets.IssuesLists issueItemLists={[{ title: "Issues", issues }]} />
) : (
<ReportTemplate.Widgets.Issue
breadcrumbs={breadCrumbItems}
title={currentIssue.text}
description={currentIssue.description}
occurrences={{
items: baseOccurrences.map((occurrence, index) => ({
...occurrence,
onClick: () => setHighlightedOccurrence(index),
selected: index === highlightedOccurrence,
})),
emptyListMessage: "No occurrences",
isLoading: false,
}}
/>
)}
</ReportTemplate.Widgets>
<ReportTemplate.Footer>
Page last checked:{" "}
<FormattedDate date={new Date(Date.now())} format="datetime" locale="en-us" />
</ReportTemplate.Footer>
<ReportTemplate.ReportContent>
<ReportTemplate.ActionBar>
<ToggleSwitch label="HTML view" value={checkedHTML} onChange={setCheckedHTML} />
<Popover
popoverContent={(id) => (
<div id={id}>
<Content>
<ToggleSwitch
style={{ justifyContent: "space-between", marginBottom: "1rem" }}
label="Enable JavaScript"
value={checkedJS}
onChange={setCheckedJS}
/>
<ToggleSwitch
style={{ justifyContent: "space-between" }}
label="Enable CSS"
value={checkedCSS}
onChange={setCheckedCSS}
/>
</Content>
</div>
)}
buttonContent="Enable CSS"
placement="top"
/>
</ReportTemplate.ActionBar>
{yourContentRenderer(highlightedOccurrence)}
</ReportTemplate.ReportContent>
</ReportTemplate>
</div>
);#Right Sidebar
The Report Template has a RightSidebar component that can be used to display additional information or actions.
The sidebar can be toggled by setting the isRightSidebarVisible prop on ReportTemplate.
Product name
Example Page
https://www.siteimprove.com
Example Page
https://www.siteimprove.com
const { param, theme } = props;
const [showRightSidebar, setShowRightSidebar] = useState(false);
const [currentIssue, setCurrentIssue] = useState<IssueItem & { description: string }>();
const [highlightedOccurrence, setHighlightedOccurrence] = useState<number>();
const issues = baseIssues.map((issue) => ({
...issue,
onClick: () => {
setShowRightSidebar(true);
setCurrentIssue(issue);
},
}));
const [breadCrumbItems, setBreadCrumbItems] = useState<Breadcrumb[]>([]);
useEffect(() => {
if (currentIssue) {
setBreadCrumbItems([
{
title: "Overview",
url: "#",
onClick: (e) => {
e.preventDefault();
setShowRightSidebar(false);
setCurrentIssue(undefined);
setHighlightedOccurrence(undefined);
},
},
{ title: currentIssue.text, url: null },
]);
}
}, [currentIssue]);
return (
<div style={{ height: param, display: "flex", flexDirection: "column" }}>
<HorizontalNavigation
appLogo={{
icon: theme === "agentic-ai-2025" ? siteImproveLogoAgenticAi2025 : siteImproveLogo,
href: "https://my2.siteimprove.com",
title: "Go to main page",
"aria-label": "Siteimprove",
"data-observe-key": "top-nav-company-link",
}}
actions={actionsDummyData}
data-observe-key="top-nav"
/>
<ReportTemplate isRightSidebarVisible={showRightSidebar}>
<ReportTemplate.Title>
<Icon>
<IconAssistiveTechnology />
</Icon>
Product name
</ReportTemplate.Title>
<ReportTemplate.Header title="Example Page" url="https://www.siteimprove.com" />
<ReportTemplate.Widgets>
{currentIssue === undefined ? (
<ReportTemplate.Widgets.IssuesLists issueItemLists={[{ title: "Issues", issues }]} />
) : (
<ReportTemplate.Widgets.Issue
breadcrumbs={breadCrumbItems}
title={currentIssue.text}
description={currentIssue.description}
occurrences={{
items: baseOccurrences.map((occurrence, index) => ({
...occurrence,
onClick: () => setHighlightedOccurrence(index),
selected: index === highlightedOccurrence,
})),
emptyListMessage: "No occurrences",
isLoading: false,
}}
/>
)}
</ReportTemplate.Widgets>
<ReportTemplate.Footer>
Page last checked:{" "}
<FormattedDate date={new Date(Date.now())} format="datetime" locale="en-us" />
</ReportTemplate.Footer>
<ReportTemplate.ReportContent>
{yourContentRenderer(highlightedOccurrence)}
</ReportTemplate.ReportContent>
<ReportTemplate.RightSidebar>
<Card>
<Card.Header>Details</Card.Header>
<Content>
Here you can show additional information or actions related to the issue or
occurrences
</Content>
</Card>
</ReportTemplate.RightSidebar>
</ReportTemplate>
</div>
);#Left Sidebar, Widgets, and Footer
The main utility of the Report Template is the left sidebar and its tie in with Widgets and Footer. The LeftSidebar can be outright declared in the template with, though Widgets and Footer can also be used without declaring it.
The purpose of the Widgets component is to take care of overflowing in the left sidebar, as content inside of it will be scrollable if space is limited.
The Widgets component is designed to be used with the IssuesLists and Issue component.
const actionBarState = useState(false);
const [resizeDep, setResizeDep] = useState(0);
const [currentIssue, setCurrentIssue] = useState<IssueItem & { description: string }>();
const issues = baseIssues.map((issue) => ({
...issue,
onClick: () => setCurrentIssue(issue),
}));
const breadcrumbs: Breadcrumb[] = [
{
title: "Overview",
url: "#",
onClick: (e) => {
e.preventDefault();
setCurrentIssue(undefined);
},
},
{ title: "Issue", url: null },
];
return (
<div
style={{
height: "300px",
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
}}
>
<ReportContext.Provider
value={{
resizeDep,
triggerResize: () => setResizeDep((prev) => prev + 1),
actionBarCollapseState: actionBarState,
}}
>
<ReportTemplate.LeftSidebar noPaddingTop>
<ReportTemplate.Widgets>
{currentIssue === undefined ? (
<ReportTemplate.Widgets.IssuesLists issueItemLists={[{ title: "Issues", issues }]} />
) : (
<ReportTemplate.Widgets.Issue
breadcrumbs={breadcrumbs}
title={currentIssue.text}
description={currentIssue.description}
occurrences={{
items: baseOccurrences,
emptyListMessage: "No occurrences",
isLoading: false,
}}
/>
)}
</ReportTemplate.Widgets>
<ReportTemplate.Footer>Page last checked: 3/20/2025, 07:15 PM</ReportTemplate.Footer>
</ReportTemplate.LeftSidebar>
</ReportContext.Provider>
</div>
);#Lists of Issues
IssuesLists is a Widgets component that can generate multiple lists of issues.
Each list that it generates needs a title and issues. Per default, it will add an issue icon to the title. However, this can be overridden by supplying it with an icon or setting icon to "none".
The issues within the list are all created as Links. The intention is to indicate that clicking on an issue will navigate to the issue in question. However, per default the onClick method of the link will call event.preventDefault(), thus preventing the browser from actually navigating to the supplied href. If you do want the browser to navigate to the href, you can set preventDefault="false" on the issue.
const occurrences = baseOccurrences;
const issues: IssueItem[] = [
{
text: "Example issue",
count: occurrences.length,
href: "?issueId=" + 1,
onClick: () => console.log(`You clicked on Example issue`),
},
{
text: "Another example issue",
count: occurrences.length,
href: "?issueId=" + 2,
onClick: () => console.log(`You clicked on Another example issue`),
icon: "none",
},
];
const solvedIssues = [
{
icon: (
<Icon fill={ColorGreenDark}>
<IconResolvedIssue />
</Icon>
),
text: "Solved issue",
count: occurrences.length,
href: "https://www.siteimprove.com/",
onClick: () => console.log(`You clicked on Solved issue`),
preventDefault: false,
},
];
return (
<ReportTemplate.Widgets>
<ReportTemplate.Widgets.IssuesLists
issueItemLists={[
{ title: "Issues", issues, icon: "none" },
{
title: "Solved issues",
icon: (
<Icon fill={ColorGreenDark}>
<IconResolvedIssue />
</Icon>
),
issues: solvedIssues,
},
]}
/>
</ReportTemplate.Widgets>
);#Issue and Occurrences
Issue is a Widgets component for displaying information about an issue and its occurrences.
The component only requires a title but can be supplied with an icon, a description, occurrences, and even your custom content passed down to it through its children.
The occurrences prop is a collection of related properties, where your issue occurrences have to be passed to it as items. Beside occurrence items, you can add filter, make the list searchable, and manipulate the pageChanger if needed.
The Issue component is designed with responsiveness in mind, and will automatically adjust its layout based on the available space. Here the list of occurrences will be displayed in a scrollable list if space is limited.
const actionBarState = useState(false);
const [resizeDep, setResizeDep] = useState(0);
const occurrences = [
{
text: "Occurrence 1",
onClick: () => console.log("You clicked on Occurrence 1"),
},
{
text: "Occurrence 2",
onClick: () => console.log("You clicked on Occurrence 2"),
},
{
text: "Occurrence 3",
onClick: () => console.log("You clicked on Occurrence 3"),
},
{
text: "Occurrence 4",
onClick: () => console.log("You clicked on Occurrence 4"),
},
{
text: "Occurrence 5",
onClick: () => console.log("You clicked on Occurrence 5"),
},
{
text: "Occurrence 6",
onClick: () => console.log("You clicked on Occurrence 6"),
},
];
type Country = { id: string; name: string };
type Filters = {
country: Country;
};
const countries: Country[] = [
{ id: "DK", name: "Denmark" },
{ id: "SE", name: "Sweden" },
{ id: "NO", name: "Norway" },
];
const [filters, setFilters] = useState<Filters>({
country: countries[0],
});
const onChange = (newFilters: Filters) => {
setFilters(newFilters);
};
return (
<ReportContext.Provider
value={{
resizeDep,
triggerResize: () => setResizeDep((prev) => prev + 1),
actionBarCollapseState: actionBarState,
}}
>
<ReportTemplate.Widgets>
<ReportTemplate.Widgets.Issue
icon={
<Icon fill={ColorGreenDark}>
<IconResolvedIssue />
</Icon>
}
title={"Example issue"}
description={"Description of a particular example issue goes here"}
occurrences={{
items: occurrences,
filter: {
value: filters,
onChange,
filters: [
{
label: "Country",
property: "country",
items: countries.map((country) => ({ title: country.name, value: country })),
compareFn: (a, b) => a?.id === b?.id,
stringify: ({ country }) => country?.name,
},
],
},
searchable: true,
pageChanger: {
pageSize: 5,
},
errorMessage: undefined, // If set, will display error message instead of occurrences
isLoading: false,
emptyListMessage: "No occurrences",
}}
>
<Card style={{ marginTop: "1rem" }}>
<Card.Header>Custom content</Card.Header>
<Content>
<Paragraph>Your custom content can be added through children</Paragraph>
</Content>
</Card>
</ReportTemplate.Widgets.Issue>
</ReportTemplate.Widgets>
</ReportContext.Provider>
);#Custom content
The Report Template can be customized quite a bit to fit your needs. For instance, tabs could be used within the title, or custom header content and action buttons can be configured, or custom content can be added in and around the template components, in case you need to display specific breadcrumbs or additional actions.
Example Page
const { param } = props;
const [selectedTab, setSelectedTab] = useState(0);
const [currentIssue, setCurrentIssue] = useState<IssueItem & { description: string }>();
const [highlightedOccurrence, setHighlightedOccurrence] = useState<number>();
const issues = baseIssues.map((issue) => ({
...issue,
onClick: () => setCurrentIssue(issue),
}));
const [breadCrumbItems, setBreadCrumbItems] = useState<Breadcrumb[]>([
{ title: "Issues", url: null },
]);
useEffect(() => {
if (currentIssue) {
setBreadCrumbItems([
{
title: "Issues",
url: "#",
onClick: (e) => {
e.preventDefault();
setCurrentIssue(undefined);
setHighlightedOccurrence(undefined);
},
},
{ title: currentIssue.text, url: null },
]);
} else {
setBreadCrumbItems([{ title: "Issues", url: null }]);
}
}, [currentIssue]);
return (
<ReportTemplate isRightSidebarVisible={false} style={{ height: param }}>
<ReportTemplate.Title>
<H1>
<Tabs
style={{ marginLeft: "24px" }}
selectedTab={selectedTab}
onChange={setSelectedTab}
tabs={[
{
header: <span style={{ fontSize: "1rem" }}>Accessibility</span>,
content: <></>,
},
{
header: <span style={{ fontSize: "1rem" }}>QA</span>,
content: <></>,
},
{
header: <span style={{ fontSize: "1rem" }}>SEO</span>,
content: <></>,
},
]}
/>
</H1>
</ReportTemplate.Title>
<ReportTemplate.Header>
<div
style={{
height: "100%",
display: "flex",
alignItems: "center",
gap: "0.5rem",
paddingLeft: "1rem",
borderLeft: "1px solid #e0e0e0",
}}
>
<Icon>
<IconAssistiveTechnology />
</Icon>
<H2>Example Page</H2>
</div>
<ReportTemplate.Header.ActionButtons>
<Button aria-label="Settings" onClick={() => console.log("You clicked on Settings")}>
<Icon>
<IconSettings />
</Icon>
</Button>
<Button aria-label="Help" onClick={() => console.log("You clicked on Help")}>
<Icon>
<IconHelp />
</Icon>
</Button>
</ReportTemplate.Header.ActionButtons>
</ReportTemplate.Header>
<ReportTemplate.LeftSidebar noPaddingTop>
<div
style={{
height: "48px",
display: "flex",
alignItems: "center",
backgroundColor: "white",
padding: "0 1rem",
borderBottom: "1px solid #e0e0e0",
marginBottom: "1rem",
}}
>
<Breadcrumbs items={breadCrumbItems} aria-label="Report overview" />
</div>
<ReportTemplate.Widgets>
{currentIssue === undefined ? (
<ReportTemplate.Widgets.IssuesLists issueItemLists={[{ title: "Issues", issues }]} />
) : (
<ReportTemplate.Widgets.Issue
title={currentIssue.text}
description={currentIssue.description}
occurrences={{
items: baseOccurrences.map((occurrence, index) => ({
...occurrence,
onClick: () => setHighlightedOccurrence(index),
selected: index === highlightedOccurrence,
})),
emptyListMessage: "No occurrences",
isLoading: false,
}}
/>
)}
</ReportTemplate.Widgets>
<ReportTemplate.Footer>
Page last checked:{" "}
<FormattedDate date={new Date(Date.now())} format="datetime" locale="en-us" />
</ReportTemplate.Footer>
</ReportTemplate.LeftSidebar>
<ReportTemplate.ReportContent noPaddingTop noMargin>
<div style={{ display: "flex", flexDirection: "column", width: "100%", height: "100%" }}>
<div
style={{
height: "48px",
flexShrink: 0,
display: "flex",
alignItems: "center",
justifyContent: "end",
backgroundColor: "white",
padding: "0 1rem",
borderLeft: "1px solid #e0e0e0",
borderBottom: "1px solid #e0e0e0",
}}
>
<Button>Recheck</Button>
<Button>Update</Button>
<Button>Delete</Button>
</div>
{yourContentRenderer(highlightedOccurrence)}
</div>
</ReportTemplate.ReportContent>
</ReportTemplate>
);