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:
mcbrewster 2019-08-04 07:18:08 -07:00 committed by Gian Merlino
parent 3f3162b85e
commit ac856fe4c1
8 changed files with 326 additions and 1 deletions

View File

@ -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>
`;

View File

@ -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;
}
}

View File

@ -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();
});
});

View File

@ -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>
);
}
}

View File

@ -36,6 +36,7 @@ exports[`sql view matches snapshot 1`] = `
onQueryContextChange={[Function]}
onRun={[Function]}
queryContext={Object {}}
renderEditContextDialog={[Function]}
runeMode={false}
/>
</div>

View File

@ -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>
);
}

View File

@ -25,6 +25,7 @@ describe('run button', () => {
it('matches snapshot', () => {
const runButton = (
<RunButton
renderEditContextDialog={() => null}
runeMode={false}
queryContext={{}}
onQueryContextChange={() => {}}

View File

@ -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>
);
}