diff --git a/.github/workflows/react.yml b/.github/workflows/react.yml index cf20772f4b..2efa93e818 100644 --- a/.github/workflows/react.yml +++ b/.github/workflows/react.yml @@ -29,5 +29,5 @@ jobs: restore-keys: | ${{ runner.os }}-node- - - run: CI=false make check-react-app + - run: make check-react-app - run: make react-app-test diff --git a/pkg/ui/react-app/src/components/ListTree.tsx b/pkg/ui/react-app/src/components/ListTree.tsx index 4a47278b8c..5959700d8c 100644 --- a/pkg/ui/react-app/src/components/ListTree.tsx +++ b/pkg/ui/react-app/src/components/ListTree.tsx @@ -27,7 +27,7 @@ const ListTree: React.FC = ({ id, node }) => { }; // Constructing List Items recursively. - const mapper = (nodes: QueryTree[], parentId?: any, lvl?: any) => { + const mapper = (nodes: QueryTree[], parentId?: string, lvl?: number) => { return nodes.map((node: QueryTree, index: number) => { const id = `${index}-${parentId ? parentId : 'top'}`.replace(/[^a-zA-Z0-9-_]/g, ''); const item = ( @@ -36,7 +36,7 @@ const ListTree: React.FC = ({ id, node }) => { className={`bg-transparent p-0 border-0 ${parentId ? `rounded-0 ${lvl ? 'border-bottom-0' : ''}` : ''}`} > { -
+
{node.children && (
{mapping[id] ? '\u002B' : '\u002D'} diff --git a/pkg/ui/react-app/src/components/withStatusIndicator.tsx b/pkg/ui/react-app/src/components/withStatusIndicator.tsx index b640b927a3..8a9d035328 100644 --- a/pkg/ui/react-app/src/components/withStatusIndicator.tsx +++ b/pkg/ui/react-app/src/components/withStatusIndicator.tsx @@ -11,7 +11,9 @@ interface StatusIndicatorProps { } export const withStatusIndicator = - (Component: ComponentType): FC => + >( + Component: ComponentType + ): FC, keyof StatusIndicatorProps> & StatusIndicatorProps> => ({ error, isLoading, customErrorMsg, componentTitle, ...rest }: StatusIndicatorProps) => { if (error) { return ( @@ -38,5 +40,6 @@ export const withStatusIndicator = /> ); } - return ; + + return ; }; diff --git a/pkg/ui/react-app/src/hooks/useFetch.ts b/pkg/ui/react-app/src/hooks/useFetch.ts index f271190a3f..3ba46d6ff4 100644 --- a/pkg/ui/react-app/src/hooks/useFetch.ts +++ b/pkg/ui/react-app/src/hooks/useFetch.ts @@ -9,7 +9,7 @@ export interface FetchState { } export const useFetch = (url: string, options?: RequestInit): FetchState => { - const [response, setResponse] = useState>({ status: 'start fetching' } as any); + const [response, setResponse] = useState>({ status: 'start fetching' } as APIResponse); const [error, setError] = useState(); const [isLoading, setIsLoading] = useState(true); diff --git a/pkg/ui/react-app/src/pages/alerts/AlertContents.tsx b/pkg/ui/react-app/src/pages/alerts/AlertContents.tsx index 58a088037c..159661f47d 100644 --- a/pkg/ui/react-app/src/pages/alerts/AlertContents.tsx +++ b/pkg/ui/react-app/src/pages/alerts/AlertContents.tsx @@ -8,7 +8,7 @@ import { KVSearch } from '@nexucis/kvsearch'; import CustomInfiniteScroll, { InfiniteScrollItemsProps } from '../../components/CustomInfiniteScroll'; import SearchBar from '../../components/SearchBar'; -export type RuleState = keyof RuleStatus; +export type RuleState = keyof RuleStatus; export interface RuleStatus { firing: T; @@ -160,7 +160,7 @@ interface GroupInfoProps { } export const GroupInfo: FC = ({ rules, children }) => { - const statesCounter = rules.reduce( + const statesCounter = rules.reduce>( (acc, r) => { return { ...acc, @@ -170,6 +170,7 @@ export const GroupInfo: FC = ({ rules, children }) => { { firing: 0, pending: 0, + inactive: 0, } ); diff --git a/pkg/ui/react-app/src/pages/config/Config.tsx b/pkg/ui/react-app/src/pages/config/Config.tsx index 33c2fb4d92..8df90ea133 100644 --- a/pkg/ui/react-app/src/pages/config/Config.tsx +++ b/pkg/ui/react-app/src/pages/config/Config.tsx @@ -27,7 +27,7 @@ export const ConfigContent: FC = ({ error, data }) => {

Configuration  { setCopied(result); setTimeout(setCopied, 1500); diff --git a/pkg/ui/react-app/src/pages/graph/CMTheme.tsx b/pkg/ui/react-app/src/pages/graph/CMTheme.tsx index 0c7a703ce3..6051ccc192 100644 --- a/pkg/ui/react-app/src/pages/graph/CMTheme.tsx +++ b/pkg/ui/react-app/src/pages/graph/CMTheme.tsx @@ -12,7 +12,7 @@ export const baseTheme = EditorView.theme({ '&': { '&.cm-focused': { outline: 'none', - outline_fallback: 'none', + outlineFallback: 'none', }, }, '.cm-scroller': { diff --git a/pkg/ui/react-app/src/pages/graph/GraphControls.tsx b/pkg/ui/react-app/src/pages/graph/GraphControls.tsx index e9001fd68a..3d7d6a4dbf 100644 --- a/pkg/ui/react-app/src/pages/graph/GraphControls.tsx +++ b/pkg/ui/react-app/src/pages/graph/GraphControls.tsx @@ -63,7 +63,9 @@ class GraphControls extends Component { }; changeRangeInput = (range: number): void => { - this.rangeRef.current!.value = formatDuration(range); + if (this.rangeRef.current) { + this.rangeRef.current.value = formatDuration(this.props.range); + } }; increaseRange = (): void => { @@ -91,7 +93,9 @@ class GraphControls extends Component { this.changeRangeInput(this.props.range); } if (prevProps.resolution !== this.props.resolution) { - this.resolutionRef.current!.value = this.props.resolution !== null ? this.props.resolution.toString() : ''; + if (this.resolutionRef.current) { + this.resolutionRef.current.value = this.props.resolution !== null ? this.props.resolution.toString() : ''; + } } } @@ -112,7 +116,11 @@ class GraphControls extends Component { this.onChangeRangeInput(this.rangeRef.current!.value)} + onBlur={() => { + if (this.rangeRef.current) { + this.onChangeRangeInput(this.rangeRef.current.value); + } + }} /> @@ -136,7 +144,10 @@ class GraphControls extends Component { defaultValue={this.props.resolution !== null ? this.props.resolution.toString() : ''} innerRef={this.resolutionRef} onBlur={() => { - const res = parseInt(this.resolutionRef.current!.value); + if (!this.resolutionRef.current) { + return; + } + const res = parseInt(this.resolutionRef.current.value); this.props.onChangeResolution(res ? res : null); }} bsSize="sm" diff --git a/pkg/ui/react-app/src/pages/graph/GraphHelpers.ts b/pkg/ui/react-app/src/pages/graph/GraphHelpers.ts index c13f1f6246..3e52877087 100644 --- a/pkg/ui/react-app/src/pages/graph/GraphHelpers.ts +++ b/pkg/ui/react-app/src/pages/graph/GraphHelpers.ts @@ -137,7 +137,8 @@ export const getOptions = (stacked: boolean, useLocalTime: boolean): jquery.flot }; export const normalizeData = ({ queryParams, data }: GraphProps): GraphSeries[] => { - const { startTime, endTime, resolution } = queryParams!; + // use default values + const { startTime = 0, endTime = 0, resolution = 0 } = queryParams || {}; return data.result.map(({ values, histograms, metric }, index) => { // Insert nulls for all missing steps. const data = []; diff --git a/pkg/ui/react-app/src/pages/graph/Panel.tsx b/pkg/ui/react-app/src/pages/graph/Panel.tsx index 32b0721ea2..8fb5bc70ea 100644 --- a/pkg/ui/react-app/src/pages/graph/Panel.tsx +++ b/pkg/ui/react-app/src/pages/graph/Panel.tsx @@ -205,7 +205,7 @@ class Panel extends Component { const params: URLSearchParams = new URLSearchParams({ query: expr, dedup: this.props.options.useDeduplication.toString(), - partial_response: this.props.options.usePartialResponse.toString(), + partialResponse: this.props.options.usePartialResponse.toString(), }); // Add storeMatches to query params. @@ -414,7 +414,7 @@ class Panel extends Component { const params: URLSearchParams = new URLSearchParams({ query: this.state.exprInputValue, dedup: this.props.options.useDeduplication.toString(), - partial_response: this.props.options.usePartialResponse.toString(), + partialResponse: this.props.options.usePartialResponse.toString(), }); // Add storeMatches to query params. diff --git a/pkg/ui/react-app/src/pages/graph/SeriesName.tsx b/pkg/ui/react-app/src/pages/graph/SeriesName.tsx index 0fb8bd285b..d582986ef7 100644 --- a/pkg/ui/react-app/src/pages/graph/SeriesName.tsx +++ b/pkg/ui/react-app/src/pages/graph/SeriesName.tsx @@ -27,9 +27,13 @@ const SeriesName: FC = ({ labels, format }) => { } } + if (labels === null) { + return <>scalar; + } + return (
- {labels!.__name__ || ''} + {labels.__name__ || ''} {'{'} {labelNodes} {'}'} @@ -37,16 +41,12 @@ const SeriesName: FC = ({ labels, format }) => { ); }; - if (labels === null) { - return <>scalar; - } - if (format) { return renderFormatted(); } // Return a simple text node. This is much faster to scroll through // for longer lists (hundreds of items). - return <>{metricToSeriesName(labels!)}; + return <>{labels ? metricToSeriesName(labels) : null}; }; export default SeriesName; diff --git a/pkg/ui/react-app/src/pages/graph/TimeInput.tsx b/pkg/ui/react-app/src/pages/graph/TimeInput.tsx index d938280589..c2bdaecd18 100644 --- a/pkg/ui/react-app/src/pages/graph/TimeInput.tsx +++ b/pkg/ui/react-app/src/pages/graph/TimeInput.tsx @@ -60,8 +60,9 @@ class TimeInput extends Component { }; componentDidMount(): void { - this.$time = $(this.timeInputRef.current!); - + if (this.timeInputRef.current) { + this.$time = $(this.timeInputRef.current); + } this.$time.datetimepicker({ icons: { today: 'fas fa-calendar-check', diff --git a/pkg/ui/react-app/src/pages/status/Status.tsx b/pkg/ui/react-app/src/pages/status/Status.tsx index 38fef7c8fb..7436f01a6d 100644 --- a/pkg/ui/react-app/src/pages/status/Status.tsx +++ b/pkg/ui/react-app/src/pages/status/Status.tsx @@ -10,9 +10,19 @@ interface StatusPageProps { title: string; } +type CustomizeValue = + | ((v: string, key: string) => React.ReactNode) + | ((v: boolean, key: string) => React.ReactNode) + | ((v: { url: string }[], key: string) => React.ReactNode); + export const statusConfig: Record< string, - { title?: string; customizeValue?: (v: any, key: string) => any; customRow?: boolean; skip?: boolean } + { + title?: string; + customizeValue?: CustomizeValue; + customRow?: boolean; + skip?: boolean; + } > = { startTime: { title: 'Start time', customizeValue: (v: string) => new Date(v).toUTCString() }, CWD: { title: 'Working directory' }, @@ -28,7 +38,7 @@ export const statusConfig: Record< storageRetention: { title: 'Storage retention' }, activeAlertmanagers: { customRow: true, - customizeValue: (alertMgrs: { url: string }[], key) => { + customizeValue: (alertMgrs: { url: string }[], key: string) => { return ( @@ -59,19 +69,19 @@ export const StatusContent: FC = ({ data, title }) => { {Object.entries(data).map(([k, v]) => { - const { title = k, customizeValue = (val: any) => val, customRow, skip } = statusConfig[k] || {}; + const { title = k, customizeValue = (val: string) => val, customRow, skip } = statusConfig[k] || {}; if (skip) { return null; } if (customRow) { - return customizeValue(v, k); + return customizeValue(v as never, k); } return ( - + ); })} diff --git a/pkg/ui/react-app/src/pages/targets/Targets.tsx b/pkg/ui/react-app/src/pages/targets/Targets.tsx index 9012002b12..8023a11b39 100644 --- a/pkg/ui/react-app/src/pages/targets/Targets.tsx +++ b/pkg/ui/react-app/src/pages/targets/Targets.tsx @@ -1,9 +1,7 @@ import React, { FC } from 'react'; import { RouteComponentProps } from '@reach/router'; -import Filter from './Filter'; import ScrapePoolList from './ScrapePoolList'; import PathPrefixProps from '../../types/PathPrefixProps'; -import { useLocalStorage } from '../../hooks/useLocalStorage'; const Targets: FC = ({ pathPrefix }) => { const scrapePoolListProps = { pathPrefix }; diff --git a/pkg/ui/react-app/src/thanos/pages/blocks/helpers.ts b/pkg/ui/react-app/src/thanos/pages/blocks/helpers.ts index 3320cc71a7..6e887e165a 100644 --- a/pkg/ui/react-app/src/thanos/pages/blocks/helpers.ts +++ b/pkg/ui/react-app/src/thanos/pages/blocks/helpers.ts @@ -152,7 +152,7 @@ export const getFilteredBlockPools = ( filteredBlocks: Block[] ): { [source: string]: BlocksPool } => { const newblockPools: { [source: string]: BlocksPool } = {}; - Object.keys(blockPools).map((key: string) => { + Object.keys(blockPools).forEach((key: string) => { const poolArrayIndex = blockPools[key]; const poolArray = poolArrayIndex[Object.keys(poolArrayIndex)[0]]; for (let i = 0; i < filteredBlocks.length; i++) { diff --git a/pkg/ui/react-app/src/thanos/pages/errorBoundary/ErrorBoundary.tsx b/pkg/ui/react-app/src/thanos/pages/errorBoundary/ErrorBoundary.tsx index 467dcf537e..56c14d4548 100644 --- a/pkg/ui/react-app/src/thanos/pages/errorBoundary/ErrorBoundary.tsx +++ b/pkg/ui/react-app/src/thanos/pages/errorBoundary/ErrorBoundary.tsx @@ -1,14 +1,18 @@ -import React, { ErrorInfo } from 'react'; +import React, { ErrorInfo, ReactNode } from 'react'; import { Container, UncontrolledCollapse, Button } from 'reactstrap'; import styles from './ErrorBoundary.module.css'; +interface ErrorBoundaryProps { + children: ReactNode; +} + interface ErrorState { error: Error | null; errorInfo: ErrorInfo | null; } -class ErrorBoundary extends React.Component { - constructor(props: any) { +class ErrorBoundary extends React.Component { + constructor(props: ErrorBoundaryProps) { super(props); this.state = { error: null, @@ -22,6 +26,7 @@ class ErrorBoundary extends React.Component { errorInfo, }); } + render(): React.ReactNode { if (this.state.errorInfo) { return (
{title} {customizeValue(v, title)}{customizeValue(v as never, title)}