From 59c8784ed51902e485620b7689cd41751dbfee87 Mon Sep 17 00:00:00 2001 From: Guido Modarelli <38738725+guidomodarelli@users.noreply.github.com> Date: Wed, 18 Sep 2024 17:47:49 -0300 Subject: [PATCH] Remove many loading spinners in some views (#6956) * feat: Add loading searchbar progress component * Change loading spinners to search progress in some views * style: Remove unused imports and elements from LoadingSearchbarProgress * Bump Wazuh 4.9.2 rev 00 (#6971) * Bump version to 4.9.2 * Fix changelog * Update Wazuh version to 4.9.1 in all files * Fix Prettier issues * Refactor error handling imports in wz-flyout-discover * Fix Prettier issues * Update loading spinner to loading searchbar progress component * Refactor loading spinners to use searchbar progress bars * Refactor loading spinner and panel components * Update condition to check for server in third argument * Remove loading spinner component and styles * Refactor compliance table functions and components * Remove unnecessary SCSS file import in LoadingSearchbarProgress * Refactor function parameters and object keys in compliance table --------- Co-authored-by: Federico Rodriguez --- CHANGELOG.md | 3 +- docker/osd-dev/dev.sh | 2 +- .../loading-searchbar-progress.tsx | 12 + .../common/wazuh-data-grid/wz-data-grid.tsx | 4 +- .../common/wazuh-discover/wz-discover.tsx | 190 ++++++++-------- .../wazuh-discover/wz-flyout-discover.tsx | 140 ++++++------ .../common/welcome/dashboard/events-count.tsx | 4 +- .../cluster/dashboard/dashboard.tsx | 4 +- .../dashboards/dashboard.tsx | 84 +++---- .../compliance-table/compliance-table.tsx | 138 ++++++------ .../overview/docker/dashboards/dashboard.tsx | 80 +++---- .../overview/fim/dashboard/dashboard.tsx | 84 +++---- .../overview/gdpr/dashboards/dashboard.tsx | 80 +++---- .../overview/github/dashboards/dashboard.tsx | 82 +++---- .../overview/github/panel/github-panel.tsx | 66 +++--- .../google-cloud/dashboards/dashboard.tsx | 80 +++---- .../overview/hipaa/dashboards/dashboard.tsx | 80 +++---- .../malware-detection/dashboard/dashboard.tsx | 84 +++---- .../overview/mitre/dashboard/dashboard.tsx | 80 +++---- .../overview/mitre/framework/mitre.tsx | 106 ++++----- .../overview/nist/dashboards/dashboard.tsx | 80 +++---- .../overview/office/dashboard/dashboard.tsx | 116 +++++----- .../overview/office/panel/office-panel.tsx | 16 +- .../overview/pci/dashboards/dashboard.tsx | 80 +++---- .../dashboards/dashboardTabsPanels.tsx | 4 +- .../threat-hunting/dashboard/dashboard.tsx | 208 +++++++++--------- .../overview/tsc/dashboards/dashboard.tsx | 80 +++---- .../virustotal/dashboard/dashboard.tsx | 124 +++++------ .../common/components/loading_spinner.scss | 4 - .../common/components/loading_spinner.tsx | 21 -- .../dashboards/inventory/inventory.tsx | 4 +- .../dashboards/overview/dashboard.tsx | 4 +- .../management/cluster/cluster-overview.tsx | 4 +- 33 files changed, 1075 insertions(+), 1073 deletions(-) create mode 100644 plugins/main/public/components/common/loading-searchbar-progress/loading-searchbar-progress.tsx delete mode 100644 plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.scss delete mode 100644 plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.tsx diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d6fbf59d7..39356fab6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ All notable changes to the Wazuh app project will be documented in this file. - Upgraded Event-tab column selector showing first the picked columns [#6984](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6984) - Changed vulnerabilities.reference to links in Vulnerability Detection > Inventory columns [#6960](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6960) - Upgraded the `follow-redirects` dependency to `1.15.6` [#6982](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6982) +- Changed many loading spinners in some views to loading search progress [#6956](https://github.com/wazuh/wazuh-dashboard-plugins/pull/6956) ## Wazuh v4.9.0 - OpenSearch Dashboards 2.13.0 - Revision 07 @@ -644,7 +645,7 @@ All notable changes to the Wazuh app project will be documented in this file. [#3367](https://github.com/wazuh/wazuh-dashboard-plugins/pull/3367) [#3373](https://github.com/wazuh/wazuh-dashboard-plugins/pull/3373) [#3374](https://github.com/wazuh/wazuh-dashboard-plugins/pull/3374) - [#3390](https://github.com/wazuh/wazuh-dashboard-plugins/pull/3390) + [#3390](https://github.com/wazuh/wazuh-dashboard-plugins/pull/3390) [#3410](https://github.com/wazuh/wazuh-dashboard-plugins/pull/3410) [#3408](https://github.com/wazuh/wazuh-dashboard-plugins/pull/3408) [#3429](https://github.com/wazuh/wazuh-dashboard-plugins/pull/3429) diff --git a/docker/osd-dev/dev.sh b/docker/osd-dev/dev.sh index 8215d59896..75a4b91540 100755 --- a/docker/osd-dev/dev.sh +++ b/docker/osd-dev/dev.sh @@ -133,7 +133,7 @@ up) docker compose --profile $profile -f dev.yml up -Vd # Display a command to deploy an agent when using the real server - if [[ "$5" =~ "server" ]]; then + if [[ "$3" =~ "server" ]]; then echo echo "**************WARNING**************" echo "The agent version must be a published one. This uses only released versions." diff --git a/plugins/main/public/components/common/loading-searchbar-progress/loading-searchbar-progress.tsx b/plugins/main/public/components/common/loading-searchbar-progress/loading-searchbar-progress.tsx new file mode 100644 index 0000000000..263b739b4d --- /dev/null +++ b/plugins/main/public/components/common/loading-searchbar-progress/loading-searchbar-progress.tsx @@ -0,0 +1,12 @@ +import { EuiProgress } from '@elastic/eui'; +import React from 'react'; + +interface LoadingSearchbarProgress { + message?: React.ReactNode; +} + +export function LoadingSearchbarProgress({ + message, +}: LoadingSearchbarProgress) { + return ; +} diff --git a/plugins/main/public/components/common/wazuh-data-grid/wz-data-grid.tsx b/plugins/main/public/components/common/wazuh-data-grid/wz-data-grid.tsx index c5d0e83cf8..0da3e2e1c5 100644 --- a/plugins/main/public/components/common/wazuh-data-grid/wz-data-grid.tsx +++ b/plugins/main/public/components/common/wazuh-data-grid/wz-data-grid.tsx @@ -29,7 +29,7 @@ import { ErrorFactory, HttpError, } from '../../../react-services/error-management'; -import { LoadingSpinner } from '../loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../loading-searchbar-progress/loading-searchbar-progress'; import { DiscoverNoResults } from '../no-results/no-results'; import { DocumentViewTableAndJson } from '../wazuh-discover/components/document-view-table-and-json'; import DiscoverDataGridAdditionalControls from '../wazuh-discover/components/data-grid-additional-controls'; @@ -152,7 +152,7 @@ const WazuhDataGrid = (props: tWazuhDataGridProps) => { return ( <> - {isLoading ? : null} + {isLoading ? : null} {!isLoading && !results?.hits?.total === 0 ? ( ) : null} diff --git a/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx b/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx index e8ac8842bd..f679f0a142 100644 --- a/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx +++ b/plugins/main/public/components/common/wazuh-discover/wz-discover.tsx @@ -16,7 +16,7 @@ import { IntlProvider } from 'react-intl'; import { IndexPattern } from '../../../../../../src/plugins/data/common'; import { SearchResponse } from '../../../../../../src/core/server'; import { DiscoverNoResults } from '../no-results/no-results'; -import { LoadingSpinner } from '../loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../loading-searchbar-progress/loading-searchbar-progress'; import { useDataGrid, tDataGridColumn, @@ -190,17 +190,17 @@ const WazuhDiscoverComponent = (props: WazuhDiscoverProps) => { return ( - - {isDataSourceLoading ? ( - - ) : ( + {isDataSourceLoading ? ( + + ) : ( + { showQueryBar={true} showSaveQuery={true} /> - )} - {!isDataSourceLoading && results?.hits?.total === 0 ? ( - - ) : null} -
0 - ? '' - : 'wz-no-display' - } - > - + ) : null} +
0 + ? '' + : 'wz-no-display' + } > - - - - - + + + + + + + - - - - - - - ), - }} - /> - - - - {inspectedHit && ( - setInspectedHit(undefined)} size='m'> - - - - - - - - - - - - )} -
- + + + + + + ), + }} + /> + + +
+ {inspectedHit && ( + setInspectedHit(undefined)} size='m'> + + + + + + + + + + + + )} +
+
+ )}
); }; diff --git a/plugins/main/public/components/common/wazuh-discover/wz-flyout-discover.tsx b/plugins/main/public/components/common/wazuh-discover/wz-flyout-discover.tsx index 1182491229..93cb855a4f 100644 --- a/plugins/main/public/components/common/wazuh-discover/wz-flyout-discover.tsx +++ b/plugins/main/public/components/common/wazuh-discover/wz-flyout-discover.tsx @@ -15,7 +15,7 @@ import { IntlProvider } from 'react-intl'; import { IndexPattern } from '../../../../../../src/plugins/data/public'; import { SearchResponse } from '../../../../../../src/core/server'; import { DiscoverNoResults } from '../no-results/no-results'; -import { LoadingSpinner } from '../loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../loading-searchbar-progress/loading-searchbar-progress'; import { tDataGridColumn } from '../data-grid'; import { ErrorHandler, @@ -268,82 +268,82 @@ const WazuhFlyoutDiscoverComponent = (props: WazuhDiscoverProps) => { return ( - - - {isDataSourceLoading ? ( - - ) : ( + {isDataSourceLoading ? ( + + ) : ( + + - )} - {!isDataSourceLoading && results?.hits?.total === 0 && ( - - )} - {!isDataSourceLoading && dataSource && results?.hits?.total > 0 && ( - <> - - MAX_ENTRIES_PER_QUERY - ? { - ariaLabel: 'Warning', - content: `The query results exceeded the limit of ${formatNumWithCommas( - MAX_ENTRIES_PER_QUERY, - )} hits. Please refine your search.`, - iconType: 'alert', - position: 'top', - } - : undefined - } + {!isDataSourceLoading && results?.hits?.total === 0 && ( + + )} + {!isDataSourceLoading && dataSource && results?.hits?.total > 0 && ( + <> + + MAX_ENTRIES_PER_QUERY + ? { + ariaLabel: 'Warning', + content: `The query results exceeded the limit of ${formatNumWithCommas( + MAX_ENTRIES_PER_QUERY, + )} hits. Please refine your search.`, + iconType: 'alert', + position: 'top', + } + : undefined + } + /> + {absoluteDateRange ? ( + + + + {formatUIDate(absoluteDateRange?.from)} -{' '} + {formatUIDate(absoluteDateRange?.to)} + + + + ) : null} + + MAX_ENTRIES_PER_QUERY + ? MAX_ENTRIES_PER_QUERY + : results?.hits?.total ?? 0, + }} + sorting={sorting} + onChange={onTableChange} /> - {absoluteDateRange ? ( - - - - {formatUIDate(absoluteDateRange?.from)} -{' '} - {formatUIDate(absoluteDateRange?.to)} - - - - ) : null} - - MAX_ENTRIES_PER_QUERY - ? MAX_ENTRIES_PER_QUERY - : results?.hits?.total ?? 0, - }} - sorting={sorting} - onChange={onTableChange} - /> - - )} - - + + )} + + + )} ); }; diff --git a/plugins/main/public/components/common/welcome/dashboard/events-count.tsx b/plugins/main/public/components/common/welcome/dashboard/events-count.tsx index e41f399a5f..a0b2c009c7 100644 --- a/plugins/main/public/components/common/welcome/dashboard/events-count.tsx +++ b/plugins/main/public/components/common/welcome/dashboard/events-count.tsx @@ -14,7 +14,7 @@ import { EuiText, } from '@elastic/eui'; import { useTimeFilter } from '../../hooks'; -import { LoadingSpinner } from '../../loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../loading-searchbar-progress/loading-searchbar-progress'; const plugins = getPlugins(); const DashboardByRenderer = plugins.dashboard.DashboardContainerByValueRenderer; @@ -67,7 +67,7 @@ export const EventsCount = () => { }} /> ) : ( - + )} diff --git a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx index d469c57779..c4a8e64b9a 100644 --- a/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx +++ b/plugins/main/public/components/management/cluster/dashboard/dashboard.tsx @@ -8,7 +8,7 @@ import { ErrorHandler, HttpError, } from '../../../../react-services/error-management'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { withErrorBoundary } from '../../../common/hocs/error-boundary/with-error-boundary'; import { EuiSpacer, EuiFlexItem } from '@elastic/eui'; @@ -154,7 +154,7 @@ const DashboardCT: React.FC = ({ statusRunning }) => { {isDataSourceLoading && !dataSource ? ( - + ) : !state.showNodes ? ( { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 - ? '' - : 'wz-no-display' - } - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 + ? '' + : 'wz-no-display' + } + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/compliance-table/compliance-table.tsx b/plugins/main/public/components/overview/compliance-table/compliance-table.tsx index cba7ddfcf5..d186be87a9 100644 --- a/plugins/main/public/components/overview/compliance-table/compliance-table.tsx +++ b/plugins/main/public/components/overview/compliance-table/compliance-table.tsx @@ -35,7 +35,7 @@ import { } from '../../common/data-source'; import { IndexPattern } from '../../../../../../src/plugins/data/common'; import useSearchBar from '../../common/search-bar/use-search-bar'; -import { LoadingSpinner } from '../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../common/loading-searchbar-progress/loading-searchbar-progress'; import { I18nProvider } from '@osd/i18n/react'; import { useAsyncAction } from '../../common/hooks'; import { WzSearchBar } from '../../common/search-bar'; @@ -262,7 +262,9 @@ export const ComplianceTable = withAgentSupportModule(props => { useEffect(() => { const { descriptions, complianceObject, selectedRequirements } = - buildComplianceObject({ section: props.section }); + buildComplianceObject({ + section: props.section, + }); setComplianceData({ descriptions, complianceObject, selectedRequirements }); }, []); @@ -284,16 +286,16 @@ export const ComplianceTable = withAgentSupportModule(props => { return ( - <> - - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> + { showQueryBar={true} showSaveQuery={true} /> - )} - - - - - - {!!Object.keys(complianceData.complianceObject).length && ( - - - - setComplianceData(state => ({ - ...state, - selectedRequirements, - })) - } - requirementsCount={action.data || []} - loadingAlerts={action.running} - {...complianceData} - /> - - - - props.onSelectedTabChanged(id) - } - requirementsCount={action.data || []} - loadingAlerts={action.running} - fetchFilters={fetchFilters} - getRegulatoryComplianceRequirementFilter={ - getRegulatoryComplianceRequirementFilter - } - {...complianceData} - /> - - - )} - - - - + + + + + {!!Object.keys(complianceData.complianceObject).length && ( + + + + setComplianceData(state => ({ + ...state, + selectedRequirements, + })) + } + requirementsCount={action.data || []} + loadingAlerts={action.running} + {...complianceData} + /> + + + + props.onSelectedTabChanged(id) + } + requirementsCount={action.data || []} + loadingAlerts={action.running} + fetchFilters={fetchFilters} + getRegulatoryComplianceRequirementFilter={ + getRegulatoryComplianceRequirementFilter + } + {...complianceData} + /> + + + )} + + + + + + )} ); }); diff --git a/plugins/main/public/components/overview/docker/dashboards/dashboard.tsx b/plugins/main/public/components/overview/docker/dashboards/dashboard.tsx index c4170c6a91..e95a8a46d6 100644 --- a/plugins/main/public/components/overview/docker/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/docker/dashboards/dashboard.tsx @@ -23,7 +23,7 @@ import { DockerDataSource, } from '../../../common/data-source'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context'; import { WzSearchBar } from '../../../common/search-bar'; @@ -90,10 +90,10 @@ const DashboardDockerComponent: React.FC = ({}) => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryInput={true} showQueryBar={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 ? '' : 'wz-no-display' - } - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 ? '' : 'wz-no-display' + } + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/fim/dashboard/dashboard.tsx b/plugins/main/public/components/overview/fim/dashboard/dashboard.tsx index 3372e61637..371bfb503a 100644 --- a/plugins/main/public/components/overview/fim/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/fim/dashboard/dashboard.tsx @@ -23,7 +23,7 @@ import { FIMDataSource, } from '../../../common/data-source'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context'; import { WzSearchBar } from '../../../common/search-bar'; @@ -90,10 +90,10 @@ const DashboardFIMComponent: React.FC = ({}) => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryInput={true} showQueryBar={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 - ? '' - : 'wz-no-display' - } - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 + ? '' + : 'wz-no-display' + } + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/gdpr/dashboards/dashboard.tsx b/plugins/main/public/components/overview/gdpr/dashboards/dashboard.tsx index 6444bf359e..382ecb0af6 100644 --- a/plugins/main/public/components/overview/gdpr/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/gdpr/dashboards/dashboard.tsx @@ -8,7 +8,7 @@ import useSearchBar from '../../../common/search-bar/use-search-bar'; import './styles.scss'; import { withErrorBoundary } from '../../../common/hocs'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { IndexPattern } from '../../../../../../../src/plugins/data/common'; import { ErrorFactory, @@ -91,10 +91,10 @@ const DashboardGDPRComponent: React.FC = () => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryInput={true} showQueryBar={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 ? '' : 'wz-no-display' - } - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 ? '' : 'wz-no-display' + } + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/github/dashboards/dashboard.tsx b/plugins/main/public/components/overview/github/dashboards/dashboard.tsx index c76a9d0b6a..393def651b 100644 --- a/plugins/main/public/components/overview/github/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/github/dashboards/dashboard.tsx @@ -8,7 +8,7 @@ import useSearchBar from '../../../common/search-bar/use-search-bar'; import './styles.scss'; import { withErrorBoundary } from '../../../common/hocs'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { IndexPattern } from '../../../../../../../src/plugins/data/common'; import { ErrorFactory, @@ -91,10 +91,10 @@ const DashboardGitHubComponent: React.FC = () => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryInput={true} showQueryBar={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} - {dataSource && results?.hits?.total > 0 ? ( -
0 ? '' : 'wz-no-display' - }`} - > - - -
- ) : null} - + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} + {dataSource && results?.hits?.total > 0 ? ( +
0 ? '' : 'wz-no-display' + }`} + > + + +
+ ) : null} + + )}
); diff --git a/plugins/main/public/components/overview/github/panel/github-panel.tsx b/plugins/main/public/components/overview/github/panel/github-panel.tsx index e228d36f76..200d9310f0 100644 --- a/plugins/main/public/components/overview/github/panel/github-panel.tsx +++ b/plugins/main/public/components/overview/github/panel/github-panel.tsx @@ -16,7 +16,7 @@ import { MainPanel } from '../../../common/modules/panel'; import { withErrorBoundary } from '../../../common/hocs'; import { CustomSearchBar } from '../../../common/custom-search-bar'; import useSearchBar from '../../../common/search-bar/use-search-bar'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { ModuleConfiguration } from './views'; import { ModuleConfig, filtersValues } from './config'; import { @@ -47,14 +47,13 @@ export const GitHubPanel = withErrorBoundary(() => { isLoading: isDataSourceLoading, fetchData, setFilters, - filterManager + filterManager, } = useDataSource({ DataSource: GitHubDataSource, repository: new AlertsDataSourceRepository(), - fetchFilters: [...selectedPanelFilter] + fetchFilters: [...selectedPanelFilter], }); - const { searchBarProps } = useSearchBar({ indexPattern: dataSource?.indexPattern as IndexPattern, filters, @@ -68,43 +67,48 @@ export const GitHubPanel = withErrorBoundary(() => { const { field, value } = selectedFilter; const controlledByFilter = 'github-panel-row-filter'; if (value) { - const filter = filterManager?.createFilter(FILTER_OPERATOR.IS_ONE_OF, field, [value], controlledByFilter); + const filter = filterManager?.createFilter( + FILTER_OPERATOR.IS_ONE_OF, + field, + [value], + controlledByFilter, + ); setSelectedPanelFilter([filter]); } else { setSelectedPanelFilter([]); } setCurrentSelectedFilter(selectedFilter); - } + }; return ( <> {isDataSourceLoading ? ( - + ) : ( - <> - - } - onChangeView={handleChangeView} - dataSourceProps={{ - fetchData: fetchData, - fetchFilters: [...fetchFilters, ...selectedPanelFilter], - searchBarProps, - indexPattern: dataSource?.indexPattern, - }} - isLoading={isDataSourceLoading} - /> - - )} + <> + + } + onChangeView={handleChangeView} + dataSourceProps={{ + fetchData: fetchData, + fetchFilters: [...fetchFilters, ...selectedPanelFilter], + searchBarProps, + indexPattern: dataSource?.indexPattern, + }} + isLoading={isDataSourceLoading} + /> + + )} ); }); diff --git a/plugins/main/public/components/overview/google-cloud/dashboards/dashboard.tsx b/plugins/main/public/components/overview/google-cloud/dashboards/dashboard.tsx index 3d9c37b558..1566f0d965 100644 --- a/plugins/main/public/components/overview/google-cloud/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/google-cloud/dashboards/dashboard.tsx @@ -22,7 +22,7 @@ import { } from '../../../common/data-source'; import { GoogleCloudDataSource } from '../../../common/data-source/pattern/alerts/google-cloud/google-cloud-data-source'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context'; import { WzSearchBar } from '../../../common/search-bar'; @@ -89,10 +89,10 @@ const DashboardGoogleCloudComponent: React.FC = () => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryBar={true} showSaveQuery={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 ? '' : 'wz-no-display' - } - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 ? '' : 'wz-no-display' + } + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/hipaa/dashboards/dashboard.tsx b/plugins/main/public/components/overview/hipaa/dashboards/dashboard.tsx index e7c7cc70b2..812b515537 100644 --- a/plugins/main/public/components/overview/hipaa/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/hipaa/dashboards/dashboard.tsx @@ -8,7 +8,7 @@ import useSearchBar from '../../../common/search-bar/use-search-bar'; import './styles.scss'; import { withErrorBoundary } from '../../../common/hocs'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { IndexPattern } from '../../../../../../../src/plugins/data/common'; import { ErrorFactory, @@ -91,10 +91,10 @@ const DashboardHIPAAComponent: React.FC = () => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryInput={true} showQueryBar={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 ? '' : 'wz-no-display' - } - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 ? '' : 'wz-no-display' + } + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/malware-detection/dashboard/dashboard.tsx b/plugins/main/public/components/overview/malware-detection/dashboard/dashboard.tsx index a1d19c5710..8d5b4b12df 100644 --- a/plugins/main/public/components/overview/malware-detection/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/malware-detection/dashboard/dashboard.tsx @@ -23,7 +23,7 @@ import { MalwareDetectionDataSource, } from '../../../common/data-source'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context'; import { WzSearchBar } from '../../../common/search-bar'; @@ -90,10 +90,10 @@ const DashboardMalwareDetectionComponent: React.FC = ({}) => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryInput={true} showQueryBar={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 - ? '' - : 'wz-no-display' - } - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 + ? '' + : 'wz-no-display' + } + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/mitre/dashboard/dashboard.tsx b/plugins/main/public/components/overview/mitre/dashboard/dashboard.tsx index 486563af8f..deaf0f5056 100644 --- a/plugins/main/public/components/overview/mitre/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/mitre/dashboard/dashboard.tsx @@ -12,7 +12,7 @@ import { HttpError, } from '../../../../react-services/error-management'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { SearchResponse } from '../../../../../../../src/core/server'; import './mitre_dashboard_filters.scss'; import { @@ -88,10 +88,10 @@ export const DashboardMITRE: React.FC = () => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryBar={true} showSaveQuery={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 ? '' : 'wz-no-display' - }`} - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 ? '' : 'wz-no-display' + }`} + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/mitre/framework/mitre.tsx b/plugins/main/public/components/overview/mitre/framework/mitre.tsx index 4d6e0be20b..9a37658387 100644 --- a/plugins/main/public/components/overview/mitre/framework/mitre.tsx +++ b/plugins/main/public/components/overview/mitre/framework/mitre.tsx @@ -23,7 +23,7 @@ import { UI_LOGGER_LEVELS } from '../../../../../common/constants'; import { UI_ERROR_SEVERITIES } from '../../../../react-services/error-orchestrator/types'; import { getErrorOrchestrator } from '../../../../react-services/common-services'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import useSearchBar from '../../../common/search-bar/use-search-bar'; import { useDataSource, @@ -147,16 +147,16 @@ const MitreComponent = props => { return ( - <> - - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> + { showQueryBar={true} showSaveQuery={true} /> - )} - - - - - - - - - onSelectedTabChanged(id)} - tacticsObject={mitreState.tacticsObject} - selectedTactics={mitreState.selectedTactics} - fetchData={fetchData} - isLoading={isLoading} - /> - - - - + + + + + + + + onSelectedTabChanged(id)} + tacticsObject={mitreState.tacticsObject} + selectedTactics={mitreState.selectedTactics} + fetchData={fetchData} + isLoading={isLoading} + /> + + + + + + )} ); }; diff --git a/plugins/main/public/components/overview/nist/dashboards/dashboard.tsx b/plugins/main/public/components/overview/nist/dashboards/dashboard.tsx index 39cd2623fa..017efa65f3 100644 --- a/plugins/main/public/components/overview/nist/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/nist/dashboards/dashboard.tsx @@ -8,7 +8,7 @@ import useSearchBar from '../../../common/search-bar/use-search-bar'; import './styles.scss'; import { withErrorBoundary } from '../../../common/hocs'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { IndexPattern } from '../../../../../../../src/plugins/data/common'; import { ErrorFactory, @@ -91,10 +91,10 @@ const DashboardNIST80053Component: React.FC = () => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryInput={true} showQueryBar={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 ? '' : 'wz-no-display' - } - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 ? '' : 'wz-no-display' + } + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/office/dashboard/dashboard.tsx b/plugins/main/public/components/overview/office/dashboard/dashboard.tsx index f985e7628e..b3b48d1e5b 100644 --- a/plugins/main/public/components/overview/office/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/office/dashboard/dashboard.tsx @@ -23,7 +23,7 @@ import { } from '../../../common/data-source'; import { Office365DataSource } from '../../../common/data-source/pattern/alerts/office-365/office-365-data-source'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context'; import { WzSearchBar } from '../../../common/search-bar'; @@ -90,10 +90,10 @@ const DashboardOffice365Component: React.FC = () => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryBar={true} showSaveQuery={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} -
0 ? '' : 'wz-no-display' - }`} - > - - - -
- +
0 ? '' : 'wz-no-display' + }`} + > + + + +
+ + )}
); diff --git a/plugins/main/public/components/overview/office/panel/office-panel.tsx b/plugins/main/public/components/overview/office/panel/office-panel.tsx index 3c234e3b52..7d908087d7 100644 --- a/plugins/main/public/components/overview/office/panel/office-panel.tsx +++ b/plugins/main/public/components/overview/office/panel/office-panel.tsx @@ -16,7 +16,7 @@ import { MainPanel } from '../../../common/modules/panel'; import { withErrorBoundary } from '../../../common/hocs'; import { CustomSearchBar } from '../../../common/custom-search-bar'; import useSearchBar from '../../../common/search-bar/use-search-bar'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { ModuleConfiguration } from './views'; import { ModuleConfig, filtersValues } from './config'; import { @@ -47,7 +47,7 @@ export const OfficePanel = withErrorBoundary(() => { isLoading: isDataSourceLoading, fetchData, setFilters, - filterManager + filterManager, } = useDataSource({ DataSource: Office365DataSource, repository: new AlertsDataSourceRepository(), @@ -67,19 +67,24 @@ export const OfficePanel = withErrorBoundary(() => { const { field, value } = selectedFilter; const controlledByFilter = 'office-panel-row-filter'; if (value) { - const filter = filterManager?.createFilter(FILTER_OPERATOR.IS_ONE_OF, field, [value], controlledByFilter); + const filter = filterManager?.createFilter( + FILTER_OPERATOR.IS_ONE_OF, + field, + [value], + controlledByFilter, + ); // this hide the remove filter button in the filter bar setSelectedPanelFilter([filter]); } else { setSelectedPanelFilter([]); } setCurrentSelectedFilter(selectedFilter); - } + }; return ( <> {isDataSourceLoading ? ( - + ) : ( <> { ); }); - diff --git a/plugins/main/public/components/overview/pci/dashboards/dashboard.tsx b/plugins/main/public/components/overview/pci/dashboards/dashboard.tsx index 9e8efad4b2..64305d0b5c 100644 --- a/plugins/main/public/components/overview/pci/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/pci/dashboards/dashboard.tsx @@ -23,7 +23,7 @@ import { } from '../../../common/data-source'; import { PCIDSSDataSource } from '../../../common/data-source/pattern/alerts/pci-dss/pci-dss-data-source'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context'; import { WzSearchBar } from '../../../common/search-bar'; @@ -92,10 +92,10 @@ const DashboardPCIDSSComponent: React.FC = () => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryInput={true} showQueryBar={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 ? '' : 'wz-no-display' - } - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 ? '' : 'wz-no-display' + } + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboardTabsPanels.tsx b/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboardTabsPanels.tsx index 4b40984946..b261d69619 100644 --- a/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboardTabsPanels.tsx +++ b/plugins/main/public/components/overview/server-management-statistics/dashboards/dashboardTabsPanels.tsx @@ -22,7 +22,7 @@ import { StatisticsDataSource, StatisticsDataSourceRepository, } from '../../../common/data-source/pattern/statistics'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { ErrorFactory, ErrorHandler, @@ -131,7 +131,7 @@ export const DashboardTabsPanels = ({ return ( <> {isDataSourceLoading && !dataSource ? ( - + ) : ( {!!(clusterNodes && clusterNodes.length && clusterNodeSelected) && ( diff --git a/plugins/main/public/components/overview/threat-hunting/dashboard/dashboard.tsx b/plugins/main/public/components/overview/threat-hunting/dashboard/dashboard.tsx index 2cda96f4fd..ba380ff059 100644 --- a/plugins/main/public/components/overview/threat-hunting/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/threat-hunting/dashboard/dashboard.tsx @@ -49,7 +49,7 @@ import { useDataSource, } from '../../../common/data-source'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { useReportingCommunicateSearchContext } from '../../../common/hooks/use-reporting-communicate-search-context'; import { wzDiscoverRenderColumns } from '../../../common/wazuh-discover/render-columns'; import { WzSearchBar } from '../../../common/search-bar'; @@ -207,10 +207,10 @@ const DashboardTH: React.FC = () => { return ( - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryBar={true} showSaveQuery={true} /> - )} - {!isDataSourceLoading && dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 - ? '' - : 'wz-no-display' - }`} - > - -
- - -
- {!isDataSourceLoading ? ( - - + ) : null} +
0 + ? '' + : 'wz-no-display' + }`} + > + +
+ + +
+ {!isDataSourceLoading ? ( + + + + ), + }} + /> + ) : null} +
+ {inspectedHit && ( + setInspectedHit(undefined)} size='m'> + + + + + + + - - ), - }} - /> - ) : null} + + + + + )}
- {inspectedHit && ( - setInspectedHit(undefined)} size='m'> - - - - - - - - - - - - )}
-
- + + )} ); }; diff --git a/plugins/main/public/components/overview/tsc/dashboards/dashboard.tsx b/plugins/main/public/components/overview/tsc/dashboards/dashboard.tsx index b4c77dfc1a..dfb3da8ee1 100644 --- a/plugins/main/public/components/overview/tsc/dashboards/dashboard.tsx +++ b/plugins/main/public/components/overview/tsc/dashboards/dashboard.tsx @@ -8,7 +8,7 @@ import useSearchBar from '../../../common/search-bar/use-search-bar'; import './styles.scss'; import { withErrorBoundary } from '../../../common/hocs'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { IndexPattern } from '../../../../../../../src/plugins/data/common'; import { ErrorFactory, @@ -91,10 +91,10 @@ const DashboardTSCComponent: React.FC = () => { return ( <> - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryInput={true} showQueryBar={true} /> - )} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 ? '' : 'wz-no-display' - } - > - -
- + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 ? '' : 'wz-no-display' + } + > + +
+ +
-
- + + )} ); diff --git a/plugins/main/public/components/overview/virustotal/dashboard/dashboard.tsx b/plugins/main/public/components/overview/virustotal/dashboard/dashboard.tsx index f834d90d95..29cf0cff74 100644 --- a/plugins/main/public/components/overview/virustotal/dashboard/dashboard.tsx +++ b/plugins/main/public/components/overview/virustotal/dashboard/dashboard.tsx @@ -20,7 +20,7 @@ import { tParsedIndexPattern, useDataSource, } from '../../../common/data-source'; -import { LoadingSpinner } from '../../../common/loading-spinner/loading-spinner'; +import { LoadingSearchbarProgress } from '../../../common/loading-searchbar-progress/loading-searchbar-progress'; import { DiscoverNoResults } from '../../../common/no-results/no-results'; import { VirusTotalDataSource } from '../../../common/data-source/pattern/alerts/virustotal/virustotal-data-source'; import './virustotal_dashboard.scss'; @@ -91,10 +91,10 @@ const DashboardVT: React.FC = () => { return ( - <> - {isDataSourceLoading && !dataSource ? ( - - ) : ( + {isDataSourceLoading && !dataSource ? ( + + ) : ( + <> { showQueryBar={true} showSaveQuery={true} /> - )} - {!isDataSourceLoading && dataSource && results?.hits?.total > 0 ? ( - - ) : null} - {dataSource && results?.hits?.total === 0 ? ( - - ) : null} -
0 - ? '' - : 'wz-no-display' - }`} - > - - -
- + {!isDataSourceLoading && dataSource && results?.hits?.total > 0 ? ( + + ) : null} + {dataSource && results?.hits?.total === 0 ? ( + + ) : null} +
0 + ? '' + : 'wz-no-display' + }`} + > + + +
+ + )}
); }; diff --git a/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.scss b/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.scss deleted file mode 100644 index 051ab642c1..0000000000 --- a/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.scss +++ /dev/null @@ -1,4 +0,0 @@ -.discoverNoResults { - display: flex; - align-items: center; -} diff --git a/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.tsx b/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.tsx deleted file mode 100644 index 7f505e6167..0000000000 --- a/plugins/main/public/components/overview/vulnerabilities/common/components/loading_spinner.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import './loading_spinner.scss'; -import React from 'react'; -import { EuiTitle, EuiPanel, EuiEmptyPrompt, EuiLoadingSpinner } from '@elastic/eui'; -import { FormattedMessage } from '@osd/i18n/react'; - -export function LoadingSpinner() { - return ( - - } - title={ - -

- -

-
- } - /> -
- ); -} diff --git a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx index 377438dc3e..ecef54dd3a 100644 --- a/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx +++ b/plugins/main/public/components/overview/vulnerabilities/dashboards/inventory/inventory.tsx @@ -30,7 +30,7 @@ import { getAllCustomRenders, } from '../../../../common/data-grid/data-grid-service'; import { DiscoverNoResults } from '../../common/components/no_results'; -import { LoadingSpinner } from '../../common/components/loading_spinner'; +import { LoadingSearchbarProgress } from '../../../../../../public/components/common/loading-searchbar-progress/loading-searchbar-progress'; // common components/hooks import useSearchBar from '../../../../common/search-bar/use-search-bar'; import { useDataGrid } from '../../../../common/data-grid/use-data-grid'; @@ -185,7 +185,7 @@ const InventoryVulsComponent = () => { > <> {isDataSourceLoading ? ( - + ) : ( = ({ <> {isDataSourceLoading && !dataSource ? ( - + ) : ( ), () => ( -