Skip to content

Commit

Permalink
fix(webapp): refresh data when (re)submitting query (#1410)
Browse files Browse the repository at this point in the history
Closes #1406

Hitting execute/resubmitting the same query now refreshes all data (timelines and flamegraphs).

RE QueryInput:

    Move to its own file
    Add tests
    Move to css modules

Update comparison/diff to, when resubmitting, to reload EVERYTHING, including the opposite's timeline's and flamegraph. For example, when resubmitting the left side, it will also reload:

    the main timeline
    the left AND right timeline
    left and right flamegraphs

Which has the side effect of having your selection be "lost".
  • Loading branch information
eh-am authored Aug 16, 2022
1 parent 948623a commit 3554f84
Show file tree
Hide file tree
Showing 9 changed files with 242 additions and 189 deletions.
7 changes: 5 additions & 2 deletions cypress/integration/webapp/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ describe('QueryParams', () => {
};

cy.visit('/');
cy.get('.tags-input').clear().type(myTag);
cy.get('.tags-query button').click();
cy.get(`[aria-label="query-input"] textarea`).clear().type(myTag);
cy.get(`[aria-label="query-input"] button`).click();
validate();

cy.findByTestId('sidebar-continuous-comparison').click();
Expand All @@ -29,5 +29,8 @@ describe('QueryParams', () => {

cy.findByTestId('sidebar-continuous-single').click();
validate();

cy.findByTestId('sidebar-explore-page').click();
validate();
});
});
66 changes: 66 additions & 0 deletions webapp/javascript/components/QueryInput/QueryInput.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
.executeButton {
flex-grow: 1;
color: var(--ps-immutable-white);
}

.wrapper {
flex-grow: 1;
display: flex;
align-items: flex-start;
position: relative;
}

.input {
flex-grow: 20;
border-top-right-radius: 0px;
border-bottom-right-radius: 0px;
border-right: none;
caret-color: var(--ps-neutral-2);
color: var(--ps-grey-primary);
letter-spacing: 0px;
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
font-size: 16px;
border: 1px solid var(--ps-ui-border);
padding: 0.25em 0.375em;
resize: none;
word-break: break-all;
box-sizing: border-box;
line-height: 27px;
white-space: break-spaces;
overflow: hidden;
}

.highlight {
height: 0;
width: 0;
z-index: 1;
pointer-events: none;
overflow: inherit !important;
position: absolute;
top: 0;

// We use !important so that we can overwrite the classes from
// pre[class*="language-"]
// which from from prism.scss
background: none !important;
padding: 0 !important;
margin: 0 !important;

code {
font-size: 16px;
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
background: none;
border: 1px solid transparent;
box-shadow: none;
padding: 0.25em 0.375em;
margin: 0;
line-height: 27px;
position: absolute;
top: 0px;
left: 0px;
word-break: break-all;
white-space: break-spaces;
letter-spacing: 0px;
border-right: none;
}
}
45 changes: 45 additions & 0 deletions webapp/javascript/components/QueryInput/QueryInput.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';
import { brandQuery } from '@webapp/models/query';
import { render, screen, fireEvent } from '@testing-library/react';

import QueryInput from './QueryInput';

describe('QueryInput', () => {
it('changes content correctly', () => {
const onSubmit = jest.fn();
render(
<QueryInput initialQuery={brandQuery('myquery')} onSubmit={onSubmit} />
);

const form = screen.getByRole('form', { name: /query-input/i });
fireEvent.submit(form);
expect(onSubmit).toHaveBeenCalledWith('myquery');

const input = screen.getByRole('textbox');
fireEvent.change(input, { target: { value: 'myquery2' } });
fireEvent.submit(form);
expect(onSubmit).toHaveBeenCalledWith('myquery2');
});

describe('submission', () => {
const onSubmit = jest.fn();

beforeEach(() => {
render(
<QueryInput initialQuery={brandQuery('myquery')} onSubmit={onSubmit} />
);
});

it('is submitted by pressing Enter', () => {
const input = screen.getByRole('textbox');
fireEvent.keyDown(input, { key: 'Enter' });
expect(onSubmit).toHaveBeenCalledWith('myquery');
});

it('is submitted by clicking on the Execute button', () => {
const button = screen.getByRole('button');
button.click();
expect(onSubmit).toHaveBeenCalledWith('myquery');
});
});
});
103 changes: 103 additions & 0 deletions webapp/javascript/components/QueryInput/QueryInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React, { useState, useEffect, useRef } from 'react';
import { useWindowWidth } from '@react-hook/window-size';
import TextareaAutosize from 'react-textarea-autosize';
import cx from 'classnames';

import { Query, brandQuery } from '@webapp/models/query';
import { Prism } from '@webapp/util/prism';
import Button from '@webapp/ui/Button';

import styles from './QueryInput.module.scss';

interface QueryInputProps {
initialQuery: Query;
onSubmit: (query: string) => void;
}

