mirror of
https://github.com/lensapp/lens.git
synced 2025-05-20 05:10:56 +00:00
Log search (#1114)
* Moving logs to virtual list Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Introducing log search Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Setting ref for VirtualList to access its methods Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Introducing search store Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Centering overlay when scroll to it Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Using SearchInput in PodLogSearch Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Using Prev/Next icons for search Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * No trigger logs load when scrolled by method Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * SearchInput refactoring Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Adding find counters Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Clean search query on dock tab change Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Refresh search when logs get changed Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Case-insensitive search Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Improve logs scrolling experience Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Catching empty logs in various places Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fixing downloading logs Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Clean up some duplicated styles Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Removing jump-to-bottom animation Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fixing since label Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Reducing container selector size Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Scroll down to bottom after each reload Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fix search within timestamps if they not provided Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Use log row hover color from theme Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Add search bindings for 'Esc' & 'Enter' hits Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Focus input fields on CmdOrCtrl+F Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Move search.store.ts in to /common folder Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * search.store.ts -> search-store.ts Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Adding test for search store Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Adding integration tests for logs Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Fixing scroll jumping bug Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Removing download icon check for testing purpose Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Removing clicking on nginx-create-pod-test Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Moving log tests before cluster operations Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Build extensions before integration tests Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Build also npm before integration tests Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Move npm build and extension build into own build step Signed-off-by: Lauri Nevala <lauri.nevala@gmail.com> * Removing separator sketches Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Horizontal scrolling to founded keyword Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> * Delaying horizontal scrolling Signed-off-by: Alex Andreev <alex.andreev.email@gmail.com> Co-authored-by: Lauri Nevala <lauri.nevala@gmail.com>
This commit is contained in:
1 parent
f69f8c793f
commit
16fb35e3f9
25 files changed
+697
-144
No files matched your search
@@ -1,3 +1,4 @@
|
||||
export * from './input'
|
||||
export * from './search-input'
|
||||
export * from './search-input-url'
|
||||
export * from './file-input'
|
||||
@@ -27,6 +27,7 @@ export type InputProps<T = string> = Omit<InputElementProps, "onChange" | "onSub
|
||||
showValidationLine?: boolean; // show animated validation line for async validators
|
||||
iconLeft?: string | React.ReactNode; // material-icon name in case of string-type
|
||||
iconRight?: string | React.ReactNode;
|
||||
contentRight?: string | React.ReactNode; // Any component of string goes after iconRight
|
||||
validators?: InputValidator | InputValidator[];
|
||||
onChange?(value: T, evt: React.ChangeEvent<InputElement>): void;
|
||||
onSubmit?(value: T): void;
|
||||
@@ -216,6 +217,10 @@ export class Input extends React.Component<InputProps, State> {
|
||||
onKeyDown(evt: React.KeyboardEvent<any>) {
|
||||
const modified = evt.shiftKey || evt.metaKey || evt.altKey || evt.ctrlKey;
|
||||
|
||||
if (this.props.onKeyDown) {
|
||||
this.props.onKeyDown(evt);
|
||||
}
|
||||
|
||||
switch (evt.key) {
|
||||
case "Enter":
|
||||
if (this.props.onSubmit && !modified && !evt.repeat) {
|
||||
@@ -261,7 +266,7 @@ export class Input extends React.Component<InputProps, State> {
|
||||
render() {
|
||||
const {
|
||||
multiLine, showValidationLine, validators, theme, maxRows, children,
|
||||
maxLength, rows, disabled, autoSelectOnFocus, iconLeft, iconRight,
|
||||
maxLength, rows, disabled, autoSelectOnFocus, iconLeft, iconRight, contentRight,
|
||||
...inputProps
|
||||
} = this.props;
|
||||
const { focused, dirty, valid, validating, errors } = this.state;
|
||||
@@ -293,7 +298,8 @@ export class Input extends React.Component<InputProps, State> {
|
||||
<label className="input-area flex gaps align-center">
|
||||
{isString(iconLeft) ? <Icon material={iconLeft}/> : iconLeft}
|
||||
{multiLine ? <textarea {...inputProps as any} /> : <input {...inputProps as any} />}
|
||||
{isString(iconRight) ? <Icon material={iconRight}/> : iconRight}
|
||||
{isString(iconRight) ? <Icon material={iconRight} /> : iconRight}
|
||||
{contentRight}
|
||||
</label>
|
||||
<div className="input-info flex gaps">
|
||||
{!valid && dirty && (
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import React from "react";
|
||||
import debounce from "lodash/debounce";
|
||||
import { autorun, observable } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import { getSearch, setSearch } from "../../navigation";
|
||||
import { InputProps } from "./input";
|
||||
import { SearchInput } from "./search-input";
|
||||
|
||||
interface Props extends InputProps {
|
||||
compact?: boolean; // show only search-icon when not focused
|
||||
}
|
||||
|
||||
@observer
|
||||
export class SearchInputUrl extends React.Component<Props> {
|
||||
@observable inputVal = ""; // fix: use empty string to avoid react warnings
|
||||
|
||||
@disposeOnUnmount
|
||||
updateInput = autorun(() => this.inputVal = getSearch())
|
||||
updateUrl = debounce((val: string) => setSearch(val), 250)
|
||||
|
||||
setValue = (value: string) => {
|
||||
this.inputVal = value;
|
||||
this.updateUrl(value);
|
||||
}
|
||||
|
||||
clear = () => {
|
||||
this.setValue("");
|
||||
this.updateUrl.flush();
|
||||
}
|
||||
|
||||
onChange = (val: string, evt: React.ChangeEvent<any>) => {
|
||||
this.setValue(val);
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(val, evt);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { inputVal } = this;
|
||||
return (
|
||||
<SearchInput
|
||||
value={inputVal}
|
||||
onChange={this.onChange}
|
||||
onClear={this.clear}
|
||||
{...this.props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,10 @@
|
||||
|
||||
> label {
|
||||
color: inherit;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: $radius;
|
||||
box-shadow: 0 0 0 1px $halfGray;
|
||||
padding: 6px 6px 6px 10px;
|
||||
|
||||
.Icon {
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import "./search-input.scss";
|
||||
|
||||
import React from "react";
|
||||
import debounce from "lodash/debounce"
|
||||
import { autorun, observable } from "mobx";
|
||||
import { disposeOnUnmount, observer } from "mobx-react";
|
||||
import React, { createRef } from "react";
|
||||
import { t } from "@lingui/macro";
|
||||
import { observer } from "mobx-react";
|
||||
import { _i18n } from "../../i18n";
|
||||
import { autobind, cssNames } from "../../utils";
|
||||
import { Icon } from "../icon";
|
||||
import { cssNames } from "../../utils";
|
||||
import { Input, InputProps } from "./input";
|
||||
import { getSearch, setSearch } from "../../navigation";
|
||||
import { _i18n } from '../../i18n';
|
||||
|
||||
interface Props extends InputProps {
|
||||
compact?: boolean; // show only search-icon when not focused
|
||||
closeIcon?: boolean;
|
||||
onClear?: () => void;
|
||||
}
|
||||
|
||||
const defaultProps: Partial<Props> = {
|
||||
autoFocus: true,
|
||||
closeIcon: true,
|
||||
get placeholder() {
|
||||
return _i18n._(t`Search...`)
|
||||
},
|
||||
@@ -26,27 +26,24 @@ const defaultProps: Partial<Props> = {
|
||||
export class SearchInput extends React.Component<Props> {
|
||||
static defaultProps = defaultProps as object;
|
||||
|
||||
@observable inputVal = ""; // fix: use empty string to avoid react warnings
|
||||
private input = createRef<Input>();
|
||||
|
||||
@disposeOnUnmount
|
||||
updateInput = autorun(() => this.inputVal = getSearch())
|
||||
updateUrl = debounce((val: string) => setSearch(val), 250)
|
||||
componentDidMount() {
|
||||
addEventListener("keydown", this.focus);
|
||||
}
|
||||
|
||||
setValue = (value: string) => {
|
||||
this.inputVal = value;
|
||||
this.updateUrl(value);
|
||||
componentWillUnmount() {
|
||||
removeEventListener("keydown", this.focus);
|
||||
}
|
||||
|
||||
clear = () => {
|
||||
this.setValue("");
|
||||
this.updateUrl.flush();
|
||||
if (this.props.onClear) {
|
||||
this.props.onClear();
|
||||
}
|
||||
}
|
||||
|
||||
onChange = (val: string, evt: React.ChangeEvent<any>) => {
|
||||
this.setValue(val);
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(val, evt);
|
||||
}
|
||||
this.props.onChange(val, evt);
|
||||
}
|
||||
|
||||
onKeyDown = (evt: React.KeyboardEvent<any>) => {
|
||||
@@ -61,20 +58,27 @@ export class SearchInput extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
@autobind()
|
||||
focus(evt: KeyboardEvent) {
|
||||
const meta = evt.metaKey || evt.ctrlKey;
|
||||
if (meta && evt.key == "f") {
|
||||
this.input.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { inputVal } = this;
|
||||
const { className, compact, ...inputProps } = this.props;
|
||||
const icon = inputVal
|
||||
? <Icon small material="close" onClick={this.clear}/>
|
||||
const { className, compact, closeIcon, onClear, ...inputProps } = this.props;
|
||||
const icon = this.props.value
|
||||
? closeIcon ? <Icon small material="close" onClick={this.clear}/> : null
|
||||
: <Icon small material="search"/>
|
||||
return (
|
||||
<Input
|
||||
{...inputProps}
|
||||
className={cssNames("SearchInput", className, { compact })}
|
||||
value={inputVal}
|
||||
onChange={this.onChange}
|
||||
onKeyDown={this.onKeyDown}
|
||||
iconRight={icon}
|
||||
ref={this.input}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user