mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[Logs + Metrics UI] Remove eslint exceptions (#50979)
This removes the two eslint exceptions specific to the `infra` plugin introduced in #49244. fixes #49563
This commit is contained in:
parent
9fcc93457f
commit
0cd62cabbb
41 changed files with 269 additions and 231 deletions
|
@ -170,13 +170,6 @@ module.exports = {
|
|||
'react-hooks/rules-of-hooks': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['x-pack/legacy/plugins/infra/**/*.{js,ts,tsx}'],
|
||||
rules: {
|
||||
'react-hooks/exhaustive-deps': 'off',
|
||||
'react-hooks/rules-of-hooks': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['x-pack/legacy/plugins/lens/**/*.{js,ts,tsx}'],
|
||||
rules: {
|
||||
|
|
|
@ -37,7 +37,6 @@ export const useFormattedTime = (
|
|||
|
||||
const dateFormat = formatMap[format];
|
||||
const formattedTime = useMemo(() => getFormattedTime(time, dateFormat, fallbackFormat), [
|
||||
getFormattedTime,
|
||||
time,
|
||||
dateFormat,
|
||||
fallbackFormat,
|
||||
|
|
|
@ -51,7 +51,7 @@ export const LogEntryActionsMenu: React.FunctionComponent<{
|
|||
/>
|
||||
</EuiContextMenuItem>,
|
||||
],
|
||||
[uptimeLink]
|
||||
[apmLink, uptimeLink]
|
||||
);
|
||||
|
||||
const hasMenuItems = useMemo(() => menuItems.length > 0, [menuItems]);
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { debounce } from 'lodash';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import euiStyled from '../../../../../common/eui_styled_components';
|
||||
import { useVisibilityState } from '../../utils/use_visibility_state';
|
||||
|
@ -47,8 +47,25 @@ export const LogHighlightsMenu: React.FC<LogHighlightsMenuProps> = ({
|
|||
} = useVisibilityState(false);
|
||||
|
||||
// Input field state
|
||||
const [highlightTerm, setHighlightTerm] = useState('');
|
||||
const [highlightTerm, _setHighlightTerm] = useState('');
|
||||
|
||||
const debouncedOnChange = useMemo(() => debounce(onChange, 275), [onChange]);
|
||||
const setHighlightTerm = useCallback<typeof _setHighlightTerm>(
|
||||
valueOrUpdater =>
|
||||
_setHighlightTerm(previousHighlightTerm => {
|
||||
const newHighlightTerm =
|
||||
typeof valueOrUpdater === 'function'
|
||||
? valueOrUpdater(previousHighlightTerm)
|
||||
: valueOrUpdater;
|
||||
|
||||
if (newHighlightTerm !== previousHighlightTerm) {
|
||||
debouncedOnChange([newHighlightTerm]);
|
||||
}
|
||||
|
||||
return newHighlightTerm;
|
||||
}),
|
||||
[debouncedOnChange]
|
||||
);
|
||||
const changeHighlightTerm = useCallback(
|
||||
e => {
|
||||
const value = e.target.value;
|
||||
|
@ -57,9 +74,6 @@ export const LogHighlightsMenu: React.FC<LogHighlightsMenuProps> = ({
|
|||
[setHighlightTerm]
|
||||
);
|
||||
const clearHighlightTerm = useCallback(() => setHighlightTerm(''), [setHighlightTerm]);
|
||||
useEffect(() => {
|
||||
debouncedOnChange([highlightTerm]);
|
||||
}, [highlightTerm]);
|
||||
|
||||
const button = (
|
||||
<EuiButtonEmpty color="text" size="xs" iconType="brush" onClick={togglePopover}>
|
||||
|
|
|
@ -63,7 +63,7 @@ export const useMeasuredCharacterDimensions = (scale: TextScale) => {
|
|||
X
|
||||
</MonospaceCharacterDimensionsProbe>
|
||||
),
|
||||
[scale]
|
||||
[measureElement, scale]
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { EuiComboBox } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import React, { useCallback, useState, useEffect } from 'react';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { FieldType } from 'ui/index_patterns';
|
||||
import { colorTransformer, MetricsExplorerColor } from '../../../common/color_palette';
|
||||
import {
|
||||
|
@ -31,24 +31,19 @@ interface SelectedOption {
|
|||
|
||||
export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus = false }: Props) => {
|
||||
const colors = Object.keys(MetricsExplorerColor) as MetricsExplorerColor[];
|
||||
const [inputRef, setInputRef] = useState<HTMLInputElement | null>(null);
|
||||
const [focusOnce, setFocusState] = useState<boolean>(false);
|
||||
const [shouldFocus, setShouldFocus] = useState(autoFocus);
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef && autoFocus && !focusOnce) {
|
||||
inputRef.focus();
|
||||
setFocusState(true);
|
||||
// the EuiCombobox forwards the ref to an input element
|
||||
const autoFocusInputElement = useCallback(
|
||||
(inputElement: HTMLInputElement | null) => {
|
||||
if (inputElement && shouldFocus) {
|
||||
inputElement.focus();
|
||||
setShouldFocus(false);
|
||||
}
|
||||
}, [inputRef]);
|
||||
},
|
||||
[shouldFocus]
|
||||
);
|
||||
|
||||
// I tried to use useRef originally but the EUIComboBox component's type definition
|
||||
// would only accept an actual input element or a callback function (with the same type).
|
||||
// This effectivly does the same thing but is compatible with EuiComboBox.
|
||||
const handleInputRef = (ref: HTMLInputElement) => {
|
||||
if (ref) {
|
||||
setInputRef(ref);
|
||||
}
|
||||
};
|
||||
const handleChange = useCallback(
|
||||
selectedOptions => {
|
||||
onChange(
|
||||
|
@ -59,7 +54,7 @@ export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus =
|
|||
}))
|
||||
);
|
||||
},
|
||||
[options, onChange]
|
||||
[onChange, options.aggregation, colors]
|
||||
);
|
||||
|
||||
const comboOptions = fields
|
||||
|
@ -86,7 +81,7 @@ export const MetricsExplorerMetrics = ({ options, onChange, fields, autoFocus =
|
|||
selectedOptions={selectedOptions}
|
||||
onChange={handleChange}
|
||||
isClearable={true}
|
||||
inputRef={handleInputRef}
|
||||
inputRef={autoFocusInputElement}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -36,7 +36,7 @@ export const SavedViewCreateModal = ({ close, save, isInvalid }: Props) => {
|
|||
|
||||
const saveView = useCallback(() => {
|
||||
save(viewName, includeTime);
|
||||
}, [viewName, includeTime]);
|
||||
}, [includeTime, save, viewName]);
|
||||
|
||||
return (
|
||||
<EuiOverlayMask>
|
||||
|
|
|
@ -94,7 +94,7 @@ export const AddLogColumnButtonAndPopover: React.FunctionComponent<{
|
|||
|
||||
addLogColumn(selectedOption.columnConfiguration);
|
||||
},
|
||||
[addLogColumn, availableColumnOptions]
|
||||
[addLogColumn, availableColumnOptions, closePopover]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -52,7 +52,7 @@ export const useSourceConfigurationFormState = (configuration?: SourceConfigurat
|
|||
const resetForm = useCallback(() => {
|
||||
indicesConfigurationFormState.resetForm();
|
||||
logColumnsConfigurationFormState.resetForm();
|
||||
}, [indicesConfigurationFormState.resetForm, logColumnsConfigurationFormState.formState]);
|
||||
}, [indicesConfigurationFormState, logColumnsConfigurationFormState]);
|
||||
|
||||
const isFormDirty = useMemo(
|
||||
() => indicesConfigurationFormState.isFormDirty || logColumnsConfigurationFormState.isFormDirty,
|
||||
|
|
|
@ -17,28 +17,33 @@ import {
|
|||
} from '../../graphql/types';
|
||||
import { findInventoryModel } from '../../../common/inventory_models';
|
||||
|
||||
interface Props {
|
||||
interface WaffleInventorySwitcherProps {
|
||||
nodeType: InfraNodeType;
|
||||
changeNodeType: (nodeType: InfraNodeType) => void;
|
||||
changeGroupBy: (groupBy: InfraSnapshotGroupbyInput[]) => void;
|
||||
changeMetric: (metric: InfraSnapshotMetricInput) => void;
|
||||
}
|
||||
|
||||
export const WaffleInventorySwitcher = (props: Props) => {
|
||||
export const WaffleInventorySwitcher: React.FC<WaffleInventorySwitcherProps> = ({
|
||||
changeNodeType,
|
||||
changeGroupBy,
|
||||
changeMetric,
|
||||
nodeType,
|
||||
}) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const closePopover = useCallback(() => setIsOpen(false), []);
|
||||
const openPopover = useCallback(() => setIsOpen(true), []);
|
||||
const goToNodeType = useCallback(
|
||||
(nodeType: InfraNodeType) => {
|
||||
(targetNodeType: InfraNodeType) => {
|
||||
closePopover();
|
||||
props.changeNodeType(nodeType);
|
||||
props.changeGroupBy([]);
|
||||
const inventoryModel = findInventoryModel(nodeType);
|
||||
props.changeMetric({
|
||||
changeNodeType(targetNodeType);
|
||||
changeGroupBy([]);
|
||||
const inventoryModel = findInventoryModel(targetNodeType);
|
||||
changeMetric({
|
||||
type: inventoryModel.metrics.defaultSnapshot as InfraSnapshotMetricType,
|
||||
});
|
||||
},
|
||||
[props.changeGroupBy, props.changeNodeType, props.changeMetric]
|
||||
[closePopover, changeNodeType, changeGroupBy, changeMetric]
|
||||
);
|
||||
const goToHost = useCallback(() => goToNodeType('host' as InfraNodeType), [goToNodeType]);
|
||||
const goToK8 = useCallback(() => goToNodeType('pod' as InfraNodeType), [goToNodeType]);
|
||||
|
@ -68,10 +73,10 @@ export const WaffleInventorySwitcher = (props: Props) => {
|
|||
],
|
||||
},
|
||||
],
|
||||
[]
|
||||
[goToDocker, goToHost, goToK8]
|
||||
);
|
||||
const selectedText = useMemo(() => {
|
||||
switch (props.nodeType) {
|
||||
switch (nodeType) {
|
||||
case InfraNodeType.host:
|
||||
return i18n.translate('xpack.infra.waffle.nodeTypeSwitcher.hostsLabel', {
|
||||
defaultMessage: 'Hosts',
|
||||
|
@ -81,7 +86,7 @@ export const WaffleInventorySwitcher = (props: Props) => {
|
|||
case InfraNodeType.container:
|
||||
return 'Docker';
|
||||
}
|
||||
}, [props.nodeType]);
|
||||
}, [nodeType]);
|
||||
|
||||
return (
|
||||
<EuiFilterGroup>
|
||||
|
|
|
@ -46,7 +46,7 @@ export const useLogAnalysisCapabilities = () => {
|
|||
|
||||
useEffect(() => {
|
||||
fetchMlCapabilities();
|
||||
}, []);
|
||||
}, [fetchMlCapabilities]);
|
||||
|
||||
const isLoading = useMemo(() => fetchMlCapabilitiesRequest.state === 'pending', [
|
||||
fetchMlCapabilitiesRequest.state,
|
||||
|
|
|
@ -125,23 +125,23 @@ export const useLogAnalysisModule = <JobType extends string>({
|
|||
dispatchModuleStatus({ type: 'failedSetup' });
|
||||
});
|
||||
},
|
||||
[cleanUpModule, setUpModule]
|
||||
[cleanUpModule, dispatchModuleStatus, setUpModule]
|
||||
);
|
||||
|
||||
const viewSetupForReconfiguration = useCallback(() => {
|
||||
dispatchModuleStatus({ type: 'requestedJobConfigurationUpdate' });
|
||||
}, []);
|
||||
}, [dispatchModuleStatus]);
|
||||
|
||||
const viewSetupForUpdate = useCallback(() => {
|
||||
dispatchModuleStatus({ type: 'requestedJobDefinitionUpdate' });
|
||||
}, []);
|
||||
}, [dispatchModuleStatus]);
|
||||
|
||||
const viewResults = useCallback(() => {
|
||||
dispatchModuleStatus({ type: 'viewedResults' });
|
||||
}, []);
|
||||
}, [dispatchModuleStatus]);
|
||||
|
||||
const jobIds = useMemo(() => moduleDescriptor.getJobIds(spaceId, sourceId), [
|
||||
moduleDescriptor.getJobIds,
|
||||
moduleDescriptor,
|
||||
spaceId,
|
||||
sourceId,
|
||||
]);
|
||||
|
|
|
@ -140,7 +140,7 @@ export const useAnalysisSetupState = <JobType extends string>({
|
|||
? [...errors, ...index.errors]
|
||||
: errors;
|
||||
}, []);
|
||||
}, [selectedIndexNames, validatedIndices, validateIndicesRequest.state]);
|
||||
}, [isValidating, validateIndicesRequest.state, selectedIndexNames, validatedIndices]);
|
||||
|
||||
return {
|
||||
cleanupAndSetup,
|
||||
|
|
|
@ -78,7 +78,7 @@ export const useLogEntryHighlights = (
|
|||
} else {
|
||||
setLogEntryHighlights([]);
|
||||
}
|
||||
}, [highlightTerms, startKey, endKey, filterQuery, sourceVersion]);
|
||||
}, [endKey, filterQuery, highlightTerms, loadLogEntryHighlights, sourceVersion, startKey]);
|
||||
|
||||
const logEntryHighlightsById = useMemo(
|
||||
() =>
|
||||
|
|
|
@ -74,7 +74,15 @@ export const useLogSummaryHighlights = (
|
|||
} else {
|
||||
setLogSummaryHighlights([]);
|
||||
}
|
||||
}, [highlightTerms, start, end, bucketSize, filterQuery, sourceVersion]);
|
||||
}, [
|
||||
bucketSize,
|
||||
debouncedLoadSummaryHighlights,
|
||||
end,
|
||||
filterQuery,
|
||||
highlightTerms,
|
||||
sourceVersion,
|
||||
start,
|
||||
]);
|
||||
|
||||
return {
|
||||
logSummaryHighlights,
|
||||
|
|
|
@ -53,7 +53,7 @@ export const useNextAndPrevious = ({
|
|||
const initialTimeKey = getUniqueLogEntryKey(entries[initialIndex]);
|
||||
setCurrentTimeKey(initialTimeKey);
|
||||
}
|
||||
}, [currentTimeKey, entries, setCurrentTimeKey]);
|
||||
}, [currentTimeKey, entries, setCurrentTimeKey, visibleMidpoint]);
|
||||
|
||||
const indexOfCurrentTimeKey = useMemo(() => {
|
||||
if (currentTimeKey && entries.length > 0) {
|
||||
|
|
|
@ -25,11 +25,11 @@ export const LogHighlightsPositionBridge = withLogPosition(
|
|||
const { setJumpToTarget, setVisibleMidpoint } = useContext(LogHighlightsState.Context);
|
||||
useEffect(() => {
|
||||
setVisibleMidpoint(visibleMidpoint);
|
||||
}, [visibleMidpoint]);
|
||||
}, [setVisibleMidpoint, visibleMidpoint]);
|
||||
|
||||
useEffect(() => {
|
||||
setJumpToTarget(() => jumpToTargetPosition);
|
||||
}, [jumpToTargetPosition]);
|
||||
}, [jumpToTargetPosition, setJumpToTarget]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ export const LogHighlightsFilterQueryBridge = withLogFilter(
|
|||
|
||||
useEffect(() => {
|
||||
setFilterQuery(serializedFilterQuery);
|
||||
}, [serializedFilterQuery]);
|
||||
}, [serializedFilterQuery, setFilterQuery]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ export const WithStreamItems: React.FunctionComponent<{
|
|||
createLogEntryStreamItem(logEntry, logEntryHighlightsById[logEntry.gid] || [])
|
||||
),
|
||||
|
||||
[logEntries.entries, logEntryHighlightsById]
|
||||
[isAutoReloading, logEntries.entries, logEntries.isReloading, logEntryHighlightsById]
|
||||
);
|
||||
|
||||
return children({
|
||||
|
|
|
@ -96,6 +96,9 @@ export function useMetricsExplorerData(
|
|||
}
|
||||
setLoading(false);
|
||||
})();
|
||||
|
||||
// TODO: fix this dependency list while preserving the semantics
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [options, source, timerange, signal, afterKey]);
|
||||
return { error, loading, data };
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ function useStateWithLocalStorage<State>(
|
|||
const [state, setState] = useState<State>(parseJsonOrDefault<State>(storageState, defaultState));
|
||||
useEffect(() => {
|
||||
localStorage.setItem(key, JSON.stringify(state));
|
||||
}, [state]);
|
||||
}, [key, state]);
|
||||
return [state, setState];
|
||||
}
|
||||
|
||||
|
|
|
@ -26,12 +26,13 @@ export const useSavedView = <ViewState>(defaultViewState: ViewState, viewType: s
|
|||
>(viewType);
|
||||
const { create, error: errorOnCreate, createdId } = useCreateSavedObject(viewType);
|
||||
const { deleteObject, deletedId } = useDeleteSavedObject(viewType);
|
||||
const deleteView = useCallback((id: string) => deleteObject(id), []);
|
||||
const deleteView = useCallback((id: string) => deleteObject(id), [deleteObject]);
|
||||
const [createError, setCreateError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => setCreateError(createError), [errorOnCreate, setCreateError]);
|
||||
useEffect(() => setCreateError(errorOnCreate), [errorOnCreate]);
|
||||
|
||||
const saveView = useCallback((d: { [p: string]: any }) => {
|
||||
const saveView = useCallback(
|
||||
(d: { [p: string]: any }) => {
|
||||
const doSave = async () => {
|
||||
const exists = await hasView(d.name);
|
||||
if (exists) {
|
||||
|
@ -46,9 +47,11 @@ export const useSavedView = <ViewState>(defaultViewState: ViewState, viewType: s
|
|||
};
|
||||
setCreateError(null);
|
||||
doSave();
|
||||
}, []);
|
||||
},
|
||||
[create, hasView]
|
||||
);
|
||||
|
||||
const savedObjects = data ? data.savedObjects : [];
|
||||
const savedObjects = useMemo(() => (data ? data.savedObjects : []), [data]);
|
||||
const views = useMemo(() => {
|
||||
const items: Array<SavedView<ViewState>> = [
|
||||
{
|
||||
|
@ -61,8 +64,7 @@ export const useSavedView = <ViewState>(defaultViewState: ViewState, viewType: s
|
|||
},
|
||||
];
|
||||
|
||||
if (data) {
|
||||
data.savedObjects.forEach(
|
||||
savedObjects.forEach(
|
||||
o =>
|
||||
o.type === viewType &&
|
||||
items.push({
|
||||
|
@ -70,10 +72,9 @@ export const useSavedView = <ViewState>(defaultViewState: ViewState, viewType: s
|
|||
id: o.id,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return items;
|
||||
}, [savedObjects, defaultViewState]);
|
||||
}, [defaultViewState, savedObjects, viewType]);
|
||||
|
||||
return {
|
||||
views,
|
||||
|
|
|
@ -57,6 +57,9 @@ export function useTrackMetric(
|
|||
const trackUiMetric = getTrackerForApp(app);
|
||||
const id = setTimeout(() => trackUiMetric(metricType, decoratedMetric), Math.max(delay, 0));
|
||||
return () => clearTimeout(id);
|
||||
|
||||
// the dependencies are managed externally
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, effectDependencies);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import { MetricsExplorerPage } from './metrics_explorer';
|
|||
import { SnapshotPage } from './snapshot';
|
||||
import { SettingsPage } from '../shared/settings';
|
||||
import { AppNavigation } from '../../components/navigation/app_navigation';
|
||||
import { SourceLoadingPage } from '../../components/source_loading_page';
|
||||
|
||||
interface InfrastructurePageProps extends RouteComponentProps {
|
||||
uiCapabilities: UICapabilities;
|
||||
|
@ -95,11 +96,15 @@ export const InfrastructurePage = injectUICapabilities(
|
|||
{({ configuration, createDerivedIndexPattern }) => (
|
||||
<MetricsExplorerOptionsContainer.Provider>
|
||||
<WithMetricsExplorerOptionsUrlState />
|
||||
{configuration ? (
|
||||
<MetricsExplorerPage
|
||||
derivedIndexPattern={createDerivedIndexPattern('metrics')}
|
||||
source={configuration}
|
||||
{...props}
|
||||
/>
|
||||
) : (
|
||||
<SourceLoadingPage />
|
||||
)}
|
||||
</MetricsExplorerOptionsContainer.Provider>
|
||||
)}
|
||||
</WithSource>
|
||||
|
|
|
@ -11,22 +11,17 @@ import { IIndexPattern } from 'src/plugins/data/public';
|
|||
import { DocumentTitle } from '../../../components/document_title';
|
||||
import { MetricsExplorerCharts } from '../../../components/metrics_explorer/charts';
|
||||
import { MetricsExplorerToolbar } from '../../../components/metrics_explorer/toolbar';
|
||||
import { SourceLoadingPage } from '../../../components/source_loading_page';
|
||||
import { SourceQuery } from '../../../../common/graphql/types';
|
||||
import { NoData } from '../../../components/empty_states';
|
||||
import { useMetricsExplorerState } from './use_metric_explorer_state';
|
||||
import { useTrackPageview } from '../../../hooks/use_track_metric';
|
||||
|
||||
interface MetricsExplorerPageProps {
|
||||
source: SourceQuery.Query['source']['configuration'] | undefined;
|
||||
source: SourceQuery.Query['source']['configuration'];
|
||||
derivedIndexPattern: IIndexPattern;
|
||||
}
|
||||
|
||||
export const MetricsExplorerPage = ({ source, derivedIndexPattern }: MetricsExplorerPageProps) => {
|
||||
if (!source) {
|
||||
return <SourceLoadingPage />;
|
||||
}
|
||||
|
||||
const {
|
||||
loading,
|
||||
error,
|
||||
|
|
|
@ -59,7 +59,7 @@ export const useMetricsExplorerState = (
|
|||
setAfterKey(null);
|
||||
setTimeRange({ ...currentTimerange, from: start, to: end });
|
||||
},
|
||||
[currentTimerange]
|
||||
[currentTimerange, setTimeRange]
|
||||
);
|
||||
|
||||
const handleGroupByChange = useCallback(
|
||||
|
@ -70,7 +70,7 @@ export const useMetricsExplorerState = (
|
|||
groupBy: groupBy || void 0,
|
||||
});
|
||||
},
|
||||
[options]
|
||||
[options, setOptions]
|
||||
);
|
||||
|
||||
const handleFilterQuerySubmit = useCallback(
|
||||
|
@ -81,7 +81,7 @@ export const useMetricsExplorerState = (
|
|||
filterQuery: query,
|
||||
});
|
||||
},
|
||||
[options]
|
||||
[options, setOptions]
|
||||
);
|
||||
|
||||
const handleMetricsChange = useCallback(
|
||||
|
@ -92,7 +92,7 @@ export const useMetricsExplorerState = (
|
|||
metrics,
|
||||
});
|
||||
},
|
||||
[options]
|
||||
[options, setOptions]
|
||||
);
|
||||
|
||||
const handleAggregationChange = useCallback(
|
||||
|
@ -109,7 +109,7 @@ export const useMetricsExplorerState = (
|
|||
}));
|
||||
setOptions({ ...options, aggregation, metrics });
|
||||
},
|
||||
[options]
|
||||
[options, setOptions]
|
||||
);
|
||||
|
||||
const onViewStateChange = useCallback(
|
||||
|
@ -124,7 +124,7 @@ export const useMetricsExplorerState = (
|
|||
setOptions(vs.options);
|
||||
}
|
||||
},
|
||||
[setChartOptions, setTimeRange, setTimeRange]
|
||||
[setChartOptions, setOptions, setTimeRange]
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
@ -36,7 +36,7 @@ export const LogEntryRatePageContent = () => {
|
|||
useEffect(() => {
|
||||
fetchModuleDefinition();
|
||||
fetchJobStatus();
|
||||
}, []);
|
||||
}, [fetchJobStatus, fetchModuleDefinition]);
|
||||
|
||||
if (!hasLogAnalysisCapabilites) {
|
||||
return <LogEntryRateUnavailableContent />;
|
||||
|
|
|
@ -124,7 +124,7 @@ export const AnomaliesTable: React.FunctionComponent<{
|
|||
setItemIdToExpandedRowMap(newItemIdToExpandedRowMap);
|
||||
}
|
||||
},
|
||||
[results, setTimeRange, timeRange, itemIdToExpandedRowMap, setItemIdToExpandedRowMap]
|
||||
[itemIdToExpandedRowMap, jobId, results, setTimeRange, timeRange]
|
||||
);
|
||||
|
||||
const columns = [
|
||||
|
|
|
@ -54,7 +54,7 @@ export const AnalysisSetupIndicesForm: React.FunctionComponent<{
|
|||
</div>
|
||||
);
|
||||
}),
|
||||
[indices]
|
||||
[handleCheckboxChange, indices]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -31,7 +31,7 @@ export const useLogEntryRateModule = ({
|
|||
spaceId,
|
||||
timestampField,
|
||||
}),
|
||||
[indexPattern]
|
||||
[indexPattern, sourceId, spaceId, timestampField]
|
||||
);
|
||||
|
||||
return useLogAnalysisModule({
|
||||
|
|
|
@ -8,7 +8,6 @@ import { fold } from 'fp-ts/lib/Either';
|
|||
import { constant, identity } from 'fp-ts/lib/function';
|
||||
import { pipe } from 'fp-ts/lib/pipeable';
|
||||
import * as rt from 'io-ts';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { useUrlState } from '../../../utils/use_url_state';
|
||||
|
||||
|
@ -41,12 +40,9 @@ export const useLogAnalysisResultsUrlState = () => {
|
|||
pipe(urlTimeRangeRT.decode(value), fold(constant(undefined), identity)),
|
||||
encodeUrlState: urlTimeRangeRT.encode,
|
||||
urlStateKey: TIME_RANGE_URL_STATE_KEY,
|
||||
writeDefaultState: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setTimeRange(timeRange);
|
||||
}, []);
|
||||
|
||||
const [autoRefresh, setAutoRefresh] = useUrlState({
|
||||
defaultState: {
|
||||
isPaused: false,
|
||||
|
@ -56,12 +52,9 @@ export const useLogAnalysisResultsUrlState = () => {
|
|||
pipe(autoRefreshRT.decode(value), fold(constant(undefined), identity)),
|
||||
encodeUrlState: autoRefreshRT.encode,
|
||||
urlStateKey: AUTOREFRESH_URL_STATE_KEY,
|
||||
writeDefaultState: true,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setAutoRefresh(autoRefresh);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
timeRange,
|
||||
setTimeRange,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import React, { useCallback } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import moment from 'moment';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
|
@ -42,15 +42,15 @@ export const ChartSectionVis = ({
|
|||
seriesOverrides,
|
||||
type,
|
||||
}: VisSectionProps) => {
|
||||
if (!metric || !id) {
|
||||
return null;
|
||||
}
|
||||
const [dateFormat] = useKibanaUiSetting('dateFormat');
|
||||
const valueFormatter = useCallback(getFormatter(formatter, formatterTemplate), [
|
||||
formatter,
|
||||
formatterTemplate,
|
||||
]);
|
||||
const dateFormatter = useCallback(niceTimeFormatter(getMaxMinTimestamp(metric)), [metric]);
|
||||
const dateFormatter = useMemo(
|
||||
() => (metric != null ? niceTimeFormatter(getMaxMinTimestamp(metric)) : undefined),
|
||||
[metric]
|
||||
);
|
||||
const handleTimeChange = useCallback(
|
||||
(from: number, to: number) => {
|
||||
if (onChangeRangeTime) {
|
||||
|
@ -73,7 +73,9 @@ export const ChartSectionVis = ({
|
|||
),
|
||||
};
|
||||
|
||||
if (!metric) {
|
||||
if (!id) {
|
||||
return null;
|
||||
} else if (!metric) {
|
||||
return (
|
||||
<ErrorMessage
|
||||
title={i18n.translate('xpack.infra.chartSection.missingMetricDataText', {
|
||||
|
@ -84,9 +86,7 @@ export const ChartSectionVis = ({
|
|||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (metric.series.some(seriesHasLessThen2DataPoints)) {
|
||||
} else if (metric.series.some(seriesHasLessThen2DataPoints)) {
|
||||
return (
|
||||
<ErrorMessage
|
||||
title={i18n.translate('xpack.infra.chartSection.notEnoughDataPointsToRenderTitle', {
|
||||
|
|
|
@ -41,7 +41,7 @@ interface Props {
|
|||
isAutoReloading: boolean;
|
||||
refreshInterval: number;
|
||||
sideNav: NavItem[];
|
||||
metadata: InfraMetadata | null;
|
||||
metadata: InfraMetadata;
|
||||
addNavItem(item: NavItem): void;
|
||||
setRefreshInterval(refreshInterval: number): void;
|
||||
setAutoReload(isAutoReloading: boolean): void;
|
||||
|
@ -49,10 +49,6 @@ interface Props {
|
|||
setTimeRange(timeRange: MetricsTimeInput): void;
|
||||
}
|
||||
export const NodeDetailsPage = (props: Props) => {
|
||||
if (!props.metadata) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { parsedTimeRange } = props;
|
||||
const { metrics, loading, makeRequest, error } = useNodeDetails(
|
||||
props.requiredMetrics,
|
||||
|
@ -65,11 +61,11 @@ export const NodeDetailsPage = (props: Props) => {
|
|||
|
||||
const refetch = useCallback(() => {
|
||||
makeRequest();
|
||||
}, []);
|
||||
}, [makeRequest]);
|
||||
|
||||
useEffect(() => {
|
||||
makeRequest();
|
||||
}, [parsedTimeRange]);
|
||||
}, [makeRequest, parsedTimeRange]);
|
||||
|
||||
if (error) {
|
||||
return <PageError error={error} name={props.name} />;
|
||||
|
|
|
@ -4,15 +4,15 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiTitle } from '@elastic/eui';
|
||||
import React, {
|
||||
useContext,
|
||||
Children,
|
||||
isValidElement,
|
||||
cloneElement,
|
||||
FunctionComponent,
|
||||
useMemo,
|
||||
isValidElement,
|
||||
useContext,
|
||||
} from 'react';
|
||||
import { EuiTitle } from '@elastic/eui';
|
||||
|
||||
import { SideNavContext, SubNavItem } from '../lib/side_nav_context';
|
||||
import { LayoutProps } from '../types';
|
||||
|
||||
|
@ -31,15 +31,19 @@ export const Section: FunctionComponent<SectionProps> = ({
|
|||
stopLiveStreaming,
|
||||
}) => {
|
||||
const { addNavItem } = useContext(SideNavContext);
|
||||
const subNavItems: SubNavItem[] = [];
|
||||
|
||||
const childrenWithProps = useMemo(
|
||||
() =>
|
||||
Children.map(children, child => {
|
||||
if (isValidElement(child)) {
|
||||
const metric = (metrics && metrics.find(m => m.id === child.props.id)) || null;
|
||||
if (metric) {
|
||||
subNavItems.push({
|
||||
const subNavItems = Children.toArray(children).reduce<SubNavItem[]>(
|
||||
(accumulatedChildren, child) => {
|
||||
if (!isValidElement(child)) {
|
||||
return accumulatedChildren;
|
||||
}
|
||||
const metric = metrics?.find(m => m.id === child.props.id) ?? null;
|
||||
if (metric === null) {
|
||||
return accumulatedChildren;
|
||||
}
|
||||
return [
|
||||
...accumulatedChildren,
|
||||
{
|
||||
id: child.props.id,
|
||||
name: child.props.label,
|
||||
onClick: () => {
|
||||
|
@ -48,18 +52,21 @@ export const Section: FunctionComponent<SectionProps> = ({
|
|||
el.scrollIntoView();
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
return cloneElement(child, {
|
||||
},
|
||||
];
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const childrenWithProps = Children.map(children, child =>
|
||||
isValidElement(child)
|
||||
? cloneElement(child, {
|
||||
metrics,
|
||||
onChangeRangeTime,
|
||||
isLiveStreaming,
|
||||
stopLiveStreaming,
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
[children, metrics, onChangeRangeTime, isLiveStreaming, stopLiveStreaming]
|
||||
})
|
||||
: null
|
||||
);
|
||||
|
||||
if (metrics && subNavItems.length) {
|
||||
|
|
|
@ -23,16 +23,13 @@ export const SubSection: FunctionComponent<SubSectionProps> = ({
|
|||
isLiveStreaming,
|
||||
stopLiveStreaming,
|
||||
}) => {
|
||||
if (!children || !metrics) {
|
||||
const metric = useMemo(() => metrics?.find(m => m.id === id), [id, metrics]);
|
||||
|
||||
if (!children || !metric) {
|
||||
return null;
|
||||
}
|
||||
const metric = metrics.find(m => m.id === id);
|
||||
if (!metric) {
|
||||
return null;
|
||||
}
|
||||
const childrenWithProps = useMemo(
|
||||
() =>
|
||||
Children.map(children, child => {
|
||||
|
||||
const childrenWithProps = Children.map(children, child => {
|
||||
if (isValidElement(child)) {
|
||||
return cloneElement(child, {
|
||||
metric,
|
||||
|
@ -43,9 +40,8 @@ export const SubSection: FunctionComponent<SubSectionProps> = ({
|
|||
});
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
[children, metric, id, onChangeRangeTime, isLiveStreaming, stopLiveStreaming]
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div style={{ margin: '10px 0 16px 0' }} id={id}>
|
||||
{label ? (
|
||||
|
|
|
@ -59,13 +59,10 @@ export const useMetricsTime = () => {
|
|||
|
||||
const [parsedTimeRange, setParsedTimeRange] = useState(parseRange(defaultRange));
|
||||
|
||||
const updateTimeRange = useCallback(
|
||||
(range: MetricsTimeInput) => {
|
||||
const updateTimeRange = useCallback((range: MetricsTimeInput) => {
|
||||
setTimeRange(range);
|
||||
setParsedTimeRange(parseRange(range));
|
||||
},
|
||||
[setParsedTimeRange]
|
||||
);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
timeRange,
|
||||
|
|
|
@ -112,6 +112,7 @@ export const MetricDetail = withMetricPageProviders(
|
|||
})}
|
||||
/>
|
||||
<DetailPageContent data-test-subj="infraMetricsPage">
|
||||
{metadata ? (
|
||||
<NodeDetailsPage
|
||||
name={name}
|
||||
requiredMetrics={filteredRequiredMetrics}
|
||||
|
@ -132,6 +133,7 @@ export const MetricDetail = withMetricPageProviders(
|
|||
triggerRefresh={triggerRefresh}
|
||||
setTimeRange={setTimeRange}
|
||||
/>
|
||||
) : null}
|
||||
</DetailPageContent>
|
||||
</ColumnarPage>
|
||||
)}
|
||||
|
|
|
@ -27,5 +27,8 @@ export const useCancellableEffect = (
|
|||
effect(() => cancellationSignal.isCancelled);
|
||||
|
||||
return cancellationSignal.cancel;
|
||||
|
||||
// the dependencies are managed externally
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, deps);
|
||||
};
|
||||
|
|
|
@ -28,10 +28,15 @@ import { useObservable } from './use_observable';
|
|||
export const useKibanaUiSetting = (key: string, defaultValue?: any) => {
|
||||
const uiSettingsClient = npSetup.core.uiSettings;
|
||||
|
||||
const uiSetting$ = useMemo(() => uiSettingsClient.get$(key, defaultValue), [uiSettingsClient]);
|
||||
const uiSetting$ = useMemo(() => uiSettingsClient.get$(key, defaultValue), [
|
||||
defaultValue,
|
||||
key,
|
||||
uiSettingsClient,
|
||||
]);
|
||||
const uiSetting = useObservable(uiSetting$);
|
||||
|
||||
const setUiSetting = useCallback((value: any) => uiSettingsClient.set(key, value), [
|
||||
key,
|
||||
uiSettingsClient,
|
||||
]);
|
||||
|
||||
|
|
|
@ -190,6 +190,8 @@ export const useTrackedPromise = <Arguments extends any[], Result>(
|
|||
|
||||
return newPendingPromise.promise;
|
||||
},
|
||||
// the dependencies are managed by the caller
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
dependencies
|
||||
);
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
|
||||
import { Location } from 'history';
|
||||
import { useMemo, useCallback } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { decode, encode, RisonValue } from 'rison-node';
|
||||
|
||||
import { QueryString } from 'ui/utils/query_string';
|
||||
|
||||
import { useHistory } from './history_context';
|
||||
|
||||
export const useUrlState = <State>({
|
||||
|
@ -16,21 +16,26 @@ export const useUrlState = <State>({
|
|||
decodeUrlState,
|
||||
encodeUrlState,
|
||||
urlStateKey,
|
||||
writeDefaultState = false,
|
||||
}: {
|
||||
defaultState: State;
|
||||
decodeUrlState: (value: RisonValue | undefined) => State | undefined;
|
||||
encodeUrlState: (value: State) => RisonValue | undefined;
|
||||
urlStateKey: string;
|
||||
writeDefaultState?: boolean;
|
||||
}) => {
|
||||
const history = useHistory();
|
||||
|
||||
// history.location is mutable so we can't reliably use useMemo
|
||||
const queryString = history?.location ? getQueryStringFromLocation(history.location) : '';
|
||||
|
||||
const urlStateString = useMemo(() => {
|
||||
if (!history) {
|
||||
if (!queryString) {
|
||||
return;
|
||||
}
|
||||
|
||||
return getParamFromQueryString(getQueryStringFromLocation(history.location), urlStateKey);
|
||||
}, [history && history.location, urlStateKey]);
|
||||
return getParamFromQueryString(queryString, urlStateKey);
|
||||
}, [queryString, urlStateKey]);
|
||||
|
||||
const decodedState = useMemo(() => decodeUrlState(decodeRisonUrlState(urlStateString)), [
|
||||
decodeUrlState,
|
||||
|
@ -44,27 +49,38 @@ export const useUrlState = <State>({
|
|||
|
||||
const setState = useCallback(
|
||||
(newState: State | undefined) => {
|
||||
if (!history) {
|
||||
if (!history || !history.location) {
|
||||
return;
|
||||
}
|
||||
|
||||
const location = history.location;
|
||||
const currentLocation = history.location;
|
||||
|
||||
const newLocation = replaceQueryStringInLocation(
|
||||
location,
|
||||
currentLocation,
|
||||
replaceStateKeyInQueryString(
|
||||
urlStateKey,
|
||||
typeof newState !== 'undefined' ? encodeUrlState(newState) : undefined
|
||||
)(getQueryStringFromLocation(location))
|
||||
)(getQueryStringFromLocation(currentLocation))
|
||||
);
|
||||
|
||||
if (newLocation !== location) {
|
||||
if (newLocation !== currentLocation) {
|
||||
history.replace(newLocation);
|
||||
}
|
||||
},
|
||||
[encodeUrlState, history, history && history.location, urlStateKey]
|
||||
[encodeUrlState, history, urlStateKey]
|
||||
);
|
||||
|
||||
const [shouldInitialize, setShouldInitialize] = useState(
|
||||
writeDefaultState && typeof decodedState === 'undefined'
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (shouldInitialize) {
|
||||
setShouldInitialize(false);
|
||||
setState(defaultState);
|
||||
}
|
||||
}, [shouldInitialize, setState, defaultState]);
|
||||
|
||||
return [state, setState] as [typeof state, typeof setState];
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,6 @@ export const useVisibilityState = (initialState: boolean) => {
|
|||
show,
|
||||
toggle,
|
||||
}),
|
||||
[isVisible, show, hide]
|
||||
[hide, isVisible, show, toggle]
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue