Skip to content

Commit b491ee8

Browse files
[FEATURE] Add loading state for all tables visualizations on overview page (#237) (#238)
* [FEATURE] Add loading state for all tables/visualizations on Overview page #118 Signed-off-by: Jovan Cvetkovic <[email protected]> * [FEATURE] Add loading state for all tables/visualizations on Overview page #118 Signed-off-by: Jovan Cvetkovic <[email protected]> Signed-off-by: Jovan Cvetkovic <[email protected]> (cherry picked from commit b5f752b) Co-authored-by: Jovan Cvetkovic <[email protected]>
1 parent d395274 commit b491ee8

File tree

11 files changed

+93
-16
lines changed

11 files changed

+93
-16
lines changed

public/app.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,5 @@ $euiTextColor: $euiColorDarkestShade !default;
4444
.state-accordion:hover {
4545
text-decoration: none;
4646
}
47+
48+
@import "./components/Charts/ChartContainer.scss";
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.chart-view-container {
2+
position: relative;
3+
width: 100%;
4+
height: 100%;
5+
6+
.chart-view-container-mask {
7+
width: 100%;
8+
height: 100%;
9+
background: white;
10+
opacity: 0.5;
11+
position: absolute;
12+
}
13+
14+
.chart-view-container-loading {
15+
position: absolute;
16+
top: 50%;
17+
left: 50%;
18+
transform: translate(-50%, -50%);
19+
z-index: 1;
20+
}
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from 'react';
2+
import { EuiLoadingChart } from '@elastic/eui';
3+
4+
interface ChartContainerProps {
5+
loading?: boolean;
6+
chartViewId: string;
7+
}
8+
9+
export const ChartContainer: React.FC<ChartContainerProps> = ({ chartViewId, loading = false }) => {
10+
return (
11+
<div className="chart-view-container">
12+
{loading && (
13+
<>
14+
<EuiLoadingChart size="xl" className="chart-view-container-loading" />
15+
<div className="chart-view-container-mask"></div>
16+
</>
17+
)}
18+
<div id={chartViewId}></div>
19+
</div>
20+
);
21+
};

public/pages/Overview/components/Widgets/DetectorsWidget.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,14 @@ const getColumns = (
4444

4545
export interface DetectorsWidgetProps extends RouteComponentProps {
4646
detectorHits: DetectorHit[];
47+
loading?: boolean;
4748
}
4849

49-
export const DetectorsWidget: React.FC<DetectorsWidgetProps> = ({ detectorHits, history }) => {
50+
export const DetectorsWidget: React.FC<DetectorsWidgetProps> = ({
51+
detectorHits,
52+
history,
53+
loading = false,
54+
}) => {
5055
const detectors = detectorHits.map((detectorHit) => ({
5156
detectorName: detectorHit._source.name,
5257
id: detectorHit._id,
@@ -76,7 +81,11 @@ export const DetectorsWidget: React.FC<DetectorsWidgetProps> = ({ detectorHits,
7681

7782
return (
7883
<WidgetContainer title={`Detectors (${detectors.length})`} actions={actions}>
79-
<TableWidget columns={getColumns(detectorIdToHit, showDetectorDetails)} items={detectors} />
84+
<TableWidget
85+
columns={getColumns(detectorIdToHit, showDetectorDetails)}
86+
items={detectors}
87+
loading={loading}
88+
/>
8089
</WidgetContainer>
8190
);
8291
};

public/pages/Overview/components/Widgets/RecentAlertsWidget.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ const columns: EuiBasicTableColumn<AlertItem>[] = [
3737

3838
export interface RecentAlertsWidgetProps {
3939
items: AlertItem[];
40+
loading?: boolean;
4041
}
4142

42-
export const RecentAlertsWidget: React.FC<RecentAlertsWidgetProps> = ({ items }) => {
43+
export const RecentAlertsWidget: React.FC<RecentAlertsWidgetProps> = ({
44+
items,
45+
loading = false,
46+
}) => {
4347
const [alertItems, setAlertItems] = useState<AlertItem[]>([]);
4448

4549
useEffect(() => {
@@ -61,7 +65,7 @@ export const RecentAlertsWidget: React.FC<RecentAlertsWidgetProps> = ({ items })
6165
title={`Top ${alertItems.length < 20 ? '' : 20} recent alerts`}
6266
actions={actions}
6367
>
64-
<TableWidget columns={columns} items={alertItems} />
68+
<TableWidget columns={columns} items={alertItems} loading={loading} />
6569
</WidgetContainer>
6670
);
6771
};

public/pages/Overview/components/Widgets/RecentFindingsWidget.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,13 @@ const columns: EuiBasicTableColumn<FindingItem>[] = [
4444

4545
export interface RecentFindingsWidgetProps {
4646
items: FindingItem[];
47+
loading?: boolean;
4748
}
4849

49-
export const RecentFindingsWidget: React.FC<RecentFindingsWidgetProps> = ({ items }) => {
50+
export const RecentFindingsWidget: React.FC<RecentFindingsWidgetProps> = ({
51+
items,
52+
loading = false,
53+
}) => {
5054
const [findingItems, setFindingItems] = useState<FindingItem[]>([]);
5155

5256
useEffect(() => {
@@ -66,7 +70,7 @@ export const RecentFindingsWidget: React.FC<RecentFindingsWidgetProps> = ({ item
6670
title={`Top ${findingItems.length < 20 ? '' : 20} recent findings`}
6771
actions={actions}
6872
>
69-
<TableWidget columns={columns} items={findingItems} />
73+
<TableWidget columns={columns} items={findingItems} loading={loading} />
7074
</WidgetContainer>
7175
);
7276
};

public/pages/Overview/components/Widgets/Summary.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@ import { getOverviewVisualizationSpec, getTimeWithMinPrecision } from '../../uti
1111
import { AlertItem, FindingItem } from '../../models/interfaces';
1212
import { createSelectComponent, renderVisualization } from '../../../../utils/helpers';
1313
import { ROUTES } from '../../../../utils/constants';
14+
import { ChartContainer } from '../../../../components/Charts/ChartContainer';
1415

1516
export interface SummaryProps {
1617
findings: FindingItem[];
1718
alerts: AlertItem[];
19+
loading?: boolean;
1820
}
1921

2022
export interface SummaryData {
@@ -24,7 +26,7 @@ export interface SummaryData {
2426
logType?: string;
2527
}
2628

27-
export const Summary: React.FC<SummaryProps> = ({ alerts, findings }) => {
29+
export const Summary: React.FC<SummaryProps> = ({ alerts, findings, loading = false }) => {
2830
const [groupBy, setGroupBy] = useState('');
2931
const [summaryData, setSummaryData] = useState<SummaryData[]>([]);
3032
const [activeAlerts, setActiveAlerts] = useState(0);
@@ -112,7 +114,7 @@ export const Summary: React.FC<SummaryProps> = ({ alerts, findings }) => {
112114
</EuiFlexGroup>
113115
</EuiFlexItem>
114116
<EuiFlexItem>
115-
<div id="summary-view" style={{ width: '100%' }}></div>
117+
<ChartContainer chartViewId={'summary-view'} loading={loading} />
116118
</EuiFlexItem>
117119
</EuiFlexGroup>
118120
</WidgetContainer>

public/pages/Overview/components/Widgets/TableWidget.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { TableWidgetItem, TableWidgetProps } from '../../models/types';
99

1010
export class TableWidget<T extends TableWidgetItem> extends React.Component<TableWidgetProps<T>> {
1111
render() {
12-
const { columns, items } = this.props;
12+
const { columns, items, loading = false } = this.props;
1313

1414
return (
1515
<EuiInMemoryTable<T>
@@ -18,6 +18,7 @@ export class TableWidget<T extends TableWidgetItem> extends React.Component<Tabl
1818
items={items}
1919
itemId={(item: T) => `${item.id}`}
2020
pagination={{ pageSize: 10, pageSizeOptions: [10] }}
21+
loading={loading}
2122
/>
2223
);
2324
}

public/pages/Overview/components/Widgets/TopRulesWidget.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ import React, { useEffect } from 'react';
88
import { FindingItem } from '../../models/interfaces';
99
import { WidgetContainer } from './WidgetContainer';
1010
import { getTopRulesVisualizationSpec } from '../../utils/helpers';
11+
import { ChartContainer } from '../../../../components/Charts/ChartContainer';
1112

1213
export interface TopRulesWidgetProps {
1314
findings: FindingItem[];
15+
loading?: boolean;
1416
}
1517

1618
type RulesCount = { [ruleName: string]: number };
1719

18-
export const TopRulesWidget: React.FC<TopRulesWidgetProps> = ({ findings }) => {
20+
export const TopRulesWidget: React.FC<TopRulesWidgetProps> = ({ findings, loading = false }) => {
1921
useEffect(() => {
2022
const rulesCount: RulesCount = {};
2123
findings.forEach((finding) => {
@@ -33,7 +35,7 @@ export const TopRulesWidget: React.FC<TopRulesWidgetProps> = ({ findings }) => {
3335

3436
return (
3537
<WidgetContainer title="Most frequent detection rules">
36-
<div id="top-rules-view"></div>
38+
<ChartContainer chartViewId={'top-rules-view'} loading={loading} />
3739
</WidgetContainer>
3840
);
3941
};

public/pages/Overview/containers/Overview/Overview.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export const Overview: React.FC<OverviewProps> = (props) => {
3636
alerts: [],
3737
},
3838
});
39+
const [loading, setLoading] = useState(true);
3940
const context = useContext(CoreServicesContext);
4041
const services = useContext(ServicesContext);
4142

@@ -44,6 +45,7 @@ export const Overview: React.FC<OverviewProps> = (props) => {
4445
...state,
4546
overviewViewModel: { ...overviewViewModel },
4647
});
48+
setLoading(false);
4749
};
4850

4951
const overviewViewModelActor = useMemo(
@@ -78,6 +80,7 @@ export const Overview: React.FC<OverviewProps> = (props) => {
7880
};
7981

8082
const onRefresh = async () => {
83+
setLoading(true);
8184
overviewViewModelActor.onRefresh();
8285
};
8386

@@ -121,15 +124,20 @@ export const Overview: React.FC<OverviewProps> = (props) => {
121124
<Summary
122125
alerts={state.overviewViewModel.alerts}
123126
findings={state.overviewViewModel.findings}
127+
loading={loading}
124128
/>
125129
</EuiFlexItem>
126130

127131
<EuiFlexItem>
128132
<EuiFlexGrid columns={2} gutterSize="m">
129-
<RecentAlertsWidget items={state.overviewViewModel.alerts} />
130-
<RecentFindingsWidget items={state.overviewViewModel.findings} />
131-
<TopRulesWidget findings={state.overviewViewModel.findings} />
132-
<DetectorsWidget detectorHits={state.overviewViewModel.detectors} {...props} />
133+
<RecentAlertsWidget items={state.overviewViewModel.alerts} loading={loading} />
134+
<RecentFindingsWidget items={state.overviewViewModel.findings} loading={loading} />
135+
<TopRulesWidget findings={state.overviewViewModel.findings} loading={loading} />
136+
<DetectorsWidget
137+
detectorHits={state.overviewViewModel.detectors}
138+
{...props}
139+
loading={loading}
140+
/>
133141
</EuiFlexGrid>
134142
</EuiFlexItem>
135143
</EuiFlexGroup>

0 commit comments

Comments
 (0)