From 5b769a7d3260aa34f01240be0b44a009b376034a Mon Sep 17 00:00:00 2001 From: Vadim Ogievetsky Date: Wed, 10 Jan 2024 20:48:46 -0800 Subject: [PATCH] Update load query detail archive dialog for file input support (#15632) * Update execution-submit-dialog for file input support Modified the execution-submit-dialog to support file inputs instead of text inputs for better usability. Users can now submit their queries by selecting a JSON file directly or dragging the file into the dialog. Made appropriate UI adjustments to accommodate this change in execution-submit-dialog styles file. * Update web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx Co-authored-by: Charles Smith * Update web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx Co-authored-by: Charles Smith * Update web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx Co-authored-by: Charles Smith * Update drag-and-drop instructions in execution-submit-dialog * Add snapshot tests for ExecutionSubmitDialog * prettify --------- Co-authored-by: Charles Smith --- .../execution-submit-dialog.spec.tsx.snap | 70 +++++++++++ .../execution-submit-dialog.scss | 22 +++- .../execution-submit-dialog.spec.tsx | 31 +++++ .../execution-submit-dialog.tsx | 115 +++++++++++++----- 4 files changed, 200 insertions(+), 38 deletions(-) create mode 100644 web-console/src/views/workbench-view/execution-submit-dialog/__snapshots__/execution-submit-dialog.spec.tsx.snap create mode 100644 web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.spec.tsx diff --git a/web-console/src/views/workbench-view/execution-submit-dialog/__snapshots__/execution-submit-dialog.spec.tsx.snap b/web-console/src/views/workbench-view/execution-submit-dialog/__snapshots__/execution-submit-dialog.spec.tsx.snap new file mode 100644 index 00000000000..6719e1b7ba4 --- /dev/null +++ b/web-console/src/views/workbench-view/execution-submit-dialog/__snapshots__/execution-submit-dialog.spec.tsx.snap @@ -0,0 +1,70 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ExecutionSubmitDialog matches snapshot 1`] = ` + +
+

+ You can load query detail archive files from other Druid clusters to render the query detail here. +

+

+ To download the query detail archive for a query, click on the query in the + + + Recent query tasks + + panel in the query view. +

+ + + +

+ Alternatively, drag a file directly onto this dialog. +

+
+
+
+ + +
+
+
+`; diff --git a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.scss b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.scss index 77d355bd841..8370c3503f0 100644 --- a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.scss +++ b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.scss @@ -21,11 +21,21 @@ .execution-submit-dialog { &.#{$bp-ns}-dialog { top: 5%; - width: 90%; - } - - .execution-submit-dialog-textarea { - margin-bottom: 10px; - border-bottom: 1px solid rgba(0, 0, 0, 0.25); + width: 500px; + } +} + +.#{$bp-ns}-overlay-backdrop.dragging-file { + &::after { + position: absolute; + top: 20px; + left: 20px; + right: 20px; + bottom: 20px; + border: 3px dashed cyan; + border-radius: 20px; + + pointer-events: none; + content: ''; } } diff --git a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.spec.tsx b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.spec.tsx new file mode 100644 index 00000000000..12379306e0f --- /dev/null +++ b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.spec.tsx @@ -0,0 +1,31 @@ +/* + * 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 React from 'react'; + +import { shallow } from '../../../utils/shallow-renderer'; + +import { ExecutionSubmitDialog } from './execution-submit-dialog'; + +describe('ExecutionSubmitDialog', () => { + it('matches snapshot', () => { + const comp = shallow( {}} onClose={() => {}} />); + + expect(comp).toMatchSnapshot(); + }); +}); diff --git a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx index 046de0ffb5f..3a563df1c94 100644 --- a/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx +++ b/web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx @@ -16,10 +16,10 @@ * limitations under the License. */ -import { Button, Classes, Dialog, Intent } from '@blueprintjs/core'; +import { Button, Classes, Code, Dialog, FileInput, FormGroup, Intent } from '@blueprintjs/core'; import * as JSONBig from 'json-bigint-native'; +import type { DragEvent } from 'react'; import React, { useState } from 'react'; -import AceEditor from 'react-ace'; import { Execution } from '../../../druid-models'; import { AppToaster } from '../../../singletons'; @@ -28,6 +28,22 @@ import { offsetToRowColumn } from '../../../utils'; import './execution-submit-dialog.scss'; +function getDraggedFile(ev: DragEvent): File | undefined { + if (!ev.dataTransfer) return; + + if (ev.dataTransfer.items) { + // Use DataTransferItemList interface to access the file(s) + const item = ev.dataTransfer.items[0]; + if (item.kind === 'file') { + return item.getAsFile() || undefined; + } + } else { + return ev.dataTransfer.files[0]; + } + + return; +} + export interface ExecutionSubmitDialogProps { onSubmit(execution: Execution): void; onClose(): void; @@ -37,14 +53,19 @@ export const ExecutionSubmitDialog = React.memo(function ExecutionSubmitDialog( props: ExecutionSubmitDialogProps, ) { const { onClose, onSubmit } = props; - const [archive, setArchive] = useState(''); + const [selectedFile, setSelectedFile] = useState(); + const [dragging, setDragging] = useState(false); + + async function handleSubmit(): Promise { + if (!selectedFile) return; + + const text = await selectedFile.text(); - function handleSubmit(): void { let parsed: QueryDetailArchive; try { - parsed = JSONBig.parse(archive); + parsed = JSONBig.parse(text); } catch (e) { - const rowColumn = typeof e.at === 'number' ? offsetToRowColumn(archive, e.at) : undefined; + const rowColumn = typeof e.at === 'number' ? offsetToRowColumn(text, e.at) : undefined; AppToaster.show({ intent: Intent.DANGER, message: `Could not parse JSON: ${e.message}${ @@ -56,7 +77,8 @@ export const ExecutionSubmitDialog = React.memo(function ExecutionSubmitDialog( } let execution: Execution | undefined; - const detailArchiveVersion = parsed.detailArchiveVersion ?? (parsed as any).profileVersion; + const detailArchiveVersion: unknown = + parsed.detailArchiveVersion ?? (parsed as any).profileVersion; if (typeof detailArchiveVersion === 'number') { try { if (detailArchiveVersion === 2) { @@ -102,42 +124,71 @@ export const ExecutionSubmitDialog = React.memo(function ExecutionSubmitDialog( return ( ) { + // Prevent default behavior (Prevent file from being opened) + ev.preventDefault(); + if (dragging) setDragging(false); + + const droppedFile = getDraggedFile(ev); + + if (droppedFile) { + if (!droppedFile.name.endsWith('.json')) { + AppToaster.show({ + intent: Intent.DANGER, + message: `The Query Detail Archive must be a .json file`, + timeout: 5000, + }); + return; + } + + setSelectedFile(droppedFile); + } + }, + onDragOver(ev: DragEvent) { + ev.preventDefault(); // Prevent default behavior (Prevent file from being opened) + if (!dragging) setDragging(true); + }, + onDragLeave(ev: DragEvent) { + ev.preventDefault(); // Prevent default behavior (Prevent file from being opened) + if (dragging) setDragging(false); + }, + }} canOutsideClickClose={false} > - { - editor.renderer.setPadding(10); - editor.renderer.setScrollMargin(10, 10, 0, 0); - }} - /> +
+

+ You can load query detail archive files from other Druid clusters to render the query + detail here. +

+

+ To download the query detail archive for a query, click on the query in the{' '} + Recent query tasks panel in the query view. +

+ + setSelectedFile((e.target as any).files[0])} + inputProps={{ accept: '.json' }} + fill + /> + +

Alternatively, drag a file directly onto this dialog.

+