From 47b1eccb89d7ce4130987c360a5c28e5e1f913f4 Mon Sep 17 00:00:00 2001 From: "opensearch-trigger-bot[bot]" <98922864+opensearch-trigger-bot[bot]@users.noreply.github.com> Date: Mon, 19 Dec 2022 13:10:50 -0800 Subject: [PATCH] [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 * [FEATURE] Add loading state for all tables/visualizations on Overview page #118 Signed-off-by: Jovan Cvetkovic Signed-off-by: Jovan Cvetkovic (cherry picked from commit b5f752ba4afdd5abf54437037a97905b1319ab6b) Co-authored-by: Jovan Cvetkovic Signed-off-by: AWSHurneyt --- public/app.scss | 2 ++ public/components/Charts/ChartContainer.scss | 21 +++++++++++++++++++ public/components/Charts/ChartContainer.tsx | 21 +++++++++++++++++++ .../components/Widgets/DetectorsWidget.tsx | 13 ++++++++++-- .../components/Widgets/RecentAlertsWidget.tsx | 8 +++++-- .../Widgets/RecentFindingsWidget.tsx | 8 +++++-- .../Overview/components/Widgets/Summary.tsx | 6 ++++-- .../components/Widgets/TableWidget.tsx | 3 ++- .../components/Widgets/TopRulesWidget.tsx | 6 ++++-- .../Overview/containers/Overview/Overview.tsx | 16 ++++++++++---- public/pages/Overview/models/types.ts | 5 ++++- 11 files changed, 93 insertions(+), 16 deletions(-) create mode 100644 public/components/Charts/ChartContainer.scss create mode 100644 public/components/Charts/ChartContainer.tsx diff --git a/public/app.scss b/public/app.scss index 29ff93cfb..0e6d6a564 100644 --- a/public/app.scss +++ b/public/app.scss @@ -44,3 +44,5 @@ $euiTextColor: $euiColorDarkestShade !default; .state-accordion:hover { text-decoration: none; } + +@import "./components/Charts/ChartContainer.scss"; diff --git a/public/components/Charts/ChartContainer.scss b/public/components/Charts/ChartContainer.scss new file mode 100644 index 000000000..0cfaa2fa6 --- /dev/null +++ b/public/components/Charts/ChartContainer.scss @@ -0,0 +1,21 @@ +.chart-view-container { + position: relative; + width: 100%; + height: 100%; + + .chart-view-container-mask { + width: 100%; + height: 100%; + background: white; + opacity: 0.5; + position: absolute; + } + + .chart-view-container-loading { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 1; + } +} diff --git a/public/components/Charts/ChartContainer.tsx b/public/components/Charts/ChartContainer.tsx new file mode 100644 index 000000000..144883369 --- /dev/null +++ b/public/components/Charts/ChartContainer.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import { EuiLoadingChart } from '@elastic/eui'; + +interface ChartContainerProps { + loading?: boolean; + chartViewId: string; +} + +export const ChartContainer: React.FC = ({ chartViewId, loading = false }) => { + return ( +
+ {loading && ( + <> + +
+ + )} +
+
+ ); +}; diff --git a/public/pages/Overview/components/Widgets/DetectorsWidget.tsx b/public/pages/Overview/components/Widgets/DetectorsWidget.tsx index 9f37a9d91..21e745145 100644 --- a/public/pages/Overview/components/Widgets/DetectorsWidget.tsx +++ b/public/pages/Overview/components/Widgets/DetectorsWidget.tsx @@ -44,9 +44,14 @@ const getColumns = ( export interface DetectorsWidgetProps extends RouteComponentProps { detectorHits: DetectorHit[]; + loading?: boolean; } -export const DetectorsWidget: React.FC = ({ detectorHits, history }) => { +export const DetectorsWidget: React.FC = ({ + detectorHits, + history, + loading = false, +}) => { const detectors = detectorHits.map((detectorHit) => ({ detectorName: detectorHit._source.name, id: detectorHit._id, @@ -76,7 +81,11 @@ export const DetectorsWidget: React.FC = ({ detectorHits, return ( - + ); }; diff --git a/public/pages/Overview/components/Widgets/RecentAlertsWidget.tsx b/public/pages/Overview/components/Widgets/RecentAlertsWidget.tsx index d86c5f229..6580ed615 100644 --- a/public/pages/Overview/components/Widgets/RecentAlertsWidget.tsx +++ b/public/pages/Overview/components/Widgets/RecentAlertsWidget.tsx @@ -37,9 +37,13 @@ const columns: EuiBasicTableColumn[] = [ export interface RecentAlertsWidgetProps { items: AlertItem[]; + loading?: boolean; } -export const RecentAlertsWidget: React.FC = ({ items }) => { +export const RecentAlertsWidget: React.FC = ({ + items, + loading = false, +}) => { const [alertItems, setAlertItems] = useState([]); useEffect(() => { @@ -61,7 +65,7 @@ export const RecentAlertsWidget: React.FC = ({ items }) title={`Top ${alertItems.length < 20 ? '' : 20} recent alerts`} actions={actions} > - + ); }; diff --git a/public/pages/Overview/components/Widgets/RecentFindingsWidget.tsx b/public/pages/Overview/components/Widgets/RecentFindingsWidget.tsx index 76e20ecd8..d18136bfd 100644 --- a/public/pages/Overview/components/Widgets/RecentFindingsWidget.tsx +++ b/public/pages/Overview/components/Widgets/RecentFindingsWidget.tsx @@ -44,9 +44,13 @@ const columns: EuiBasicTableColumn[] = [ export interface RecentFindingsWidgetProps { items: FindingItem[]; + loading?: boolean; } -export const RecentFindingsWidget: React.FC = ({ items }) => { +export const RecentFindingsWidget: React.FC = ({ + items, + loading = false, +}) => { const [findingItems, setFindingItems] = useState([]); useEffect(() => { @@ -66,7 +70,7 @@ export const RecentFindingsWidget: React.FC = ({ item title={`Top ${findingItems.length < 20 ? '' : 20} recent findings`} actions={actions} > - + ); }; diff --git a/public/pages/Overview/components/Widgets/Summary.tsx b/public/pages/Overview/components/Widgets/Summary.tsx index f7cec1576..0ecfc9d0e 100644 --- a/public/pages/Overview/components/Widgets/Summary.tsx +++ b/public/pages/Overview/components/Widgets/Summary.tsx @@ -11,10 +11,12 @@ import { getOverviewVisualizationSpec, getTimeWithMinPrecision } from '../../uti import { AlertItem, FindingItem } from '../../models/interfaces'; import { createSelectComponent, renderVisualization } from '../../../../utils/helpers'; import { ROUTES } from '../../../../utils/constants'; +import { ChartContainer } from '../../../../components/Charts/ChartContainer'; export interface SummaryProps { findings: FindingItem[]; alerts: AlertItem[]; + loading?: boolean; } export interface SummaryData { @@ -24,7 +26,7 @@ export interface SummaryData { logType?: string; } -export const Summary: React.FC = ({ alerts, findings }) => { +export const Summary: React.FC = ({ alerts, findings, loading = false }) => { const [groupBy, setGroupBy] = useState(''); const [summaryData, setSummaryData] = useState([]); const [activeAlerts, setActiveAlerts] = useState(0); @@ -112,7 +114,7 @@ export const Summary: React.FC = ({ alerts, findings }) => { -
+
diff --git a/public/pages/Overview/components/Widgets/TableWidget.tsx b/public/pages/Overview/components/Widgets/TableWidget.tsx index 90ece4c6d..f59bd4da9 100644 --- a/public/pages/Overview/components/Widgets/TableWidget.tsx +++ b/public/pages/Overview/components/Widgets/TableWidget.tsx @@ -9,7 +9,7 @@ import { TableWidgetItem, TableWidgetProps } from '../../models/types'; export class TableWidget extends React.Component> { render() { - const { columns, items } = this.props; + const { columns, items, loading = false } = this.props; return ( @@ -18,6 +18,7 @@ export class TableWidget extends React.Component `${item.id}`} pagination={{ pageSize: 10, pageSizeOptions: [10] }} + loading={loading} /> ); } diff --git a/public/pages/Overview/components/Widgets/TopRulesWidget.tsx b/public/pages/Overview/components/Widgets/TopRulesWidget.tsx index 2d93de03f..40de1f699 100644 --- a/public/pages/Overview/components/Widgets/TopRulesWidget.tsx +++ b/public/pages/Overview/components/Widgets/TopRulesWidget.tsx @@ -8,14 +8,16 @@ import React, { useEffect } from 'react'; import { FindingItem } from '../../models/interfaces'; import { WidgetContainer } from './WidgetContainer'; import { getTopRulesVisualizationSpec } from '../../utils/helpers'; +import { ChartContainer } from '../../../../components/Charts/ChartContainer'; export interface TopRulesWidgetProps { findings: FindingItem[]; + loading?: boolean; } type RulesCount = { [ruleName: string]: number }; -export const TopRulesWidget: React.FC = ({ findings }) => { +export const TopRulesWidget: React.FC = ({ findings, loading = false }) => { useEffect(() => { const rulesCount: RulesCount = {}; findings.forEach((finding) => { @@ -33,7 +35,7 @@ export const TopRulesWidget: React.FC = ({ findings }) => { return ( -
+
); }; diff --git a/public/pages/Overview/containers/Overview/Overview.tsx b/public/pages/Overview/containers/Overview/Overview.tsx index 03f168540..d84d93233 100644 --- a/public/pages/Overview/containers/Overview/Overview.tsx +++ b/public/pages/Overview/containers/Overview/Overview.tsx @@ -36,6 +36,7 @@ export const Overview: React.FC = (props) => { alerts: [], }, }); + const [loading, setLoading] = useState(true); const context = useContext(CoreServicesContext); const services = useContext(ServicesContext); @@ -44,6 +45,7 @@ export const Overview: React.FC = (props) => { ...state, overviewViewModel: { ...overviewViewModel }, }); + setLoading(false); }; const overviewViewModelActor = useMemo( @@ -78,6 +80,7 @@ export const Overview: React.FC = (props) => { }; const onRefresh = async () => { + setLoading(true); overviewViewModelActor.onRefresh(); }; @@ -121,15 +124,20 @@ export const Overview: React.FC = (props) => { - - - - + + + + diff --git a/public/pages/Overview/models/types.ts b/public/pages/Overview/models/types.ts index 7528b0aae..e50bb05b3 100644 --- a/public/pages/Overview/models/types.ts +++ b/public/pages/Overview/models/types.ts @@ -1,7 +1,9 @@ /* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 - */ + */ { + loading; +} import { EuiBasicTableColumn } from '@elastic/eui'; import { AlertItem, DetectorItem, FindingItem } from './interfaces'; @@ -11,4 +13,5 @@ export type TableWidgetItem = FindingItem | AlertItem | DetectorItem; export type TableWidgetProps = { columns: EuiBasicTableColumn[]; items: T[]; + loading?: boolean; };