Skip to content

Commit

Permalink
[Logs UI] Refactor query bar state to hooks (#52656)
Browse files Browse the repository at this point in the history
* [Logs UI] Refactor query bar state to hooks

* Update typedef

* Typecheck fix

* Typecheck fix

* Simplify log filter state

* Remove WithLogFilter HOC and simplify hook further

* Rename js to ts

* Fix redirect imports

* Fix link-to test accuracy

* Fix link-to test

* Simplify destructuring signature

* Stylistic fixes

* Move URL state to hook

* Fix log filter URL state infinite loop

* Revert "Fix log filter URL state infinite loop"

This reverts commit 43302b3.

* Revert "Move URL state to hook"

This reverts commit c61f5b1.

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
Zacqary and elasticmachine authored Jan 3, 2020
1 parent ace50d8 commit a03f395
Show file tree
Hide file tree
Showing 22 changed files with 223 additions and 328 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,5 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { useContext } from 'react';
import createContainer from 'constate';
import { ReduxStateContext } from '../../../utils/redux_context';
import { logFilterSelectors as logFilterReduxSelectors } from '../../../store/local/selectors';

export const useLogFilterState = () => {
const { local: state } = useContext(ReduxStateContext);
const filterQuery = logFilterReduxSelectors.selectLogFilterQueryAsJson(state);
return { filterQuery };
};

export interface LogFilterStateParams {
filterQuery: string | null;
}

export const logFilterInitialState = {
filterQuery: null,
};

export const LogFilterState = createContainer(useLogFilterState);
export * from './log_filter_state';
export * from './with_log_filter_url_state';
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { useState, useMemo } from 'react';
import createContainer from 'constate';
import { IIndexPattern } from 'src/plugins/data/public';
import { esKuery } from '../../../../../../../../src/plugins/data/public';
import { convertKueryToElasticSearchQuery } from '../../../utils/kuery';

export interface KueryFilterQuery {
kind: 'kuery';
expression: string;
}

export interface SerializedFilterQuery {
query: KueryFilterQuery;
serializedQuery: string;
}

interface LogFilterInternalStateParams {
filterQuery: SerializedFilterQuery | null;
filterQueryDraft: KueryFilterQuery | null;
}

export const logFilterInitialState: LogFilterInternalStateParams = {
filterQuery: null,
filterQueryDraft: null,
};

export type LogFilterStateParams = Omit<LogFilterInternalStateParams, 'filterQuery'> & {
filterQuery: SerializedFilterQuery['serializedQuery'] | null;
filterQueryAsKuery: SerializedFilterQuery['query'] | null;
isFilterQueryDraftValid: boolean;
};
export interface LogFilterCallbacks {
setLogFilterQueryDraft: (expression: string) => void;
applyLogFilterQuery: (expression: string) => void;
}

export const useLogFilterState: (props: {
indexPattern: IIndexPattern;
}) => LogFilterStateParams & LogFilterCallbacks = ({ indexPattern }) => {
const [state, setState] = useState(logFilterInitialState);
const { filterQuery, filterQueryDraft } = state;

const setLogFilterQueryDraft = useMemo(() => {
const setDraft = (payload: KueryFilterQuery) =>
setState(prevState => ({ ...prevState, filterQueryDraft: payload }));
return (expression: string) =>
setDraft({
kind: 'kuery',
expression,
});
}, []);
const applyLogFilterQuery = useMemo(() => {
const applyQuery = (payload: SerializedFilterQuery) =>
setState(prevState => ({
...prevState,
filterQueryDraft: payload.query,
filterQuery: payload,
}));
return (expression: string) =>
applyQuery({
query: {
kind: 'kuery',
expression,
},
serializedQuery: convertKueryToElasticSearchQuery(expression, indexPattern),
});
}, [indexPattern]);

const isFilterQueryDraftValid = useMemo(() => {
if (filterQueryDraft?.kind === 'kuery') {
try {
esKuery.fromKueryExpression(filterQueryDraft.expression);
} catch (err) {
return false;
}
}

return true;
}, [filterQueryDraft]);

const serializedFilterQuery = useMemo(() => (filterQuery ? filterQuery.serializedQuery : null), [
filterQuery,
]);

return {
...state,
filterQueryAsKuery: state.filterQuery ? state.filterQuery.query : null,
filterQuery: serializedFilterQuery,
isFilterQueryDraftValid,
setLogFilterQueryDraft,
applyLogFilterQuery,
};
};

export const LogFilterState = createContainer(useLogFilterState);
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* 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, { useContext } from 'react';
import { LogFilterState, LogFilterStateParams } from './log_filter_state';
import { replaceStateKeyInQueryString, UrlStateContainer } from '../../../utils/url_state';

type LogFilterUrlState = LogFilterStateParams['filterQueryAsKuery'];

export const WithLogFilterUrlState: React.FC = () => {
const { filterQueryAsKuery, applyLogFilterQuery } = useContext(LogFilterState.Context);
return (
<UrlStateContainer
urlState={filterQueryAsKuery}
urlStateKey="logFilter"
mapToUrlState={mapToFilterQuery}
onChange={urlState => {
if (urlState) {
applyLogFilterQuery(urlState.expression);
}
}}
onInitialize={urlState => {
if (urlState) {
applyLogFilterQuery(urlState.expression);
}
}}
/>
);
};

const mapToFilterQuery = (value: any): LogFilterUrlState | undefined =>
value?.kind === 'kuery' && typeof value.expression === 'string'
? {
kind: value.kind,
expression: value.expression,
}
: undefined;

export const replaceLogFilterInQueryString = (expression: string) =>
replaceStateKeyInQueryString<LogFilterUrlState>('logFilter', {
kind: 'kuery',
expression,
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import React, { useEffect, useContext } from 'react';

import { TimeKey } from '../../../../common/time';
import { withLogFilter } from '../with_log_filter';
import { withLogPosition } from '../with_log_position';
import { LogHighlightsState } from './log_highlights';

Expand Down Expand Up @@ -35,21 +34,8 @@ export const LogHighlightsPositionBridge = withLogPosition(
}
);

export const LogHighlightsFilterQueryBridge = withLogFilter(
({ serializedFilterQuery }: { serializedFilterQuery: string | null }) => {
const { setFilterQuery } = useContext(LogHighlightsState.Context);

useEffect(() => {
setFilterQuery(serializedFilterQuery);
}, [serializedFilterQuery, setFilterQuery]);

return null;
}
);

export const LogHighlightsBridge = ({ indexPattern }: { indexPattern: any }) => (
export const LogHighlightsBridge = () => (
<>
<LogHighlightsPositionBridge />
<LogHighlightsFilterQueryBridge indexPattern={indexPattern} />
</>
);
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,30 @@
import { useContext } from 'react';
import { connect } from 'react-redux';

import { logFilterSelectors, logPositionSelectors, State } from '../../../store';
import { logPositionSelectors, State } from '../../../store';
import { RendererFunction } from '../../../utils/typed_react';
import { Source } from '../../source';
import { LogViewConfiguration } from '../log_view_configuration';
import { LogSummaryBuckets, useLogSummary } from './log_summary';
import { LogFilterState } from '../log_filter';

export const WithSummary = connect((state: State) => ({
visibleMidpointTime: logPositionSelectors.selectVisibleMidpointOrTargetTime(state),
filterQuery: logFilterSelectors.selectLogFilterQueryAsJson(state),
}))(
({
children,
filterQuery,
visibleMidpointTime,
}: {
children: RendererFunction<{
buckets: LogSummaryBuckets;
start: number | null;
end: number | null;
}>;
filterQuery: string | null;
visibleMidpointTime: number | null;
}) => {
const { intervalSize } = useContext(LogViewConfiguration.Context);
const { sourceId } = useContext(Source.Context);
const { filterQuery } = useContext(LogFilterState.Context);

const { buckets, start, end } = useLogSummary(
sourceId,
Expand Down
100 changes: 0 additions & 100 deletions x-pack/legacy/plugins/infra/public/containers/logs/with_log_filter.tsx

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import compose from 'lodash/fp/compose';
import React from 'react';
import { match as RouteMatch, Redirect, RouteComponentProps } from 'react-router-dom';

import { replaceLogFilterInQueryString } from '../../containers/logs/with_log_filter';
import { replaceLogFilterInQueryString } from '../../containers/logs/log_filter';
import { replaceLogPositionInQueryString } from '../../containers/logs/with_log_position';
import { replaceSourceIdInQueryString } from '../../containers/source_id';
import { getFilterFromLocation, getTimeFromLocation } from './query_params';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import React from 'react';
import { Redirect, RouteComponentProps } from 'react-router-dom';

import { LoadingPage } from '../../components/loading_page';
import { replaceLogFilterInQueryString } from '../../containers/logs/with_log_filter';
import { replaceLogFilterInQueryString } from '../../containers/logs/log_filter';
import { replaceLogPositionInQueryString } from '../../containers/logs/with_log_position';
import { replaceSourceIdInQueryString } from '../../containers/source_id';
import { InfraNodeType, SourceConfigurationFields } from '../../graphql/types';
Expand Down
Loading

0 comments on commit a03f395

Please sign in to comment.