mirror of
https://github.com/apache/druid.git
synced 2025-03-08 10:30:38 +00:00
Web-Console: Adds edit-context-dialog (#8228)
* add edit query context * remove console log * add icon * change to submit button * fix c * add stricter checking * update snapshots
This commit is contained in:
parent
3f3162b85e
commit
ac856fe4c1
@ -0,0 +1,95 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`clipboard dialog matches snapshot 1`] = `
|
||||
<div
|
||||
class="bp3-portal"
|
||||
>
|
||||
<div
|
||||
class="bp3-overlay bp3-overlay-open bp3-overlay-scroll-container"
|
||||
>
|
||||
<div
|
||||
class="bp3-overlay-backdrop bp3-overlay-appear bp3-overlay-appear-active"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
class="bp3-dialog-container bp3-overlay-content bp3-overlay-appear bp3-overlay-appear-active"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="bp3-dialog edit-context-dialog"
|
||||
>
|
||||
<div
|
||||
class="bp3-dialog-header"
|
||||
>
|
||||
<h4
|
||||
class="bp3-heading"
|
||||
>
|
||||
Edit query context
|
||||
</h4>
|
||||
<button
|
||||
aria-label="Close"
|
||||
class="bp3-button bp3-minimal bp3-dialog-close-button"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-small-cross"
|
||||
icon="small-cross"
|
||||
>
|
||||
<svg
|
||||
data-icon="small-cross"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
width="20"
|
||||
>
|
||||
<desc>
|
||||
small-cross
|
||||
</desc>
|
||||
<path
|
||||
d="M11.41 10l3.29-3.29c.19-.18.3-.43.3-.71a1.003 1.003 0 00-1.71-.71L10 8.59l-3.29-3.3a1.003 1.003 0 00-1.42 1.42L8.59 10 5.3 13.29c-.19.18-.3.43-.3.71a1.003 1.003 0 001.71.71l3.29-3.3 3.29 3.29c.18.19.43.3.71.3a1.003 1.003 0 00.71-1.71L11.41 10z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
class="bp3-input"
|
||||
>
|
||||
{
|
||||
|
||||
}
|
||||
</textarea>
|
||||
<div
|
||||
class="bp3-dialog-footer-actions"
|
||||
>
|
||||
|
||||
<div
|
||||
class="bp3-button-group edit-context-dialog-buttons"
|
||||
>
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="bp3-button bp3-intent-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Submit
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.edit-context-dialog {
|
||||
&.bp3-dialog {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.bp3-input {
|
||||
margin: 10px;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.bp3-dialog-footer-actions {
|
||||
padding: 0px 10px 0px 10px;
|
||||
display: grid;
|
||||
grid-template-columns: 340px 1fr;
|
||||
grid-template-areas: 'error buttons';
|
||||
}
|
||||
|
||||
.edit-context-dialog-error {
|
||||
grid-area: error;
|
||||
}
|
||||
.edit-context-dialog-buttons {
|
||||
grid-area: buttons;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { EditContextDialog } from './edit-context-dialog';
|
||||
|
||||
describe('clipboard dialog', () => {
|
||||
it('matches snapshot', () => {
|
||||
const compactionDialog = (
|
||||
<EditContextDialog queryContext={{}} onSubmit={() => null} onClose={() => null} />
|
||||
);
|
||||
render(compactionDialog);
|
||||
expect(document.body.lastChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Button, ButtonGroup, Callout, Classes, Dialog, Intent, TextArea } from '@blueprintjs/core';
|
||||
import React from 'react';
|
||||
|
||||
import { validJson } from '../../utils';
|
||||
import { QueryContext } from '../../utils/query-context';
|
||||
|
||||
import './edit-context-dialog.scss';
|
||||
|
||||
export interface EditContextDialogProps {
|
||||
queryContext: QueryContext;
|
||||
onSubmit: (queryContext: {}) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export interface EditContextDialogState {
|
||||
queryContextString: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export class EditContextDialog extends React.PureComponent<
|
||||
EditContextDialogProps,
|
||||
EditContextDialogState
|
||||
> {
|
||||
constructor(props: EditContextDialogProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
queryContextString: Object.keys(props.queryContext).length
|
||||
? JSON.stringify(props.queryContext, undefined, 2)
|
||||
: '{\n\n}',
|
||||
error: '',
|
||||
};
|
||||
}
|
||||
|
||||
private onTextChange = (e: any) => {
|
||||
let { error } = this.state;
|
||||
const queryContextText = (e.target as HTMLInputElement).value;
|
||||
error = undefined;
|
||||
let queryContextObject;
|
||||
try {
|
||||
queryContextObject = JSON.parse(queryContextText);
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
|
||||
if (!(typeof queryContextObject === 'object')) {
|
||||
error = 'Input is not a valid object';
|
||||
}
|
||||
|
||||
this.setState({
|
||||
queryContextString: queryContextText,
|
||||
error,
|
||||
});
|
||||
};
|
||||
|
||||
render(): JSX.Element {
|
||||
const { onClose, onSubmit } = this.props;
|
||||
const { queryContextString } = this.state;
|
||||
let { error } = this.state;
|
||||
|
||||
let queryContextObject: {} | undefined;
|
||||
try {
|
||||
queryContextObject = JSON.parse(queryContextString);
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
}
|
||||
|
||||
if (!(typeof queryContextObject === 'object') && !error) {
|
||||
error = 'Input is not a valid object';
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
className="edit-context-dialog"
|
||||
isOpen
|
||||
onClose={() => onClose()}
|
||||
title={'Edit query context'}
|
||||
>
|
||||
<TextArea value={queryContextString} onChange={this.onTextChange} autoFocus />
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
{error && (
|
||||
<Callout intent={Intent.DANGER} className="edit-context-dialog-error">
|
||||
{error}
|
||||
</Callout>
|
||||
)}
|
||||
<ButtonGroup className={'edit-context-dialog-buttons'}>
|
||||
<Button
|
||||
text={'Close'}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
disabled={!validJson(queryContextString) || typeof queryContextObject !== 'object'}
|
||||
text={'Submit'}
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={() => (queryContextObject ? onSubmit(queryContextObject) : null)}
|
||||
/>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ exports[`sql view matches snapshot 1`] = `
|
||||
onQueryContextChange={[Function]}
|
||||
onRun={[Function]}
|
||||
queryContext={Object {}}
|
||||
renderEditContextDialog={[Function]}
|
||||
runeMode={false}
|
||||
/>
|
||||
</div>
|
||||
|
@ -24,6 +24,7 @@ import React from 'react';
|
||||
import SplitterLayout from 'react-splitter-layout';
|
||||
|
||||
import { QueryPlanDialog } from '../../dialogs';
|
||||
import { EditContextDialog } from '../../dialogs/edit-context-dialog/edit-context-dialog';
|
||||
import { AppToaster } from '../../singletons/toaster';
|
||||
import {
|
||||
BasicQueryExplanation,
|
||||
@ -77,6 +78,8 @@ export interface QueryViewState {
|
||||
explainResult?: BasicQueryExplanation | SemiJoinQueryExplanation | string;
|
||||
loadingExplain: boolean;
|
||||
explainError?: string;
|
||||
|
||||
editContextDialogOpen: boolean;
|
||||
}
|
||||
|
||||
interface QueryResult {
|
||||
@ -138,6 +141,8 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
|
||||
explainDialogOpen: false,
|
||||
loadingExplain: false,
|
||||
|
||||
editContextDialogOpen: false,
|
||||
};
|
||||
|
||||
this.metadataQueryManager = new QueryManager({
|
||||
@ -316,6 +321,21 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
);
|
||||
}
|
||||
|
||||
renderEditContextDialog() {
|
||||
const { editContextDialogOpen, queryContext } = this.state;
|
||||
if (!editContextDialogOpen) return;
|
||||
|
||||
return (
|
||||
<EditContextDialog
|
||||
onSubmit={(queryContext: QueryContext) =>
|
||||
this.setState({ queryContext, editContextDialogOpen: false })
|
||||
}
|
||||
onClose={() => this.setState({ editContextDialogOpen: false })}
|
||||
queryContext={queryContext}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderMainArea() {
|
||||
const {
|
||||
queryString,
|
||||
@ -348,6 +368,7 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
/>
|
||||
<div className="control-bar">
|
||||
<RunButton
|
||||
renderEditContextDialog={() => this.setState({ editContextDialogOpen: true })}
|
||||
runeMode={runeMode}
|
||||
queryContext={queryContext}
|
||||
onQueryContextChange={this.handleQueryContextChange}
|
||||
@ -407,6 +428,7 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
)}
|
||||
{this.renderMainArea()}
|
||||
{this.renderExplainDialog()}
|
||||
{this.renderEditContextDialog()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ describe('run button', () => {
|
||||
it('matches snapshot', () => {
|
||||
const runButton = (
|
||||
<RunButton
|
||||
renderEditContextDialog={() => null}
|
||||
runeMode={false}
|
||||
queryContext={{}}
|
||||
onQueryContextChange={() => {}}
|
||||
|
@ -49,6 +49,7 @@ export interface RunButtonProps {
|
||||
onQueryContextChange: (newQueryContext: QueryContext) => void;
|
||||
onRun: (wrapQuery: boolean) => void;
|
||||
onExplain: () => void;
|
||||
renderEditContextDialog: () => void;
|
||||
}
|
||||
|
||||
interface RunButtonState {
|
||||
@ -85,7 +86,13 @@ export class RunButton extends React.PureComponent<RunButtonProps, RunButtonStat
|
||||
};
|
||||
|
||||
renderExtraMenu() {
|
||||
const { runeMode, onExplain, queryContext, onQueryContextChange } = this.props;
|
||||
const {
|
||||
runeMode,
|
||||
onExplain,
|
||||
queryContext,
|
||||
onQueryContextChange,
|
||||
renderEditContextDialog,
|
||||
} = this.props;
|
||||
const { wrapQuery } = this.state;
|
||||
|
||||
const useCache = getUseCache(queryContext);
|
||||
@ -133,6 +140,11 @@ export class RunButton extends React.PureComponent<RunButtonProps, RunButtonStat
|
||||
onQueryContextChange(setUseCache(queryContext, !useCache));
|
||||
}}
|
||||
/>
|
||||
<MenuItem
|
||||
icon={IconNames.PROPERTIES}
|
||||
text="Edit context"
|
||||
onClick={renderEditContextDialog}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user