/**
* QueryInput refers to the input box used for queries
*/
export default function QueryInput({
initialQuery,
onSubmit,
}: QueryInputProps) {
const windowWidth = useWindowWidth();
const [query, setQuery] = useState(initialQuery);
const codeRef = useRef<HTMLElement>(null);
const textareaRef = useRef<ShamefulAny>(null);
const [textAreaSize, setTextAreaSize] = useState({ width: 0, height: 0 });

// If query updated upstream, most likely the application changed
// So we prefer to use it
useEffect(() => {
setQuery(initialQuery);
}, [initialQuery]);

useEffect(() => {
if (Prism && codeRef.current) {
Prism.highlightElement(codeRef.current);
}
}, [query]);

useEffect(() => {
setTextAreaSize({
width: textareaRef?.current?.['offsetWidth'] || 0,
height: textareaRef?.current?.['offsetHeight'] || 0,
});
}, [query, windowWidth, onSubmit]);

const onFormSubmit = (
e:
| React.FormEvent<HTMLFormElement>
| React.KeyboardEvent<HTMLTextAreaElement>
) => {
e.preventDefault();
onSubmit(query);
};

const handleTextareaKeyDown = (
e: React.KeyboardEvent<HTMLTextAreaElement>
) => {
if (e.key === 'Enter' || e.keyCode === 13 || e.keyCode === 10) {
onFormSubmit(e);
}
};

return (
<form
aria-label="query-input"
className={styles.wrapper}
onSubmit={onFormSubmit}
>
<pre
className={cx(styles.highlight, 'language-promql')}
aria-hidden="true"
>
<code
className="language-promql"
id="highlighting-content"
ref={codeRef}
style={textAreaSize}
>
{query}
</code>
</pre>
<TextareaAutosize
className={styles.input}
ref={textareaRef}
value={query}
spellCheck="false"
onChange={(e) => setQuery(brandQuery(e.target.value))}
onKeyDown={handleTextareaKeyDown}
/>
<Button
type="submit"
kind="secondary"
grouped
className={styles.executeButton}
>
Execute
</Button>
</form>
);
}
88 changes: 2 additions & 86 deletions webapp/javascript/components/TagsBar.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
import React, { useState, useEffect, useRef } from 'react';
import Button from '@webapp/ui/Button';
import React, { useState } from 'react';
import 'react-dom';
import { useWindowWidth } from '@react-hook/window-size';
import { TagsState } from '@webapp/redux/reducers/continuous';
import Dropdown, {
SubMenu,
MenuItem,
FocusableItem,
MenuGroup,
} from '@webapp/ui/Dropdown';
import TextareaAutosize from 'react-textarea-autosize';
import { Prism } from '@webapp/util/prism';
import { Query, brandQuery } from '@webapp/models/query';
import Input from '@webapp/ui/Input';
import { appendLabelToQuery } from '@webapp/util/query';
import styles from './TagsBar.module.css';
import QueryInput from '@webapp/components/QueryInput/QueryInput';

interface TagsBarProps {
onSetQuery: (q: Query) => void;
Expand Down Expand Up @@ -85,86 +81,6 @@ function TagsBar({ onSetQuery, query, tags, onSelectedLabel }: TagsBarProps) {
);
}

interface QueryInputProps {
initialQuery: Query;
onSubmit: (query: string) => void;
}

function QueryInput({ initialQuery, onSubmit }: QueryInputProps) {
const windowWidth = useWindowWidth();
const [query, setQuery] = useState(initialQuery);
const codeRef = useRef<HTMLElement>(null);
const textareaRef = useRef<ShamefulAny>(null);
const [textAreaSize, setTextAreaSize] = useState({ width: 0, height: 0 });

// If query updated upstream, most likely the application changed
// So we prefer to use it
useEffect(() => {
setQuery(initialQuery);
}, [initialQuery]);

useEffect(() => {
if (Prism && codeRef.current) {
Prism.highlightElement(codeRef.current);
}
}, [query]);

useEffect(() => {
setTextAreaSize({
width: textareaRef?.current?.['offsetWidth'] || 0,
height: textareaRef?.current?.['offsetHeight'] || 0,
});
}, [query, windowWidth, onSubmit]);

const onFormSubmit = (
e:
| React.FormEvent<HTMLFormElement>
| React.KeyboardEvent<HTMLTextAreaElement>
) => {
e.preventDefault();
onSubmit(query);
};

const handleTextareaKeyDown = (
e: React.KeyboardEvent<HTMLTextAreaElement>
) => {
if ((e.keyCode === 10 || e.keyCode === 13) && e.ctrlKey) {
onFormSubmit(e);
}
};

return (
<form className="tags-query" onSubmit={onFormSubmit}>
<pre className="tags-highlighted language-promql" aria-hidden="true">
<code
className="language-promql"
id="highlighting-content"
ref={codeRef}
style={textAreaSize}
>
{query}
</code>
</pre>
<TextareaAutosize
className="tags-input"
ref={textareaRef}
value={query}
spellCheck="false"
onChange={(e) => setQuery(brandQuery(e.target.value))}
onKeyDown={handleTextareaKeyDown}
/>
<Button
type="submit"
kind="secondary"
grouped
className={styles.executeButton}
>
Execute
</Button>
</form>
);
}

interface LabelsSubmenuProps {
query: Query;
labels: TagsState['tags'];
Expand Down
5 changes: 5 additions & 0 deletions webapp/javascript/components/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ function Toolbar({ hideTagsBar, onSelectedName }: ToolbarProps) {
tags={tags}
onSetQuery={(q) => {
dispatch(actions.setQuery(q));

// It's the same query, so components' useEffect won't pick up a change
if (q === query) {
dispatch(actions.refresh());
}
}}
onSelectedLabel={(label, query) => {
dispatch(
Expand Down
Loading

0 comments on commit 3554f84

Please sign in to comment.