Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Infra UI] Selectively persist UI state in the url (via container components) #22980

1 change: 1 addition & 0 deletions src/ui/public/routes/route_manager.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

interface RouteConfiguration {
controller?: string | (() => void);
reloadOnSearch?: boolean;
template?: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface AutocompleteFieldProps {

interface AutocompleteFieldState {
areSuggestionsVisible: boolean;
isFocused: boolean;
selectedIndex: number | null;
}

Expand All @@ -40,6 +41,7 @@ export class AutocompleteField extends React.Component<
> {
public readonly state: AutocompleteFieldState = {
areSuggestionsVisible: false,
isFocused: false,
selectedIndex: null,
};

Expand All @@ -50,15 +52,15 @@ export class AutocompleteField extends React.Component<
const { areSuggestionsVisible, selectedIndex } = this.state;

return (
<EuiOutsideClickDetector onOutsideClick={this.hideSuggestions}>
<EuiOutsideClickDetector onOutsideClick={this.handleBlur}>
<AutocompleteContainer>
<FixedEuiFieldSearch
fullWidth
inputRef={this.handleChangeInputRef}
isLoading={isLoadingSuggestions}
isInvalid={!isValid}
onChange={this.handleChange}
onFocus={this.showSuggestions}
onFocus={this.handleFocus}
onKeyDown={this.handleKeyDown}
onKeyUp={this.handleKeyUp}
onSearch={this.submit}
Expand Down Expand Up @@ -91,7 +93,7 @@ export class AutocompleteField extends React.Component<
this.updateSuggestions();
}

if (hasNewSuggestions) {
if (hasNewSuggestions && this.state.isFocused) {
this.showSuggestions();
}
}
Expand Down Expand Up @@ -150,6 +152,14 @@ export class AutocompleteField extends React.Component<
}
};

private handleFocus = () => {
this.setState(composeStateUpdaters(withSuggestionsVisible, withFocused));
};

private handleBlur = () => {
this.setState(composeStateUpdaters(withSuggestionsHidden, withUnfocused));
};

private selectSuggestionAt = (index: number) => () => {
this.setState(withSuggestionAtIndexSelected(index));
};
Expand Down Expand Up @@ -196,10 +206,6 @@ export class AutocompleteField extends React.Component<
this.setState(withSuggestionsVisible);
};

private hideSuggestions = () => {
this.setState(withSuggestionsHidden);
};

private submit = () => {
const { isValid, onSubmit, value } = this.props;

Expand Down Expand Up @@ -266,6 +272,16 @@ const withSuggestionsHidden = (state: AutocompleteFieldState) => ({
selectedIndex: null,
});

const withFocused = (state: AutocompleteFieldState) => ({
...state,
isFocused: true,
});

const withUnfocused = (state: AutocompleteFieldState) => ({
...state,
isFocused: false,
});

const FixedEuiFieldSearch: React.SFC<
React.InputHTMLAttributes<HTMLInputElement> &
EuiFieldSearchProps & {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
* you may not use this file except in compliance with the Elastic License.
*/

import React from 'react';
import { connect } from 'react-redux';

import { logFilterActions, logFilterSelectors, State } from '../../store';
import { asChildFunctionRenderer } from '../../utils/typed_react';
import { bindPlainActionCreators } from '../../utils/typed_redux';
import { UrlStateContainer } from '../../utils/url_state';

export const withLogFilter = connect(
const withLogFilter = connect(
(state: State) => ({
filterQuery: logFilterSelectors.selectLogFilterQuery(state),
filterQueryDraft: logFilterSelectors.selectLogFilterQueryDraft(state),
Expand All @@ -33,3 +35,39 @@ export const withLogFilter = connect(
);

export const WithLogFilter = asChildFunctionRenderer(withLogFilter);

/**
* Url State
*/

type LogFilterUrlState = ReturnType<typeof logFilterSelectors.selectLogFilterQuery>;

export const WithLogFilterUrlState = () => (
<WithLogFilter>
{({ applyFilterQuery, filterQuery }) => (
<UrlStateContainer
urlState={filterQuery}
urlStateKey="logFilter"
mapToUrlState={mapToFilterQuery}
onChange={urlState => {
if (urlState) {
applyFilterQuery(urlState);
}
}}
onInitialize={urlState => {
if (urlState) {
applyFilterQuery(urlState);
}
}}
/>
)}
</WithLogFilter>
);

const mapToFilterQuery = (value: any): LogFilterUrlState | undefined =>
value && value.kind === 'kuery' && typeof value.expression === 'string'
? {
kind: value.kind,
expression: value.expression,
}
: undefined;
50 changes: 0 additions & 50 deletions x-pack/plugins/infra/public/containers/logs/with_log_minimap.ts

This file was deleted.

101 changes: 101 additions & 0 deletions x-pack/plugins/infra/public/containers/logs/with_log_minimap.tsx
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 React from 'react';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';

import { logMinimapActions, logMinimapSelectors, State } from '../../store';
import { asChildFunctionRenderer } from '../../utils/typed_react';
import { bindPlainActionCreators } from '../../utils/typed_redux';
import { UrlStateContainer } from '../../utils/url_state';

export const withLogMinimap = connect(
(state: State) => ({
availableIntervalSizes,
intervalSize: logMinimapSelectors.selectMinimapIntervalSize(state),
urlState: selectMinimapUrlState(state),
}),
bindPlainActionCreators({
setIntervalSize: logMinimapActions.setMinimapIntervalSize,
})
);

export const WithLogMinimap = asChildFunctionRenderer(withLogMinimap);

export const availableIntervalSizes = [
{
label: '1 Year',
intervalSize: 1000 * 60 * 60 * 24 * 365,
},
{
label: '1 Month',
intervalSize: 1000 * 60 * 60 * 24 * 30,
},
{
label: '1 Week',
intervalSize: 1000 * 60 * 60 * 24 * 7,
},
{
label: '1 Day',
intervalSize: 1000 * 60 * 60 * 24,
},
{
label: '1 Hour',
intervalSize: 1000 * 60 * 60,
},
{
label: '1 Minute',
intervalSize: 1000 * 60,
},
];

/**
* Url State
*/

interface LogMinimapUrlState {
intervalSize?: ReturnType<typeof logMinimapSelectors.selectMinimapIntervalSize>;
}

export const WithLogMinimapUrlState = () => (
<WithLogMinimap>
{({ urlState, setIntervalSize }) => (
<UrlStateContainer
urlState={urlState}
urlStateKey="logMinimap"
mapToUrlState={mapToUrlState}
onChange={newUrlState => {
if (newUrlState && newUrlState.intervalSize) {
setIntervalSize(newUrlState.intervalSize);
}
}}
onInitialize={newUrlState => {
if (newUrlState && newUrlState.intervalSize) {
setIntervalSize(newUrlState.intervalSize);
}
}}
/>
)}
</WithLogMinimap>
);

const mapToUrlState = (value: any): LogMinimapUrlState | undefined =>
value
? {
intervalSize: mapToIntervalSizeUrlState(value.intervalSize),
}
: undefined;

const mapToIntervalSizeUrlState = (value: any) =>
value && typeof value === 'number' ? value : undefined;

const selectMinimapUrlState = createSelector(
logMinimapSelectors.selectMinimapIntervalSize,
intervalSize => ({
intervalSize,
})
);
34 changes: 0 additions & 34 deletions x-pack/plugins/infra/public/containers/logs/with_log_position.ts

This file was deleted.

Loading