This is an automated email from the ASF dual-hosted git repository.

vogievetsky pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git


The following commit(s) were added to refs/heads/master by this push:
     new 5b769a7d326 Update load query detail archive dialog for file input 
support (#15632)
5b769a7d326 is described below

commit 5b769a7d3260aa34f01240be0b44a009b376034a
Author: Vadim Ogievetsky <[email protected]>
AuthorDate: Wed Jan 10 20:48:46 2024 -0800

    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 <[email protected]>
    
    * Update 
web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx
    
    Co-authored-by: Charles Smith <[email protected]>
    
    * Update 
web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.tsx
    
    Co-authored-by: Charles Smith <[email protected]>
    
    * Update drag-and-drop instructions in execution-submit-dialog
    
    * Add snapshot tests for ExecutionSubmitDialog
    
    * prettify
    
    ---------
    
    Co-authored-by: Charles Smith <[email protected]>
---
 .../execution-submit-dialog.spec.tsx.snap          |  70 +++++++++++++
 .../execution-submit-dialog.scss                   |  18 +++-
 ...ialog.scss => execution-submit-dialog.spec.tsx} |  22 ++--
 .../execution-submit-dialog.tsx                    | 115 +++++++++++++++------
 4 files changed, 178 insertions(+), 47 deletions(-)

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`] = `
+<Blueprint4.Dialog
+  backdropProps={
+    Object {
+      "onDragLeave": [Function],
+      "onDragOver": [Function],
+      "onDrop": [Function],
+    }
+  }
+  canOutsideClickClose={false}
+  className="execution-submit-dialog"
+  isOpen={true}
+  onClose={[Function]}
+  title="Load query detail archive"
+>
+  <div
+    className="bp4-dialog-body"
+  >
+    <p>
+      You can load query detail archive files from other Druid clusters to 
render the query detail here.
+    </p>
+    <p>
+      To download the query detail archive for a query, click on the query in 
the
+       
+      <Unknown>
+        Recent query tasks
+      </Unknown>
+       panel in the query view.
+    </p>
+    <Blueprint4.FormGroup
+      label="Select query detail archive file"
+    >
+      <Blueprint4.FileInput
+        fill={true}
+        hasSelection={false}
+        inputProps={
+          Object {
+            "accept": ".json",
+          }
+        }
+        onInputChange={[Function]}
+        text="Choose file..."
+      />
+    </Blueprint4.FormGroup>
+    <p>
+      Alternatively, drag a file directly onto this dialog.
+    </p>
+  </div>
+  <div
+    className="bp4-dialog-footer"
+  >
+    <div
+      className="bp4-dialog-footer-actions"
+    >
+      <Blueprint4.Button
+        onClick={[Function]}
+        text="Close"
+      />
+      <Blueprint4.Button
+        disabled={true}
+        intent="primary"
+        onClick={[Function]}
+        text="Submit"
+      />
+    </div>
+  </div>
+</Blueprint4.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%;
+    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;
 
-  .execution-submit-dialog-textarea {
-    margin-bottom: 10px;
-    border-bottom: 1px solid rgba(0, 0, 0, 0.25);
+    pointer-events: none;
+    content: '';
   }
 }
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.spec.tsx
similarity index 68%
copy from 
web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.scss
copy to 
web-console/src/views/workbench-view/execution-submit-dialog/execution-submit-dialog.spec.tsx
index 77d355bd841..12379306e0f 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.spec.tsx
@@ -16,16 +16,16 @@
  * limitations under the License.
  */
 
-@import '../../../variables';
+import React from 'react';
 
-.execution-submit-dialog {
-  &.#{$bp-ns}-dialog {
-    top: 5%;
-    width: 90%;
-  }
+import { shallow } from '../../../utils/shallow-renderer';
 
-  .execution-submit-dialog-textarea {
-    margin-bottom: 10px;
-    border-bottom: 1px solid rgba(0, 0, 0, 0.25);
-  }
-}
+import { ExecutionSubmitDialog } from './execution-submit-dialog';
+
+describe('ExecutionSubmitDialog', () => {
+  it('matches snapshot', () => {
+    const comp = shallow(<ExecutionSubmitDialog onSubmit={() => {}} 
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<HTMLDivElement>): 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<File | undefined>();
+  const [dragging, setDragging] = useState(false);
+
+  async function handleSubmit(): Promise<void> {
+    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 (
     <Dialog
       className="execution-submit-dialog"
+      backdropClassName={dragging ? `dragging-file` : undefined}
+      style={dragging ? { pointerEvents: 'none' } : undefined}
       isOpen
       onClose={onClose}
       title="Load query detail archive"
+      backdropProps={{
+        onDrop(ev: DragEvent<HTMLDivElement>) {
+          // 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<HTMLDivElement>) {
+          ev.preventDefault(); // Prevent default behavior (Prevent file from 
being opened)
+          if (!dragging) setDragging(true);
+        },
+        onDragLeave(ev: DragEvent<HTMLDivElement>) {
+          ev.preventDefault(); // Prevent default behavior (Prevent file from 
being opened)
+          if (dragging) setDragging(false);
+        },
+      }}
       canOutsideClickClose={false}
     >
-      <AceEditor
-        mode="hjson"
-        theme="solarized_dark"
-        className="execution-submit-dialog-textarea placeholder-padding"
-        onChange={setArchive}
-        fontSize={12}
-        showPrintMargin={false}
-        showGutter
-        highlightActiveLine
-        value={archive}
-        width="100%"
-        setOptions={{
-          showLineNumbers: true,
-          tabSize: 2,
-          newLineMode: 'unix' as any, // newLineMode is incorrectly assumed to 
be boolean in the typings
-        }}
-        style={{}}
-        placeholder="{ Query detail archive or query report... }"
-        onLoad={editor => {
-          editor.renderer.setPadding(10);
-          editor.renderer.setScrollMargin(10, 10, 0, 0);
-        }}
-      />
+      <div className={Classes.DIALOG_BODY}>
+        <p>
+          You can load query detail archive files from other Druid clusters to 
render the query
+          detail here.
+        </p>
+        <p>
+          To download the query detail archive for a query, click on the query 
in the{' '}
+          <Code>Recent query tasks</Code> panel in the query view.
+        </p>
+        <FormGroup label="Select query detail archive file">
+          <FileInput
+            hasSelection={Boolean(selectedFile)}
+            text={selectedFile?.name ?? 'Choose file...'}
+            onInputChange={e => setSelectedFile((e.target as any).files[0])}
+            inputProps={{ accept: '.json' }}
+            fill
+          />
+        </FormGroup>
+        <p>Alternatively, drag a file directly onto this dialog.</p>
+      </div>
       <div className={Classes.DIALOG_FOOTER}>
         <div className={Classes.DIALOG_FOOTER_ACTIONS}>
           <Button text="Close" onClick={onClose} />
           <Button
             text="Submit"
             intent={Intent.PRIMARY}
-            onClick={handleSubmit}
-            disabled={!archive}
+            onClick={() => void handleSubmit()}
+            disabled={!selectedFile}
           />
         </div>
       </div>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to