mirror of
https://github.com/apache/druid.git
synced 2025-02-11 04:24:58 +00:00
Web console: Better hotkeys and library upgrades (#11365)
* improve hotkeys * fix test name * refactor explain dialog * explain tests * small fixes * update to popover2 * use resize sensor 2 * misc cleanup
This commit is contained in:
parent
2e98d3c1ad
commit
f56a5b9ba2
@ -4931,6 +4931,15 @@ version: 3.26.1
|
||||
|
||||
---
|
||||
|
||||
name: "@blueprintjs/popover2"
|
||||
license_category: binary
|
||||
module: web-console
|
||||
license_name: Apache License version 2.0
|
||||
copyright: Palantir Technologies
|
||||
version: 0.10.1
|
||||
|
||||
---
|
||||
|
||||
name: "@hypnosphi/create-react-context"
|
||||
license_category: binary
|
||||
module: web-console
|
||||
@ -4941,6 +4950,16 @@ license_file_path: licenses/bin/@hypnosphi-create-react-context.MIT
|
||||
|
||||
---
|
||||
|
||||
name: "@popperjs/core"
|
||||
license_category: binary
|
||||
module: web-console
|
||||
license_name: MIT License
|
||||
copyright: Federico Zivolo
|
||||
version: 2.9.2
|
||||
license_file_path: licenses/bin/@popperjs-core.MIT
|
||||
|
||||
---
|
||||
|
||||
name: "@types/dom4"
|
||||
license_category: binary
|
||||
module: web-console
|
||||
@ -5498,6 +5517,16 @@ license_file_path: licenses/bin/react-dom.MIT
|
||||
|
||||
---
|
||||
|
||||
name: "react-fast-compare"
|
||||
license_category: binary
|
||||
module: web-console
|
||||
license_name: MIT License
|
||||
copyright: Chris Bolin
|
||||
version: 3.2.0
|
||||
license_file_path: licenses/bin/react-fast-compare.MIT
|
||||
|
||||
---
|
||||
|
||||
name: "react-is"
|
||||
license_category: binary
|
||||
module: web-console
|
||||
|
20
licenses/bin/@popperjs-core.MIT
Normal file
20
licenses/bin/@popperjs-core.MIT
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Federico Zivolo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
licenses/bin/react-fast-compare.MIT
Normal file
22
licenses/bin/react-fast-compare.MIT
Normal file
@ -0,0 +1,22 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Formidable Labs
|
||||
Copyright (c) 2017 Evgeny Poberezkin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
40
web-console/package-lock.json
generated
40
web-console/package-lock.json
generated
@ -3077,6 +3077,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@blueprintjs/popover2": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@blueprintjs/popover2/-/popover2-0.10.1.tgz",
|
||||
"integrity": "sha512-5zo4TsoGO79yA/CCsENE7cVuSk3BEMzS99iWDdesyh9I9AU0rkSYcaKbAmpIrWfO9Oe4s8lRPms0OrXF2fs94Q==",
|
||||
"requires": {
|
||||
"@blueprintjs/core": "^3.45.0",
|
||||
"@popperjs/core": "^2.5.4",
|
||||
"classnames": "^2.2",
|
||||
"dom4": "^2.1.5",
|
||||
"react-popper": "^2.2.4",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"tslib": "~1.13.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-popper": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.5.tgz",
|
||||
"integrity": "sha512-kxGkS80eQGtLl18+uig1UIf9MKixFSyPxglsgLBxlYnyDf65BiY9B3nZSc6C9XUNDgStROB0fMQlTEz1KxGddw==",
|
||||
"requires": {
|
||||
"react-fast-compare": "^3.0.1",
|
||||
"warning": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz",
|
||||
"integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@cnakazawa/watch": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz",
|
||||
@ -4294,6 +4324,11 @@
|
||||
"integrity": "sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@popperjs/core": {
|
||||
"version": "2.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz",
|
||||
"integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q=="
|
||||
},
|
||||
"@sheerun/mutationobserver-shim": {
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@sheerun/mutationobserver-shim/-/mutationobserver-shim-0.3.2.tgz",
|
||||
@ -18325,6 +18360,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-fast-compare": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz",
|
||||
"integrity": "sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA=="
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.8.6",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
||||
|
@ -69,6 +69,7 @@
|
||||
"@blueprintjs/core": "^3.45.0",
|
||||
"@blueprintjs/datetime": "^3.23.4",
|
||||
"@blueprintjs/icons": "^3.26.1",
|
||||
"@blueprintjs/popover2": "^0.10.1",
|
||||
"axios": "^0.21.1",
|
||||
"brace": "^0.11.1",
|
||||
"classnames": "^2.2.6",
|
||||
|
@ -16,8 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Popover, Position } from '@blueprintjs/core';
|
||||
import { Position } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import React from 'react';
|
||||
|
||||
import { BasicAction, basicActionsToMenu } from '../../utils/basic-action';
|
||||
@ -42,9 +43,9 @@ export const ActionCell = React.memo(function ActionCell(props: ActionCellProps)
|
||||
<div className="action-cell">
|
||||
{onDetail && <ActionIcon icon={IconNames.SEARCH_TEMPLATE} onClick={onDetail} />}
|
||||
{actionsMenu && (
|
||||
<Popover content={actionsMenu} position={Position.BOTTOM_RIGHT}>
|
||||
<Popover2 content={actionsMenu} position={Position.BOTTOM_RIGHT}>
|
||||
<ActionIcon icon={IconNames.WRENCH} />
|
||||
</Popover>
|
||||
</Popover2>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -24,14 +24,6 @@
|
||||
.bp3-text-muted {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
||||
.bp3-popover-wrapper {
|
||||
display: inline;
|
||||
|
||||
.bp3-popover-target {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,30 +13,26 @@ exports[`form group with info matches snapshot 1`] = `
|
||||
class="bp3-text-muted"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="bp3-icon bp3-icon-info-sign"
|
||||
icon="info-sign"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-info-sign"
|
||||
icon="info-sign"
|
||||
<svg
|
||||
data-icon="info-sign"
|
||||
height="14"
|
||||
viewBox="0 0 16 16"
|
||||
width="14"
|
||||
>
|
||||
<svg
|
||||
data-icon="info-sign"
|
||||
height="14"
|
||||
viewBox="0 0 16 16"
|
||||
width="14"
|
||||
>
|
||||
<desc>
|
||||
info-sign
|
||||
</desc>
|
||||
<path
|
||||
d="M8 0C3.58 0 0 3.58 0 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zM7 3h2v2H7V3zm3 10H6v-1h1V7H6V6h3v6h1v1z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<desc>
|
||||
info-sign
|
||||
</desc>
|
||||
<path
|
||||
d="M8 0C3.58 0 0 3.58 0 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zM7 3h2v2H7V3zm3 10H6v-1h1V7H6V6h3v6h1v1z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
|
@ -17,13 +17,17 @@
|
||||
*/
|
||||
|
||||
.form-group-with-info {
|
||||
.bp3-text-muted .bp3-popover2-target {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.bp3-form-content {
|
||||
position: relative;
|
||||
|
||||
& > .bp3-popover-wrapper {
|
||||
& > .bp3-popover2-target {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 7px;
|
||||
top: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { FormGroup, Icon, Popover } from '@blueprintjs/core';
|
||||
import { FormGroup, Icon } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import React from 'react';
|
||||
|
||||
import './form-group-with-info.scss';
|
||||
@ -35,9 +36,9 @@ export const FormGroupWithInfo = React.memo(function FormGroupWithInfo(
|
||||
const { label, info, inlineInfo, children } = props;
|
||||
|
||||
const popover = (
|
||||
<Popover content={info} position="left-bottom" boundary="viewport">
|
||||
<Popover2 content={info} position="left-bottom">
|
||||
<Icon icon={IconNames.INFO_SIGN} iconSize={14} />
|
||||
</Popover>
|
||||
</Popover2>
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -77,8 +77,8 @@ exports[`header bar matches snapshot 1`] = `
|
||||
}
|
||||
}
|
||||
/>
|
||||
<Blueprint3.Popover
|
||||
boundary="scrollParent"
|
||||
<Blueprint3.Popover2
|
||||
boundary="clippingParents"
|
||||
captureDismiss={false}
|
||||
content={
|
||||
<Blueprint3.Menu>
|
||||
@ -166,21 +166,20 @@ exports[`header bar matches snapshot 1`] = `
|
||||
inheritDarkTheme={true}
|
||||
interactionKind="click"
|
||||
minimal={false}
|
||||
modifiers={Object {}}
|
||||
openOnTargetFocus={true}
|
||||
position="bottom-right"
|
||||
positioningStrategy="absolute"
|
||||
targetTagName="span"
|
||||
transitionDuration={300}
|
||||
usePortal={true}
|
||||
wrapperTagName="span"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
icon="cog"
|
||||
minimal={true}
|
||||
/>
|
||||
</Blueprint3.Popover>
|
||||
<Blueprint3.Popover
|
||||
boundary="scrollParent"
|
||||
</Blueprint3.Popover2>
|
||||
<Blueprint3.Popover2
|
||||
boundary="clippingParents"
|
||||
captureDismiss={false}
|
||||
content={
|
||||
<Blueprint3.Menu>
|
||||
@ -244,19 +243,18 @@ exports[`header bar matches snapshot 1`] = `
|
||||
inheritDarkTheme={true}
|
||||
interactionKind="click"
|
||||
minimal={false}
|
||||
modifiers={Object {}}
|
||||
openOnTargetFocus={true}
|
||||
position="bottom-right"
|
||||
positioningStrategy="absolute"
|
||||
targetTagName="span"
|
||||
transitionDuration={300}
|
||||
usePortal={true}
|
||||
wrapperTagName="span"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
icon="help"
|
||||
minimal={true}
|
||||
/>
|
||||
</Blueprint3.Popover>
|
||||
</Blueprint3.Popover2>
|
||||
</Blueprint3.NavbarGroup>
|
||||
</Blueprint3.Navbar>
|
||||
`;
|
||||
|
@ -27,10 +27,10 @@ import {
|
||||
Navbar,
|
||||
NavbarDivider,
|
||||
NavbarGroup,
|
||||
Popover,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import {
|
||||
@ -175,7 +175,7 @@ const RestrictedMode = React.memo(function RestrictedMode(props: RestrictedModeP
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover
|
||||
<Popover2
|
||||
content={
|
||||
<PopoverText>
|
||||
<p>The console is running in restricted mode.</p>
|
||||
@ -192,7 +192,7 @@ const RestrictedMode = React.memo(function RestrictedMode(props: RestrictedModeP
|
||||
position={Position.BOTTOM_RIGHT}
|
||||
>
|
||||
<Button icon={IconNames.WARNING_SIGN} text={label} intent={Intent.WARNING} minimal />
|
||||
</Popover>
|
||||
</Popover2>
|
||||
);
|
||||
});
|
||||
|
||||
@ -367,12 +367,12 @@ export const HeaderBar = React.memo(function HeaderBar(props: HeaderBarProps) {
|
||||
</NavbarGroup>
|
||||
<NavbarGroup align={Alignment.RIGHT}>
|
||||
<RestrictedMode capabilities={capabilities} />
|
||||
<Popover content={configMenu} position={Position.BOTTOM_RIGHT}>
|
||||
<Popover2 content={configMenu} position={Position.BOTTOM_RIGHT}>
|
||||
<Button minimal icon={IconNames.COG} />
|
||||
</Popover>
|
||||
<Popover content={helpMenu} position={Position.BOTTOM_RIGHT}>
|
||||
</Popover2>
|
||||
<Popover2 content={helpMenu} position={Position.BOTTOM_RIGHT}>
|
||||
<Button minimal icon={IconNames.HELP} />
|
||||
</Popover>
|
||||
</Popover2>
|
||||
</NavbarGroup>
|
||||
{aboutDialogOpen && <AboutDialog onClose={() => setAboutDialogOpen(false)} />}
|
||||
{doctorDialogOpen && <DoctorDialog onClose={() => setDoctorDialogOpen(false)} />}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`interval calendar component matches snapshot 1`] = `
|
||||
exports[`IntervalInput matches snapshot 1`] = `
|
||||
<div
|
||||
class="bp3-input-group bp3-intent-primary"
|
||||
>
|
||||
@ -16,36 +16,32 @@ exports[`interval calendar component matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-calendar"
|
||||
icon="calendar"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-calendar"
|
||||
icon="calendar"
|
||||
<svg
|
||||
data-icon="calendar"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="calendar"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
calendar
|
||||
</desc>
|
||||
<path
|
||||
d="M11 3c.6 0 1-.5 1-1V1c0-.6-.4-1-1-1s-1 .4-1 1v1c0 .5.4 1 1 1zm3-2h-1v1c0 1.1-.9 2-2 2s-2-.9-2-2V1H6v1c0 1.1-.9 2-2 2s-2-.9-2-2V1H1c-.6 0-1 .5-1 1v12c0 .6.4 1 1 1h13c.6 0 1-.4 1-1V2c0-.6-.5-1-1-1zM5 13H2v-3h3v3zm0-4H2V6h3v3zm4 4H6v-3h3v3zm0-4H6V6h3v3zm4 4h-3v-3h3v3zm0-4h-3V6h3v3zM4 3c.6 0 1-.5 1-1V1c0-.6-.4-1-1-1S3 .4 3 1v1c0 .5.4 1 1 1z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
calendar
|
||||
</desc>
|
||||
<path
|
||||
d="M11 3c.6 0 1-.5 1-1V1c0-.6-.4-1-1-1s-1 .4-1 1v1c0 .5.4 1 1 1zm3-2h-1v1c0 1.1-.9 2-2 2s-2-.9-2-2V1H6v1c0 1.1-.9 2-2 2s-2-.9-2-2V1H1c-.6 0-1 .5-1 1v12c0 .6.4 1 1 1h13c.6 0 1-.4 1-1V2c0-.6-.5-1-1-1zM5 13H2v-3h3v3zm0-4H2V6h3v3zm4 4H6v-3h3v3zm0-4H6V6h3v3zm4 4h-3v-3h3v3zm0-4h-3V6h3v3zM4 3c.6 0 1-.5 1-1V1c0-.6-.4-1-1-1S3 .4 3 1v1c0 .5.4 1 1 1z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
.calendar {
|
||||
.bp3-popover-content {
|
||||
right: 310px;
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ import React from 'react';
|
||||
|
||||
import { IntervalInput } from './interval-input';
|
||||
|
||||
describe('interval calendar component', () => {
|
||||
describe('IntervalInput', () => {
|
||||
it('matches snapshot', () => {
|
||||
const intervalInput = (
|
||||
<IntervalInput
|
||||
|
@ -16,15 +16,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, InputGroup, Intent, Popover, Position } from '@blueprintjs/core';
|
||||
import { Button, InputGroup, Intent, Position } from '@blueprintjs/core';
|
||||
import { DateRange, DateRangePicker, TimePrecision } from '@blueprintjs/datetime';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import React from 'react';
|
||||
|
||||
import { intervalToLocalDateRange, localDateRangeToInterval } from '../../utils';
|
||||
|
||||
import './interval-input.scss';
|
||||
|
||||
export interface IntervalInputProps {
|
||||
interval: string;
|
||||
placeholder: string | undefined;
|
||||
@ -41,7 +40,7 @@ export const IntervalInput = React.memo(function IntervalInput(props: IntervalIn
|
||||
placeholder={placeholder}
|
||||
rightElement={
|
||||
<div>
|
||||
<Popover
|
||||
<Popover2
|
||||
popoverClassName="calendar"
|
||||
content={
|
||||
<DateRangePicker
|
||||
@ -57,7 +56,7 @@ export const IntervalInput = React.memo(function IntervalInput(props: IntervalIn
|
||||
position={Position.BOTTOM_RIGHT}
|
||||
>
|
||||
<Button rightIcon={IconNames.CALENDAR} />
|
||||
</Popover>
|
||||
</Popover2>
|
||||
</div>
|
||||
}
|
||||
onChange={(e: any) => {
|
||||
|
@ -2,72 +2,64 @@
|
||||
|
||||
exports[`more button matches snapshot (empty) 1`] = `
|
||||
<span
|
||||
class="bp3-popover-wrapper more-button"
|
||||
class="more-button bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button bp3-disabled"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button bp3-disabled"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-more"
|
||||
icon="more"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-more"
|
||||
icon="more"
|
||||
<svg
|
||||
data-icon="more"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="more"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
more
|
||||
</desc>
|
||||
<path
|
||||
d="M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
more
|
||||
</desc>
|
||||
<path
|
||||
d="M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
`;
|
||||
|
||||
exports[`more button matches snapshot (full) 1`] = `
|
||||
<span
|
||||
class="bp3-popover-wrapper more-button"
|
||||
class="more-button bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-more"
|
||||
icon="more"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-more"
|
||||
icon="more"
|
||||
<svg
|
||||
data-icon="more"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="more"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
more
|
||||
</desc>
|
||||
<path
|
||||
d="M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
more
|
||||
</desc>
|
||||
<path
|
||||
d="M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
`;
|
||||
|
@ -16,8 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Menu, Popover, Position } from '@blueprintjs/core';
|
||||
import { Button, Menu, Position } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
type OpenState = 'open' | 'alt-open';
|
||||
@ -39,7 +40,7 @@ export const MoreButton = React.memo(function MoreButton(props: MoreButtonProps)
|
||||
});
|
||||
|
||||
return (
|
||||
<Popover
|
||||
<Popover2
|
||||
className="more-button"
|
||||
isOpen={Boolean(openState)}
|
||||
content={
|
||||
@ -55,6 +56,6 @@ export const MoreButton = React.memo(function MoreButton(props: MoreButtonProps)
|
||||
}}
|
||||
>
|
||||
<Button icon={IconNames.MORE} disabled={!childCount} />
|
||||
</Popover>
|
||||
</Popover2>
|
||||
);
|
||||
});
|
||||
|
@ -16,10 +16,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HTMLInputProps, INumericInputProps, NumericInput } from '@blueprintjs/core';
|
||||
import { HTMLInputProps, NumericInput, NumericInputProps } from '@blueprintjs/core';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
export type NumericInputWithDefaultProps = HTMLInputProps & INumericInputProps;
|
||||
export type NumericInputWithDefaultProps = HTMLInputProps & NumericInputProps;
|
||||
|
||||
export const NumericInputWithDefault = React.memo(function NumericInputWithDefault(
|
||||
props: NumericInputWithDefaultProps,
|
||||
|
@ -14,36 +14,32 @@ exports[`suggestible input matches snapshot 1`] = `
|
||||
class="bp3-input-action"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button bp3-minimal"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button bp3-minimal"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -23,10 +23,10 @@ import {
|
||||
Intent,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Popover,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import classNames from 'classnames';
|
||||
import React, { useRef } from 'react';
|
||||
|
||||
@ -82,8 +82,7 @@ export const SuggestibleInput = React.memo(function SuggestibleInput(props: Sugg
|
||||
}}
|
||||
rightElement={
|
||||
suggestions && (
|
||||
<Popover
|
||||
boundary="window"
|
||||
<Popover2
|
||||
content={
|
||||
<Menu>
|
||||
{suggestions.map(suggestion => {
|
||||
@ -123,7 +122,7 @@ export const SuggestibleInput = React.memo(function SuggestibleInput(props: Sugg
|
||||
autoFocus={false}
|
||||
>
|
||||
<Button icon={IconNames.CARET_DOWN} minimal />
|
||||
</Popover>
|
||||
</Popover2>
|
||||
)
|
||||
}
|
||||
{...rest}
|
||||
|
@ -2,45 +2,41 @@
|
||||
|
||||
exports[`table column matches snapshot 1`] = `
|
||||
<span
|
||||
class="bp3-popover-wrapper table-column-selector"
|
||||
class="table-column-selector bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Columns
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
class="counter"
|
||||
>
|
||||
Columns
|
||||
<span
|
||||
class="counter"
|
||||
>
|
||||
(2/3)
|
||||
</span>
|
||||
(2/3)
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
`;
|
||||
|
@ -16,8 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Menu, Popover, Position } from '@blueprintjs/core';
|
||||
import { Button, Menu, Position } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { MenuCheckbox } from '../menu-checkbox/menu-checkbox';
|
||||
@ -60,7 +61,7 @@ export const TableColumnSelector = React.memo(function TableColumnSelector(
|
||||
const counterText = `(${columns.filter(isColumnShown).length}/${columns.length})`;
|
||||
|
||||
return (
|
||||
<Popover
|
||||
<Popover2
|
||||
className="table-column-selector"
|
||||
content={checkboxes}
|
||||
position={Position.BOTTOM_RIGHT}
|
||||
@ -73,6 +74,6 @@ export const TableColumnSelector = React.memo(function TableColumnSelector(
|
||||
<Button rightIcon={IconNames.CARET_DOWN}>
|
||||
Columns <span className="counter">{counterText}</span>
|
||||
</Button>
|
||||
</Popover>
|
||||
</Popover2>
|
||||
);
|
||||
});
|
||||
|
@ -7,8 +7,8 @@ exports[`TimedButton matches snapshot 1`] = `
|
||||
<Blueprint3.Button
|
||||
onClick={[Function]}
|
||||
/>
|
||||
<Blueprint3.Popover
|
||||
boundary="scrollParent"
|
||||
<Blueprint3.Popover2
|
||||
boundary="clippingParents"
|
||||
captureDismiss={false}
|
||||
content={
|
||||
<Blueprint3.Menu>
|
||||
@ -35,16 +35,15 @@ exports[`TimedButton matches snapshot 1`] = `
|
||||
inheritDarkTheme={true}
|
||||
interactionKind="click"
|
||||
minimal={false}
|
||||
modifiers={Object {}}
|
||||
openOnTargetFocus={true}
|
||||
positioningStrategy="absolute"
|
||||
targetTagName="span"
|
||||
transitionDuration={300}
|
||||
usePortal={true}
|
||||
wrapperTagName="span"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
rightIcon="caret-down"
|
||||
/>
|
||||
</Blueprint3.Popover>
|
||||
</Blueprint3.Popover2>
|
||||
</Blueprint3.ButtonGroup>
|
||||
`;
|
||||
|
@ -16,16 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
IButtonProps,
|
||||
Menu,
|
||||
MenuDivider,
|
||||
MenuItem,
|
||||
Popover,
|
||||
} from '@blueprintjs/core';
|
||||
import { Button, ButtonGroup, ButtonProps, Menu, MenuDivider, MenuItem } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { useInterval } from '../../hooks';
|
||||
@ -36,7 +29,7 @@ export interface DelayLabel {
|
||||
delay: number;
|
||||
}
|
||||
|
||||
export interface TimedButtonProps extends IButtonProps {
|
||||
export interface TimedButtonProps extends ButtonProps {
|
||||
delays: DelayLabel[];
|
||||
onRefresh: (auto: boolean) => void;
|
||||
localStorageKey?: LocalStorageKeys;
|
||||
@ -77,7 +70,7 @@ export const TimedButton = React.memo(function TimedButton(props: TimedButtonPro
|
||||
return (
|
||||
<ButtonGroup className="timed-button">
|
||||
<Button {...other} text={text} icon={icon} onClick={() => onRefresh(false)} />
|
||||
<Popover
|
||||
<Popover2
|
||||
content={
|
||||
<Menu>
|
||||
<MenuDivider title={label} />
|
||||
@ -93,7 +86,7 @@ export const TimedButton = React.memo(function TimedButton(props: TimedButtonPro
|
||||
}
|
||||
>
|
||||
<Button {...other} rightIcon={IconNames.CARET_DOWN} />
|
||||
</Popover>
|
||||
</Popover2>
|
||||
</ButtonGroup>
|
||||
);
|
||||
});
|
||||
|
@ -141,60 +141,56 @@ exports[`Datasource table action dialog matches snapshot 1`] = `
|
||||
class="footer-actions-left"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-wrench"
|
||||
icon="wrench"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-wrench"
|
||||
icon="wrench"
|
||||
<svg
|
||||
data-icon="wrench"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="wrench"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
wrench
|
||||
</desc>
|
||||
<path
|
||||
d="M15.83 3.7l-3.06 3.05-2.84-.7-.7-2.83L12.29.17a5.004 5.004 0 00-4.83 1.29 4.967 4.967 0 00-1.12 5.36L.58 12.58c-.36.36-.58.86-.58 1.41 0 1.1.9 2 2 2 .55 0 1.05-.22 1.41-.59l5.77-5.77c1.79.69 3.91.33 5.35-1.12 1.32-1.3 1.74-3.15 1.3-4.81z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
<desc>
|
||||
wrench
|
||||
</desc>
|
||||
<path
|
||||
d="M15.83 3.7l-3.06 3.05-2.84-.7-.7-2.83L12.29.17a5.004 5.004 0 00-4.83 1.29 4.967 4.967 0 00-1.12 5.36L.58 12.58c-.36.36-.58.86-.58 1.41 0 1.1.9 2 2 2 .55 0 1.05-.22 1.41-.59l5.77-5.77c1.79.69 3.91.33 5.35-1.12 1.32-1.3 1.74-3.15 1.3-4.81z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Actions
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
Actions
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
|
@ -24,7 +24,6 @@ export * from './doctor-dialog/doctor-dialog';
|
||||
export * from './history-dialog/history-dialog';
|
||||
export * from './lookup-edit-dialog/lookup-edit-dialog';
|
||||
export * from './overlord-dynamic-config-dialog/overlord-dynamic-config-dialog';
|
||||
export * from './query-plan-dialog/query-plan-dialog';
|
||||
export * from './retention-dialog/retention-dialog';
|
||||
export * from './snitch-dialog/snitch-dialog';
|
||||
export * from './spec-dialog/spec-dialog';
|
||||
|
@ -141,60 +141,56 @@ exports[`Lookup table action dialog matches snapshot 1`] = `
|
||||
class="footer-actions-left"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-wrench"
|
||||
icon="wrench"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-wrench"
|
||||
icon="wrench"
|
||||
<svg
|
||||
data-icon="wrench"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="wrench"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
wrench
|
||||
</desc>
|
||||
<path
|
||||
d="M15.83 3.7l-3.06 3.05-2.84-.7-.7-2.83L12.29.17a5.004 5.004 0 00-4.83 1.29 4.967 4.967 0 00-1.12 5.36L.58 12.58c-.36.36-.58.86-.58 1.41 0 1.1.9 2 2 2 .55 0 1.05-.22 1.41-.59l5.77-5.77c1.79.69 3.91.33 5.35-1.12 1.32-1.3 1.74-3.15 1.3-4.81z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
<desc>
|
||||
wrench
|
||||
</desc>
|
||||
<path
|
||||
d="M15.83 3.7l-3.06 3.05-2.84-.7-.7-2.83L12.29.17a5.004 5.004 0 00-4.83 1.29 4.967 4.967 0 00-1.12 5.36L.58 12.58c-.36.36-.58.86-.58 1.41 0 1.1.9 2 2 2 .55 0 1.05-.22 1.41-.59l5.77-5.77c1.79.69 3.91.33 5.35-1.12 1.32-1.3 1.74-3.15 1.3-4.81z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Actions
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
Actions
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`query plan dialog matches snapshot 1`] = `
|
||||
exports[`QueryHistoryDialog matches snapshot 1`] = `
|
||||
<div
|
||||
class="bp3-portal"
|
||||
>
|
@ -21,7 +21,7 @@ import React from 'react';
|
||||
|
||||
import { QueryHistoryDialog } from './query-history-dialog';
|
||||
|
||||
describe('query plan dialog', () => {
|
||||
describe('QueryHistoryDialog', () => {
|
||||
it('matches snapshot', () => {
|
||||
const queryPlanDialog = (
|
||||
<QueryHistoryDialog setQueryString={() => null} queryRecords={[]} onClose={() => {}} />
|
@ -1,86 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`query plan 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 query-plan-dialog"
|
||||
>
|
||||
<div
|
||||
class="bp3-dialog-header"
|
||||
>
|
||||
<h4
|
||||
class="bp3-heading"
|
||||
>
|
||||
Query plan
|
||||
</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>
|
||||
<div
|
||||
class="bp3-dialog-body"
|
||||
>
|
||||
<div
|
||||
class="generic-result"
|
||||
>
|
||||
test
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="bp3-dialog-footer"
|
||||
>
|
||||
<div
|
||||
class="bp3-dialog-footer-actions"
|
||||
>
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* 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 { QueryPlanDialog } from './query-plan-dialog';
|
||||
|
||||
describe('query plan dialog', () => {
|
||||
it('matches snapshot', () => {
|
||||
const queryPlanDialog = (
|
||||
<QueryPlanDialog
|
||||
setQueryString={() => null}
|
||||
explainResult="test"
|
||||
explainError={undefined}
|
||||
onClose={() => {}}
|
||||
/>
|
||||
);
|
||||
render(queryPlanDialog);
|
||||
expect(document.body.lastChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -1,151 +0,0 @@
|
||||
/*
|
||||
* 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,
|
||||
Classes,
|
||||
Dialog,
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Intent,
|
||||
TextArea,
|
||||
} from '@blueprintjs/core';
|
||||
import * as JSONBig from 'json-bigint-native';
|
||||
import React from 'react';
|
||||
|
||||
import { BasicQueryExplanation, SemiJoinQueryExplanation } from '../../utils';
|
||||
|
||||
import './query-plan-dialog.scss';
|
||||
|
||||
export interface QueryPlanDialogProps {
|
||||
explainResult?: BasicQueryExplanation | SemiJoinQueryExplanation | string;
|
||||
explainError?: Error;
|
||||
onClose: () => void;
|
||||
setQueryString: (queryString: string) => void;
|
||||
}
|
||||
|
||||
export const QueryPlanDialog = React.memo(function QueryPlanDialog(props: QueryPlanDialogProps) {
|
||||
const { explainResult, explainError, onClose, setQueryString } = props;
|
||||
|
||||
let content: JSX.Element;
|
||||
let queryString: string | undefined;
|
||||
|
||||
if (explainError) {
|
||||
content = <div>{explainError.message}</div>;
|
||||
} else if (!explainResult) {
|
||||
content = <div />;
|
||||
} else if ((explainResult as BasicQueryExplanation).query) {
|
||||
let signature: JSX.Element | undefined;
|
||||
if ((explainResult as BasicQueryExplanation).signature) {
|
||||
const signatureContent = (explainResult as BasicQueryExplanation).signature || '';
|
||||
signature = (
|
||||
<FormGroup label="Signature">
|
||||
<InputGroup defaultValue={signatureContent} readOnly />
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
queryString = JSONBig.stringify(
|
||||
(explainResult as BasicQueryExplanation).query[0],
|
||||
undefined,
|
||||
2,
|
||||
);
|
||||
content = (
|
||||
<div className="one-query">
|
||||
<FormGroup label="Query">
|
||||
<TextArea readOnly value={queryString} />
|
||||
</FormGroup>
|
||||
{signature}
|
||||
</div>
|
||||
);
|
||||
} else if (
|
||||
(explainResult as SemiJoinQueryExplanation).mainQuery &&
|
||||
(explainResult as SemiJoinQueryExplanation).subQueryRight
|
||||
) {
|
||||
let mainSignature: JSX.Element | undefined;
|
||||
let subSignature: JSX.Element | undefined;
|
||||
if ((explainResult as SemiJoinQueryExplanation).mainQuery.signature) {
|
||||
const signatureContent =
|
||||
(explainResult as SemiJoinQueryExplanation).mainQuery.signature || '';
|
||||
mainSignature = (
|
||||
<FormGroup label="Signature">
|
||||
<InputGroup defaultValue={signatureContent} readOnly />
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
if ((explainResult as SemiJoinQueryExplanation).subQueryRight.signature) {
|
||||
const signatureContent =
|
||||
(explainResult as SemiJoinQueryExplanation).subQueryRight.signature || '';
|
||||
subSignature = (
|
||||
<FormGroup label="Signature">
|
||||
<InputGroup defaultValue={signatureContent} readOnly />
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
content = (
|
||||
<div className="two-queries">
|
||||
<FormGroup label="Main query">
|
||||
<TextArea
|
||||
readOnly
|
||||
value={JSONBig.stringify(
|
||||
(explainResult as SemiJoinQueryExplanation).mainQuery.query,
|
||||
undefined,
|
||||
2,
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
{mainSignature}
|
||||
<FormGroup label="Sub query">
|
||||
<TextArea
|
||||
readOnly
|
||||
value={JSONBig.stringify(
|
||||
(explainResult as SemiJoinQueryExplanation).subQueryRight.query,
|
||||
undefined,
|
||||
2,
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
{subSignature}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
content = <div className="generic-result">{explainResult}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog className="query-plan-dialog" isOpen onClose={onClose} title="Query plan">
|
||||
<div className={Classes.DIALOG_BODY}>{content}</div>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button text="Close" onClick={onClose} />
|
||||
{queryString && (
|
||||
<Button
|
||||
text="Open query"
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={() => {
|
||||
if (queryString) setQueryString(queryString);
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
});
|
@ -245,36 +245,32 @@ exports[`retention dialog matches snapshot 1`] = `
|
||||
class="bp3-input-action"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button bp3-minimal"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button bp3-minimal"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -185,60 +185,56 @@ exports[`task table action dialog matches snapshot 1`] = `
|
||||
class="footer-actions-left"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-wrench"
|
||||
icon="wrench"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-wrench"
|
||||
icon="wrench"
|
||||
<svg
|
||||
data-icon="wrench"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="wrench"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
wrench
|
||||
</desc>
|
||||
<path
|
||||
d="M15.83 3.7l-3.06 3.05-2.84-.7-.7-2.83L12.29.17a5.004 5.004 0 00-4.83 1.29 4.967 4.967 0 00-1.12 5.36L.58 12.58c-.36.36-.58.86-.58 1.41 0 1.1.9 2 2 2 .55 0 1.05-.22 1.41-.59l5.77-5.77c1.79.69 3.91.33 5.35-1.12 1.32-1.3 1.74-3.15 1.3-4.81z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
<desc>
|
||||
wrench
|
||||
</desc>
|
||||
<path
|
||||
d="M15.83 3.7l-3.06 3.05-2.84-.7-.7-2.83L12.29.17a5.004 5.004 0 00-4.83 1.29 4.967 4.967 0 00-1.12 5.36L.58 12.58c-.36.36-.58.86-.58 1.41 0 1.1.9 2 2 2 .55 0 1.05-.22 1.41-.59l5.77-5.77c1.79.69 3.91.33 5.35-1.12 1.32-1.3 1.74-3.15 1.3-4.81z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Actions
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
Actions
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
|
@ -272,60 +272,56 @@ exports[`supervisor table action dialog matches snapshot 1`] = `
|
||||
class="footer-actions-left"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-wrench"
|
||||
icon="wrench"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-wrench"
|
||||
icon="wrench"
|
||||
<svg
|
||||
data-icon="wrench"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="wrench"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
wrench
|
||||
</desc>
|
||||
<path
|
||||
d="M15.83 3.7l-3.06 3.05-2.84-.7-.7-2.83L12.29.17a5.004 5.004 0 00-4.83 1.29 4.967 4.967 0 00-1.12 5.36L.58 12.58c-.36.36-.58.86-.58 1.41 0 1.1.9 2 2 2 .55 0 1.05-.22 1.41-.59l5.77-5.77c1.79.69 3.91.33 5.35-1.12 1.32-1.3 1.74-3.15 1.3-4.81z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
<desc>
|
||||
wrench
|
||||
</desc>
|
||||
<path
|
||||
d="M15.83 3.7l-3.06 3.05-2.84-.7-.7-2.83L12.29.17a5.004 5.004 0 00-4.83 1.29 4.967 4.967 0 00-1.12 5.36L.58 12.58c-.36.36-.58.86-.58 1.41 0 1.1.9 2 2 2 .55 0 1.05-.22 1.41-.59l5.77-5.77c1.79.69 3.91.33 5.35-1.12 1.32-1.3 1.74-3.15 1.3-4.81z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Actions
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
Actions
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
|
@ -16,8 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Classes, Dialog, Icon, IconName, Intent, Popover } from '@blueprintjs/core';
|
||||
import { Button, Classes, Dialog, Icon, IconName, Intent } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
import { BasicAction, basicActionsToMenu } from '../../utils/basic-action';
|
||||
@ -66,9 +67,9 @@ export const TableActionDialog = React.memo(function TableActionDialog(
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
{actionsMenu && (
|
||||
<div className="footer-actions-left">
|
||||
<Popover content={actionsMenu}>
|
||||
<Popover2 content={actionsMenu}>
|
||||
<Button icon={IconNames.WRENCH} text="Actions" rightIcon={IconNames.CARET_DOWN} />
|
||||
</Popover>
|
||||
</Popover2>
|
||||
</div>
|
||||
)}
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
|
@ -272,60 +272,56 @@ exports[`task table action dialog matches snapshot 1`] = `
|
||||
class="footer-actions-left"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-wrench"
|
||||
icon="wrench"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-wrench"
|
||||
icon="wrench"
|
||||
<svg
|
||||
data-icon="wrench"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="wrench"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
wrench
|
||||
</desc>
|
||||
<path
|
||||
d="M15.83 3.7l-3.06 3.05-2.84-.7-.7-2.83L12.29.17a5.004 5.004 0 00-4.83 1.29 4.967 4.967 0 00-1.12 5.36L.58 12.58c-.36.36-.58.86-.58 1.41 0 1.1.9 2 2 2 .55 0 1.05-.22 1.41-.59l5.77-5.77c1.79.69 3.91.33 5.35-1.12 1.32-1.3 1.74-3.15 1.3-4.81z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
<desc>
|
||||
wrench
|
||||
</desc>
|
||||
<path
|
||||
d="M15.83 3.7l-3.06 3.05-2.84-.7-.7-2.83L12.29.17a5.004 5.004 0 00-4.83 1.29 4.967 4.967 0 00-1.12 5.36L.58 12.58c-.36.36-.58.86-.58 1.41 0 1.1.9 2 2 2 .55 0 1.05-.22 1.41-.59l5.77-5.77c1.79.69 3.91.33 5.35-1.12 1.32-1.3 1.74-3.15 1.3-4.81z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Actions
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
Actions
|
||||
</span>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-caret-down"
|
||||
icon="caret-down"
|
||||
>
|
||||
<svg
|
||||
data-icon="caret-down"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
caret-down
|
||||
</desc>
|
||||
<path
|
||||
d="M12 6.5c0-.28-.22-.5-.5-.5h-7a.495.495 0 00-.37.83l3.5 4c.09.1.22.17.37.17s.28-.07.37-.17l3.5-4c.08-.09.13-.2.13-.33z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
|
@ -77,6 +77,7 @@ export function getTimestampExpressionFields(transforms: Transform[]): Field<Tra
|
||||
`timestamp_parse(concat("date", ' ', "time"))`,
|
||||
`timestamp_parse(concat("date", ' ', "time"), 'M/d/yyyy H:mm:ss')`,
|
||||
`timestamp_parse(concat("year", '-', "month", '-', "day"))`,
|
||||
`timestamp_parse("west_coast_time", 'yyyy-MM-dd\\'T\\'HH:mm:ss.SSS', 'America/Los_Angeles')`,
|
||||
`timestamp_parse("local_time", 'yyyy-MM-dd HH:mm:ss', "timezone")`,
|
||||
],
|
||||
info: (
|
||||
|
@ -21,6 +21,7 @@
|
||||
@import './blueprint-overrides';
|
||||
@import '~@blueprintjs/core/src/blueprint';
|
||||
@import '~@blueprintjs/datetime/src/blueprint-datetime';
|
||||
@import '~@blueprintjs/popover2/src/blueprint-popover2';
|
||||
@import '~react-splitter-layout/lib/index';
|
||||
@import '../lib/react-table';
|
||||
|
||||
|
@ -18,7 +18,13 @@
|
||||
|
||||
import { sane } from 'druid-query-toolkit/build/test-utils';
|
||||
|
||||
import { DruidError, getDruidErrorMessage, parseHtmlError, parseQueryPlan } from './druid-query';
|
||||
import {
|
||||
DruidError,
|
||||
getDruidErrorMessage,
|
||||
parseHtmlError,
|
||||
parseQueryPlan,
|
||||
trimSemicolon,
|
||||
} from './druid-query';
|
||||
|
||||
describe('DruidQuery', () => {
|
||||
describe('DruidError.parsePosition', () => {
|
||||
@ -226,4 +232,12 @@ describe('DruidQuery', () => {
|
||||
expect(parseQueryPlan('start')).toMatchInlineSnapshot(`"start"`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.trimSemicolon', () => {
|
||||
it('works', () => {
|
||||
expect(trimSemicolon('SELECT * FROM tbl;')).toEqual('SELECT * FROM tbl');
|
||||
expect(trimSemicolon('SELECT * FROM tbl; ')).toEqual('SELECT * FROM tbl ');
|
||||
expect(trimSemicolon('SELECT * FROM tbl; --hello ')).toEqual('SELECT * FROM tbl --hello ');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -21,10 +21,17 @@ import axios, { AxiosResponse } from 'axios';
|
||||
import { Api } from '../singletons';
|
||||
|
||||
import { assemble } from './general';
|
||||
import { QueryContext } from './query-context';
|
||||
import { RowColumn } from './query-cursor';
|
||||
|
||||
const CANCELED_MESSAGE = 'Query canceled by user.';
|
||||
|
||||
export interface QueryWithContext {
|
||||
queryString: string;
|
||||
queryContext: QueryContext;
|
||||
wrapQueryLimit: number | undefined;
|
||||
}
|
||||
|
||||
export interface DruidErrorResponse {
|
||||
error?: string;
|
||||
errorMessage?: string;
|
||||
@ -357,3 +364,8 @@ export function parseQueryPlan(
|
||||
|
||||
return parseQueryPlanResult(queryArgs);
|
||||
}
|
||||
|
||||
export function trimSemicolon(query: string): string {
|
||||
// Trims out a trailing semicolon while preserving space (https://bit.ly/1n1yfkJ)
|
||||
return query.replace(/;+((?:\s*--[^\n]*)?\s*)$/, '$1');
|
||||
}
|
||||
|
@ -34,11 +34,11 @@ import {
|
||||
Intent,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Popover,
|
||||
Switch,
|
||||
TextArea,
|
||||
} from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import classNames from 'classnames';
|
||||
import * as JSONBig from 'json-bigint-native';
|
||||
import memoize from 'memoize-one';
|
||||
@ -2696,25 +2696,25 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
|
||||
/>
|
||||
{reorderDimensionMenu && (
|
||||
<FormGroup>
|
||||
<Popover content={reorderDimensionMenu}>
|
||||
<Popover2 content={reorderDimensionMenu}>
|
||||
<Button
|
||||
icon={IconNames.ARROWS_HORIZONTAL}
|
||||
text="Reorder dimension"
|
||||
rightIcon={IconNames.CARET_DOWN}
|
||||
/>
|
||||
</Popover>
|
||||
</Popover2>
|
||||
</FormGroup>
|
||||
)}
|
||||
{selectedDimensionSpecIndex !== -1 && deepGet(spec, 'spec.dataSchema.metricsSpec') && (
|
||||
<FormGroup>
|
||||
<Popover content={convertToMetricMenu}>
|
||||
<Popover2 content={convertToMetricMenu}>
|
||||
<Button
|
||||
icon={IconNames.EXCHANGE}
|
||||
text="Convert to metric"
|
||||
rightIcon={IconNames.CARET_DOWN}
|
||||
disabled={dimensions.length <= 1}
|
||||
/>
|
||||
</Popover>
|
||||
</Popover2>
|
||||
</FormGroup>
|
||||
)}
|
||||
<div className="control-buttons">
|
||||
@ -2806,13 +2806,13 @@ export class LoadDataView extends React.PureComponent<LoadDataViewProps, LoadDat
|
||||
/>
|
||||
{selectedMetricSpecIndex !== -1 && dimensionMode === 'specific' && (
|
||||
<FormGroup>
|
||||
<Popover content={convertToDimensionMenu}>
|
||||
<Popover2 content={convertToDimensionMenu}>
|
||||
<Button
|
||||
icon={IconNames.EXCHANGE}
|
||||
text="Convert to dimension"
|
||||
rightIcon={IconNames.CARET_DOWN}
|
||||
/>
|
||||
</Popover>
|
||||
</Popover2>
|
||||
</FormGroup>
|
||||
)}
|
||||
<div className="control-buttons">
|
||||
|
@ -45,7 +45,7 @@ exports[`QueryView matches snapshot 1`] = `
|
||||
queryContext={Object {}}
|
||||
runeMode={false}
|
||||
/>
|
||||
<Blueprint3.Tooltip
|
||||
<Blueprint3.Tooltip2
|
||||
content="Automatically wrap the query with a limit to protect against queries with very large result sets."
|
||||
hoverCloseDelay={0}
|
||||
hoverOpenDelay={800}
|
||||
@ -58,7 +58,7 @@ exports[`QueryView matches snapshot 1`] = `
|
||||
label="Auto limit"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</Blueprint3.Tooltip>
|
||||
</Blueprint3.Tooltip2>
|
||||
<Memo(LiveQueryModeSelector)
|
||||
autoLiveQueryModeShouldRun={true}
|
||||
liveQueryMode="auto"
|
||||
@ -124,7 +124,7 @@ exports[`QueryView matches snapshot with query 1`] = `
|
||||
queryContext={Object {}}
|
||||
runeMode={false}
|
||||
/>
|
||||
<Blueprint3.Tooltip
|
||||
<Blueprint3.Tooltip2
|
||||
content="Automatically wrap the query with a limit to protect against queries with very large result sets."
|
||||
hoverCloseDelay={0}
|
||||
hoverOpenDelay={800}
|
||||
@ -137,7 +137,7 @@ exports[`QueryView matches snapshot with query 1`] = `
|
||||
label="Auto limit"
|
||||
onChange={[Function]}
|
||||
/>
|
||||
</Blueprint3.Tooltip>
|
||||
</Blueprint3.Tooltip2>
|
||||
<Memo(LiveQueryModeSelector)
|
||||
autoLiveQueryModeShouldRun={true}
|
||||
liveQueryMode="auto"
|
||||
|
@ -36,9 +36,9 @@ exports[`ColumnTree matches snapshot 1`] = `
|
||||
Object {
|
||||
"icon": "time",
|
||||
"id": "__time",
|
||||
"label": <Blueprint3.Popover
|
||||
"label": <Blueprint3.Popover2
|
||||
autoFocus={false}
|
||||
boundary="window"
|
||||
boundary="clippingParents"
|
||||
captureDismiss={false}
|
||||
content={
|
||||
<Memo(Deferred)
|
||||
@ -54,24 +54,22 @@ exports[`ColumnTree matches snapshot 1`] = `
|
||||
inheritDarkTheme={true}
|
||||
interactionKind="click"
|
||||
minimal={false}
|
||||
modifiers={Object {}}
|
||||
openOnTargetFocus={true}
|
||||
position="right"
|
||||
targetClassName="bp3-popover-open"
|
||||
positioningStrategy="absolute"
|
||||
targetTagName="span"
|
||||
transitionDuration={300}
|
||||
usePortal={true}
|
||||
wrapperTagName="span"
|
||||
>
|
||||
__time
|
||||
</Blueprint3.Popover>,
|
||||
</Blueprint3.Popover2>,
|
||||
},
|
||||
Object {
|
||||
"icon": "numerical",
|
||||
"id": "added",
|
||||
"label": <Blueprint3.Popover
|
||||
"label": <Blueprint3.Popover2
|
||||
autoFocus={false}
|
||||
boundary="window"
|
||||
boundary="clippingParents"
|
||||
captureDismiss={false}
|
||||
content={
|
||||
<Memo(Deferred)
|
||||
@ -87,24 +85,22 @@ exports[`ColumnTree matches snapshot 1`] = `
|
||||
inheritDarkTheme={true}
|
||||
interactionKind="click"
|
||||
minimal={false}
|
||||
modifiers={Object {}}
|
||||
openOnTargetFocus={true}
|
||||
position="right"
|
||||
targetClassName="bp3-popover-open"
|
||||
positioningStrategy="absolute"
|
||||
targetTagName="span"
|
||||
transitionDuration={300}
|
||||
usePortal={true}
|
||||
wrapperTagName="span"
|
||||
>
|
||||
added
|
||||
</Blueprint3.Popover>,
|
||||
</Blueprint3.Popover2>,
|
||||
},
|
||||
Object {
|
||||
"icon": "numerical",
|
||||
"id": "addedBy10",
|
||||
"label": <Blueprint3.Popover
|
||||
"label": <Blueprint3.Popover2
|
||||
autoFocus={false}
|
||||
boundary="window"
|
||||
boundary="clippingParents"
|
||||
captureDismiss={false}
|
||||
content={
|
||||
<Memo(Deferred)
|
||||
@ -120,24 +116,22 @@ exports[`ColumnTree matches snapshot 1`] = `
|
||||
inheritDarkTheme={true}
|
||||
interactionKind="click"
|
||||
minimal={false}
|
||||
modifiers={Object {}}
|
||||
openOnTargetFocus={true}
|
||||
position="right"
|
||||
targetClassName="bp3-popover-open"
|
||||
positioningStrategy="absolute"
|
||||
targetTagName="span"
|
||||
transitionDuration={300}
|
||||
usePortal={true}
|
||||
wrapperTagName="span"
|
||||
>
|
||||
addedBy10
|
||||
</Blueprint3.Popover>,
|
||||
</Blueprint3.Popover2>,
|
||||
},
|
||||
],
|
||||
"icon": "th",
|
||||
"id": "wikipedia",
|
||||
"isExpanded": true,
|
||||
"label": <Blueprint3.Popover
|
||||
boundary="window"
|
||||
"label": <Blueprint3.Popover2
|
||||
boundary="clippingParents"
|
||||
captureDismiss={false}
|
||||
content={
|
||||
<Memo(Deferred)
|
||||
@ -153,16 +147,15 @@ exports[`ColumnTree matches snapshot 1`] = `
|
||||
inheritDarkTheme={true}
|
||||
interactionKind="click"
|
||||
minimal={false}
|
||||
modifiers={Object {}}
|
||||
openOnTargetFocus={true}
|
||||
position="right"
|
||||
positioningStrategy="absolute"
|
||||
targetTagName="span"
|
||||
transitionDuration={300}
|
||||
usePortal={true}
|
||||
wrapperTagName="span"
|
||||
>
|
||||
wikipedia
|
||||
</Blueprint3.Popover>,
|
||||
</Blueprint3.Popover2>,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
@ -45,14 +45,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
.bp3-popover-wrapper {
|
||||
.bp3-popover2-target {
|
||||
width: 188px;
|
||||
display: inline-block;
|
||||
|
||||
.bp3-popover-target {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HTMLSelect, ITreeNode, Menu, MenuItem, Popover, Position, Tree } from '@blueprintjs/core';
|
||||
import { HTMLSelect, Menu, MenuItem, Position, Tree, TreeNodeInfo } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import {
|
||||
SqlComparison,
|
||||
SqlExpression,
|
||||
@ -115,8 +116,8 @@ export interface ColumnTreeProps {
|
||||
|
||||
export interface ColumnTreeState {
|
||||
prevColumnMetadata?: readonly ColumnMetadata[];
|
||||
columnTree?: ITreeNode[];
|
||||
currentSchemaSubtree?: ITreeNode[];
|
||||
columnTree?: TreeNodeInfo[];
|
||||
currentSchemaSubtree?: TreeNodeInfo[];
|
||||
selectedTreeIndex: number;
|
||||
}
|
||||
|
||||
@ -150,18 +151,17 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
|
||||
const columnTree = groupBy(
|
||||
columnMetadata,
|
||||
r => r.TABLE_SCHEMA,
|
||||
(metadata, schemaName): ITreeNode => ({
|
||||
(metadata, schemaName): TreeNodeInfo => ({
|
||||
id: schemaName,
|
||||
label: schemaName,
|
||||
childNodes: groupBy(
|
||||
metadata,
|
||||
r => r.TABLE_NAME,
|
||||
(metadata, tableName): ITreeNode => ({
|
||||
(metadata, tableName): TreeNodeInfo => ({
|
||||
id: tableName,
|
||||
icon: IconNames.TH,
|
||||
label: (
|
||||
<Popover
|
||||
boundary="window"
|
||||
<Popover2
|
||||
position={Position.RIGHT}
|
||||
content={
|
||||
<Deferred
|
||||
@ -306,19 +306,17 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
|
||||
}
|
||||
>
|
||||
{tableName}
|
||||
</Popover>
|
||||
</Popover2>
|
||||
),
|
||||
childNodes: metadata
|
||||
.map(
|
||||
(columnData): ITreeNode => ({
|
||||
(columnData): TreeNodeInfo => ({
|
||||
id: columnData.COLUMN_NAME,
|
||||
icon: dataTypeToIcon(columnData.DATA_TYPE),
|
||||
label: (
|
||||
<Popover
|
||||
boundary="window"
|
||||
<Popover2
|
||||
position={Position.RIGHT}
|
||||
autoFocus={false}
|
||||
targetClassName="bp3-popover-open"
|
||||
content={
|
||||
<Deferred
|
||||
content={() => {
|
||||
@ -384,7 +382,7 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
|
||||
}
|
||||
>
|
||||
{columnData.COLUMN_NAME}
|
||||
</Popover>
|
||||
</Popover2>
|
||||
),
|
||||
}),
|
||||
)
|
||||
@ -441,7 +439,7 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
|
||||
};
|
||||
}
|
||||
|
||||
renderSchemaSelector() {
|
||||
private renderSchemaSelector() {
|
||||
const { columnTree, selectedTreeIndex } = this.state;
|
||||
if (!columnTree) return null;
|
||||
|
||||
@ -465,11 +463,10 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
|
||||
|
||||
private readonly handleSchemaSelectorChange = (e: ChangeEvent<HTMLSelectElement>): void => {
|
||||
const { columnTree } = this.state;
|
||||
if (!columnTree) return;
|
||||
|
||||
const selectedTreeIndex = Number(e.target.value);
|
||||
|
||||
if (!columnTree) return;
|
||||
|
||||
const currentSchemaSubtree =
|
||||
columnTree[selectedTreeIndex > -1 ? selectedTreeIndex : 0].childNodes;
|
||||
|
||||
@ -479,24 +476,16 @@ export class ColumnTree extends React.PureComponent<ColumnTreeProps, ColumnTreeS
|
||||
});
|
||||
};
|
||||
|
||||
private readonly handleNodeCollapse = (nodeData: ITreeNode) => {
|
||||
private readonly handleNodeCollapse = (nodeData: TreeNodeInfo) => {
|
||||
nodeData.isExpanded = false;
|
||||
this.bounceState();
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
private readonly handleNodeExpand = (nodeData: ITreeNode) => {
|
||||
private readonly handleNodeExpand = (nodeData: TreeNodeInfo) => {
|
||||
nodeData.isExpanded = true;
|
||||
this.bounceState();
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
bounceState() {
|
||||
const { columnTree } = this.state;
|
||||
if (!columnTree) return;
|
||||
this.setState(prevState => ({
|
||||
columnTree: prevState.columnTree ? prevState.columnTree.slice() : undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
render(): JSX.Element | null {
|
||||
const { columnMetadataLoading } = this.props;
|
||||
const { currentSchemaSubtree } = this.state;
|
||||
|
@ -0,0 +1,179 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ExplainDialog matches snapshot on error 1`] = `
|
||||
<Blueprint3.Dialog
|
||||
canOutsideClickClose={true}
|
||||
className="explain-dialog"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
title="Query plan"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-body"
|
||||
>
|
||||
<div>
|
||||
test error
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="bp3-dialog-footer"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
onClick={[Function]}
|
||||
text="Close"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Blueprint3.Dialog>
|
||||
`;
|
||||
|
||||
exports[`ExplainDialog matches snapshot on init 1`] = `
|
||||
<Blueprint3.Dialog
|
||||
canOutsideClickClose={true}
|
||||
className="explain-dialog"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
title="Query plan"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-body"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
<div
|
||||
className="bp3-dialog-footer"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
onClick={[Function]}
|
||||
text="Close"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Blueprint3.Dialog>
|
||||
`;
|
||||
|
||||
exports[`ExplainDialog matches snapshot on loading 1`] = `
|
||||
<Blueprint3.Dialog
|
||||
canOutsideClickClose={true}
|
||||
className="explain-dialog"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
title="Query plan"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-body"
|
||||
>
|
||||
<Memo(Loader) />
|
||||
</div>
|
||||
<div
|
||||
className="bp3-dialog-footer"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
onClick={[Function]}
|
||||
text="Close"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Blueprint3.Dialog>
|
||||
`;
|
||||
|
||||
exports[`ExplainDialog matches snapshot on some data 1`] = `
|
||||
<Blueprint3.Dialog
|
||||
canOutsideClickClose={true}
|
||||
className="explain-dialog"
|
||||
isOpen={true}
|
||||
onClose={[Function]}
|
||||
title="Query plan"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-body"
|
||||
>
|
||||
<div
|
||||
className="one-query"
|
||||
>
|
||||
<Blueprint3.FormGroup
|
||||
label="Query"
|
||||
>
|
||||
<Blueprint3.TextArea
|
||||
readOnly={true}
|
||||
value="{
|
||||
\\"queryType\\": \\"topN\\",
|
||||
\\"dataSource\\": {
|
||||
\\"type\\": \\"table\\",
|
||||
\\"name\\": \\"kttm-multi-day\\"
|
||||
},
|
||||
\\"virtualColumns\\": [],
|
||||
\\"dimension\\": {
|
||||
\\"type\\": \\"default\\",
|
||||
\\"dimension\\": \\"browser\\",
|
||||
\\"outputName\\": \\"d0\\",
|
||||
\\"outputType\\": \\"STRING\\"
|
||||
},
|
||||
\\"metric\\": {
|
||||
\\"type\\": \\"numeric\\",
|
||||
\\"metric\\": \\"a0\\"
|
||||
},
|
||||
\\"threshold\\": 101,
|
||||
\\"intervals\\": {
|
||||
\\"type\\": \\"intervals\\",
|
||||
\\"intervals\\": [
|
||||
\\"-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z\\"
|
||||
]
|
||||
},
|
||||
\\"filter\\": null,
|
||||
\\"granularity\\": {
|
||||
\\"type\\": \\"all\\"
|
||||
},
|
||||
\\"aggregations\\": [
|
||||
{
|
||||
\\"type\\": \\"count\\",
|
||||
\\"name\\": \\"a0\\"
|
||||
}
|
||||
],
|
||||
\\"postAggregations\\": [],
|
||||
\\"context\\": {
|
||||
\\"sqlOuterLimit\\": 101,
|
||||
\\"sqlQueryId\\": \\"5905fe8d-9a91-41e0-8f3a-d7a8ac21dce6\\"
|
||||
},
|
||||
\\"descending\\": false
|
||||
}"
|
||||
/>
|
||||
</Blueprint3.FormGroup>
|
||||
<Blueprint3.FormGroup
|
||||
label="Signature"
|
||||
>
|
||||
<Blueprint3.InputGroup
|
||||
defaultValue="[{d0:STRING, a0:LONG}]"
|
||||
readOnly={true}
|
||||
/>
|
||||
</Blueprint3.FormGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="bp3-dialog-footer"
|
||||
>
|
||||
<div
|
||||
className="bp3-dialog-footer-actions"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
onClick={[Function]}
|
||||
text="Close"
|
||||
/>
|
||||
<Blueprint3.Button
|
||||
intent="primary"
|
||||
onClick={[Function]}
|
||||
text="Open query"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Blueprint3.Dialog>
|
||||
`;
|
@ -16,11 +16,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
.query-plan-dialog {
|
||||
.explain-dialog {
|
||||
&.bp3-dialog {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
.bp3-dialog-body {
|
||||
min-height: 70vh;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
BasicQueryExplanation,
|
||||
parseQueryPlan,
|
||||
QueryState,
|
||||
SemiJoinQueryExplanation,
|
||||
} from '../../../utils';
|
||||
|
||||
import { ExplainDialog } from './explain-dialog';
|
||||
|
||||
let explainState: QueryState<BasicQueryExplanation | SemiJoinQueryExplanation | string> =
|
||||
QueryState.INIT;
|
||||
|
||||
jest.mock('../../../hooks', () => {
|
||||
return {
|
||||
useQueryManager: () => [explainState],
|
||||
};
|
||||
});
|
||||
|
||||
describe('ExplainDialog', () => {
|
||||
function makeExplainDialog() {
|
||||
return (
|
||||
<ExplainDialog
|
||||
setQueryString={() => {}}
|
||||
queryWithContext={{ queryString: 'test', queryContext: {}, wrapQueryLimit: undefined }}
|
||||
onClose={() => {}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
it('matches snapshot on init', () => {
|
||||
expect(shallow(makeExplainDialog())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('matches snapshot on loading', () => {
|
||||
explainState = QueryState.LOADING;
|
||||
|
||||
expect(shallow(makeExplainDialog())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('matches snapshot on error', () => {
|
||||
explainState = new QueryState({ error: new Error('test error') });
|
||||
|
||||
expect(shallow(makeExplainDialog())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('matches snapshot on some data', () => {
|
||||
explainState = new QueryState({
|
||||
data: parseQueryPlan(
|
||||
`DruidQueryRel(query=[{"queryType":"topN","dataSource":{"type":"table","name":"kttm-multi-day"},"virtualColumns":[],"dimension":{"type":"default","dimension":"browser","outputName":"d0","outputType":"STRING"},"metric":{"type":"numeric","metric":"a0"},"threshold":101,"intervals":{"type":"intervals","intervals":["-146136543-09-08T08:23:32.096Z/146140482-04-24T15:36:27.903Z"]},"filter":null,"granularity":{"type":"all"},"aggregations":[{"type":"count","name":"a0"}],"postAggregations":[],"context":{"sqlOuterLimit":101,"sqlQueryId":"5905fe8d-9a91-41e0-8f3a-d7a8ac21dce6"},"descending":false}], signature=[{d0:STRING, a0:LONG}])`,
|
||||
),
|
||||
});
|
||||
|
||||
expect(shallow(makeExplainDialog())).toMatchSnapshot();
|
||||
});
|
||||
});
|
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* 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,
|
||||
Classes,
|
||||
Dialog,
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Intent,
|
||||
TextArea,
|
||||
} from '@blueprintjs/core';
|
||||
import * as JSONBig from 'json-bigint-native';
|
||||
import React from 'react';
|
||||
|
||||
import { Loader } from '../../../components';
|
||||
import { useQueryManager } from '../../../hooks';
|
||||
import {
|
||||
BasicQueryExplanation,
|
||||
getDruidErrorMessage,
|
||||
parseQueryPlan,
|
||||
queryDruidSql,
|
||||
QueryWithContext,
|
||||
SemiJoinQueryExplanation,
|
||||
trimSemicolon,
|
||||
} from '../../../utils';
|
||||
import { isEmptyContext } from '../../../utils/query-context';
|
||||
|
||||
import './explain-dialog.scss';
|
||||
|
||||
function isExplainQuery(query: string): boolean {
|
||||
return /EXPLAIN\sPLAN\sFOR/i.test(query);
|
||||
}
|
||||
|
||||
function wrapInExplainIfNeeded(query: string): string {
|
||||
query = trimSemicolon(query);
|
||||
if (isExplainQuery(query)) return query;
|
||||
return `EXPLAIN PLAN FOR (${query}\n)`;
|
||||
}
|
||||
|
||||
export interface ExplainDialogProps {
|
||||
queryWithContext: QueryWithContext;
|
||||
mandatoryQueryContext?: Record<string, any>;
|
||||
onClose: () => void;
|
||||
setQueryString: (queryString: string) => void;
|
||||
}
|
||||
|
||||
export const ExplainDialog = React.memo(function ExplainDialog(props: ExplainDialogProps) {
|
||||
const { queryWithContext, onClose, setQueryString, mandatoryQueryContext } = props;
|
||||
|
||||
const [explainState] = useQueryManager<
|
||||
QueryWithContext,
|
||||
BasicQueryExplanation | SemiJoinQueryExplanation | string
|
||||
>({
|
||||
processQuery: async (queryWithContext: QueryWithContext) => {
|
||||
const { queryString, queryContext, wrapQueryLimit } = queryWithContext;
|
||||
|
||||
let context: Record<string, any> | undefined;
|
||||
if (!isEmptyContext(queryContext) || wrapQueryLimit || mandatoryQueryContext) {
|
||||
context = { ...queryContext, ...(mandatoryQueryContext || {}) };
|
||||
if (typeof wrapQueryLimit !== 'undefined') {
|
||||
context.sqlOuterLimit = wrapQueryLimit + 1;
|
||||
}
|
||||
}
|
||||
|
||||
let result: any[] | undefined;
|
||||
try {
|
||||
result = await queryDruidSql({
|
||||
query: wrapInExplainIfNeeded(queryString),
|
||||
context,
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(getDruidErrorMessage(e));
|
||||
}
|
||||
|
||||
return parseQueryPlan(result[0]['PLAN']);
|
||||
},
|
||||
initQuery: queryWithContext,
|
||||
});
|
||||
|
||||
let content: JSX.Element;
|
||||
let queryString: string | undefined;
|
||||
|
||||
const { loading, error: explainError, data: explainResult } = explainState;
|
||||
if (loading) {
|
||||
content = <Loader />;
|
||||
} else if (explainError) {
|
||||
content = <div>{explainError.message}</div>;
|
||||
} else if (!explainResult) {
|
||||
content = <div />;
|
||||
} else if ((explainResult as BasicQueryExplanation).query) {
|
||||
queryString = JSONBig.stringify(
|
||||
(explainResult as BasicQueryExplanation).query[0],
|
||||
undefined,
|
||||
2,
|
||||
);
|
||||
content = (
|
||||
<div className="one-query">
|
||||
<FormGroup label="Query">
|
||||
<TextArea readOnly value={queryString} />
|
||||
</FormGroup>
|
||||
{(explainResult as BasicQueryExplanation).signature && (
|
||||
<FormGroup label="Signature">
|
||||
<InputGroup
|
||||
defaultValue={(explainResult as BasicQueryExplanation).signature || ''}
|
||||
readOnly
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
} else if (
|
||||
(explainResult as SemiJoinQueryExplanation).mainQuery &&
|
||||
(explainResult as SemiJoinQueryExplanation).subQueryRight
|
||||
) {
|
||||
content = (
|
||||
<div className="two-queries">
|
||||
<FormGroup label="Main query">
|
||||
<TextArea
|
||||
readOnly
|
||||
value={JSONBig.stringify(
|
||||
(explainResult as SemiJoinQueryExplanation).mainQuery.query,
|
||||
undefined,
|
||||
2,
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
{(explainResult as SemiJoinQueryExplanation).mainQuery.signature && (
|
||||
<FormGroup label="Signature">
|
||||
<InputGroup
|
||||
defaultValue={(explainResult as SemiJoinQueryExplanation).mainQuery.signature || ''}
|
||||
readOnly
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
<FormGroup label="Sub query">
|
||||
<TextArea
|
||||
readOnly
|
||||
value={JSONBig.stringify(
|
||||
(explainResult as SemiJoinQueryExplanation).subQueryRight.query,
|
||||
undefined,
|
||||
2,
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
{(explainResult as SemiJoinQueryExplanation).subQueryRight.signature && (
|
||||
<FormGroup label="Signature">
|
||||
<InputGroup
|
||||
defaultValue={
|
||||
(explainResult as SemiJoinQueryExplanation).subQueryRight.signature || ''
|
||||
}
|
||||
readOnly
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
content = <div className="generic-result">{explainResult}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog className="explain-dialog" isOpen onClose={onClose} title="Query plan">
|
||||
<div className={Classes.DIALOG_BODY}>{content}</div>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<Button text="Close" onClick={onClose} />
|
||||
{queryString && (
|
||||
<Button
|
||||
text="Open query"
|
||||
intent={Intent.PRIMARY}
|
||||
onClick={() => {
|
||||
if (queryString) setQueryString(queryString);
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
);
|
||||
});
|
@ -1,8 +1,8 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LiveQueryModeSelector matches snapshot auto 1`] = `
|
||||
<Blueprint3.Popover
|
||||
boundary="scrollParent"
|
||||
<Blueprint3.Popover2
|
||||
boundary="clippingParents"
|
||||
captureDismiss={false}
|
||||
content={
|
||||
<Blueprint3.Menu>
|
||||
@ -47,14 +47,13 @@ exports[`LiveQueryModeSelector matches snapshot auto 1`] = `
|
||||
inheritDarkTheme={true}
|
||||
interactionKind="click"
|
||||
minimal={true}
|
||||
modifiers={Object {}}
|
||||
openOnTargetFocus={true}
|
||||
portalClassName="live-query-mode-selector-portal"
|
||||
position="bottom-left"
|
||||
positioningStrategy="absolute"
|
||||
targetTagName="span"
|
||||
transitionDuration={300}
|
||||
usePortal={true}
|
||||
wrapperTagName="span"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
className="live-query-mode-selector"
|
||||
@ -70,12 +69,12 @@ exports[`LiveQueryModeSelector matches snapshot auto 1`] = `
|
||||
Auto
|
||||
</span>
|
||||
</Blueprint3.Button>
|
||||
</Blueprint3.Popover>
|
||||
</Blueprint3.Popover2>
|
||||
`;
|
||||
|
||||
exports[`LiveQueryModeSelector matches snapshot on 1`] = `
|
||||
<Blueprint3.Popover
|
||||
boundary="scrollParent"
|
||||
<Blueprint3.Popover2
|
||||
boundary="clippingParents"
|
||||
captureDismiss={false}
|
||||
content={
|
||||
<Blueprint3.Menu>
|
||||
@ -120,14 +119,13 @@ exports[`LiveQueryModeSelector matches snapshot on 1`] = `
|
||||
inheritDarkTheme={true}
|
||||
interactionKind="click"
|
||||
minimal={true}
|
||||
modifiers={Object {}}
|
||||
openOnTargetFocus={true}
|
||||
portalClassName="live-query-mode-selector-portal"
|
||||
position="bottom-left"
|
||||
positioningStrategy="absolute"
|
||||
targetTagName="span"
|
||||
transitionDuration={300}
|
||||
usePortal={true}
|
||||
wrapperTagName="span"
|
||||
>
|
||||
<Blueprint3.Button
|
||||
className="live-query-mode-selector"
|
||||
@ -143,5 +141,5 @@ exports[`LiveQueryModeSelector matches snapshot on 1`] = `
|
||||
On
|
||||
</span>
|
||||
</Blueprint3.Button>
|
||||
</Blueprint3.Popover>
|
||||
</Blueprint3.Popover2>
|
||||
`;
|
||||
|
@ -16,8 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Menu, MenuItem, Popover, PopoverPosition } from '@blueprintjs/core';
|
||||
import { Button, Menu, MenuItem, PopoverPosition } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
|
||||
@ -43,7 +44,7 @@ export const LiveQueryModeSelector = React.memo(function LiveQueryModeSelector(
|
||||
const { liveQueryMode, onLiveQueryModeChange, autoLiveQueryModeShouldRun } = props;
|
||||
|
||||
return (
|
||||
<Popover
|
||||
<Popover2
|
||||
portalClassName="live-query-mode-selector-portal"
|
||||
minimal
|
||||
position={PopoverPosition.BOTTOM_LEFT}
|
||||
@ -72,6 +73,6 @@ export const LiveQueryModeSelector = React.memo(function LiveQueryModeSelector(
|
||||
{LIVE_QUERY_MODE_TITLE[liveQueryMode]}
|
||||
</span>
|
||||
</Button>
|
||||
</Popover>
|
||||
</Popover2>
|
||||
);
|
||||
});
|
||||
|
@ -8,51 +8,43 @@ exports[`QueryExtraInfo matches snapshot 1`] = `
|
||||
class="query-info"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class=""
|
||||
tabindex="0"
|
||||
>
|
||||
<span
|
||||
class=""
|
||||
tabindex="0"
|
||||
>
|
||||
0 results in 8.00s
|
||||
</span>
|
||||
0 results in 8.00s
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
class="bp3-popover-wrapper download-button"
|
||||
class="download-button bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button bp3-minimal"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button bp3-minimal"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-download"
|
||||
icon="download"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-download"
|
||||
icon="download"
|
||||
<svg
|
||||
data-icon="download"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="download"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
download
|
||||
</desc>
|
||||
<path
|
||||
d="M7.99-.01c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zM11.7 9.7l-3 3c-.18.18-.43.29-.71.29s-.53-.11-.71-.29l-3-3A1.003 1.003 0 015.7 8.28l1.29 1.29V3.99c0-.55.45-1 1-1s1 .45 1 1v5.59l1.29-1.29a1.003 1.003 0 011.71.71c0 .27-.11.52-.29.7z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
download
|
||||
</desc>
|
||||
<path
|
||||
d="M7.99-.01c-4.42 0-8 3.58-8 8s3.58 8 8 8 8-3.58 8-8-3.58-8-8-8zM11.7 9.7l-3 3c-.18.18-.43.29-.71.29s-.53-.11-.71-.29l-3-3A1.003 1.003 0 015.7 8.28l1.29 1.29V3.99c0-.55.45-1 1-1s1 .45 1 1v5.59l1.29-1.29a1.003 1.003 0 011.71.71c0 .27-.11.52-.29.7z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
@ -16,17 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Button,
|
||||
Intent,
|
||||
Menu,
|
||||
MenuDivider,
|
||||
MenuItem,
|
||||
Popover,
|
||||
Position,
|
||||
Tooltip,
|
||||
} from '@blueprintjs/core';
|
||||
import { Button, Intent, Menu, MenuDivider, MenuItem, Position } from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2, Tooltip2 } from '@blueprintjs/popover2';
|
||||
import copy from 'copy-to-clipboard';
|
||||
import { QueryResult } from 'druid-query-toolkit';
|
||||
import React, { MouseEvent } from 'react';
|
||||
@ -103,14 +95,14 @@ export const QueryExtraInfo = React.memo(function QueryExtraInfo(props: QueryExt
|
||||
<div className="query-extra-info">
|
||||
{typeof queryResult.queryDuration !== 'undefined' && (
|
||||
<div className="query-info" onClick={handleQueryInfoClick}>
|
||||
<Tooltip content={tooltipContent} hoverOpenDelay={500}>
|
||||
<Tooltip2 content={tooltipContent} hoverOpenDelay={500} placement="top-start">
|
||||
{`${resultCount} in ${(queryResult.queryDuration / 1000).toFixed(2)}s`}
|
||||
</Tooltip>
|
||||
</Tooltip2>
|
||||
</div>
|
||||
)}
|
||||
<Popover className="download-button" content={downloadMenu} position={Position.BOTTOM_RIGHT}>
|
||||
<Popover2 className="download-button" content={downloadMenu} position={Position.BOTTOM_RIGHT}>
|
||||
<Button icon={IconNames.DOWNLOAD} minimal />
|
||||
</Popover>
|
||||
</Popover2>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
@ -1,13 +1,13 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`query input correctly formats helper HTML 1`] = `
|
||||
exports[`QueryInput correctly formats helper HTML 1`] = `
|
||||
"
|
||||
<div class=\\"doc-name\\">COUNT</div>
|
||||
<div class=\\"doc-syntax\\">COUNT(*)</div>
|
||||
<div class=\\"doc-description\\">Counts the number of things</div>"
|
||||
`;
|
||||
|
||||
exports[`query input matches snapshot 1`] = `
|
||||
exports[`QueryInput matches snapshot 1`] = `
|
||||
<div
|
||||
class="query-input"
|
||||
>
|
||||
|
@ -21,7 +21,7 @@ import React from 'react';
|
||||
|
||||
import { QueryInput } from './query-input';
|
||||
|
||||
describe('query input', () => {
|
||||
describe('QueryInput', () => {
|
||||
it('matches snapshot', () => {
|
||||
const sqlControl = (
|
||||
<QueryInput queryString="hello world" onQueryStringChange={() => {}} runeMode={false} />
|
||||
|
@ -16,7 +16,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IResizeEntry, ResizeSensor } from '@blueprintjs/core';
|
||||
import { ResizeEntry } from '@blueprintjs/core';
|
||||
import { ResizeSensor2 } from '@blueprintjs/popover2';
|
||||
import ace, { Editor } from 'brace';
|
||||
import escape from 'lodash.escape';
|
||||
import React from 'react';
|
||||
@ -198,7 +199,7 @@ export class QueryInput extends React.PureComponent<QueryInputProps, QueryInputS
|
||||
}
|
||||
}
|
||||
|
||||
private readonly handleAceContainerResize = (entries: IResizeEntry[]) => {
|
||||
private readonly handleAceContainerResize = (entries: ResizeEntry[]) => {
|
||||
if (entries.length !== 1) return;
|
||||
this.setState({ editorHeight: entries[0].contentRect.height });
|
||||
};
|
||||
@ -228,7 +229,7 @@ export class QueryInput extends React.PureComponent<QueryInputProps, QueryInputS
|
||||
// Set the key in the AceEditor to force a rebind and prevent an error that happens otherwise
|
||||
return (
|
||||
<div className="query-input">
|
||||
<ResizeSensor onResize={this.handleAceContainerResize}>
|
||||
<ResizeSensor2 onResize={this.handleAceContainerResize}>
|
||||
<div className="ace-container">
|
||||
<AceEditor
|
||||
mode={runeMode ? 'hjson' : 'dsql'}
|
||||
@ -259,7 +260,7 @@ export class QueryInput extends React.PureComponent<QueryInputProps, QueryInputS
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ResizeSensor>
|
||||
</ResizeSensor2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -29,36 +29,32 @@ exports[`query output matches snapshot 1`] = `
|
||||
class="rt-resizable-header-content"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper clickable-cell"
|
||||
class="clickable-cell bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
language
|
||||
<span
|
||||
class="bp3-icon bp3-icon-filter"
|
||||
icon="filter"
|
||||
>
|
||||
language
|
||||
<span
|
||||
class="bp3-icon bp3-icon-filter"
|
||||
icon="filter"
|
||||
<svg
|
||||
data-icon="filter"
|
||||
height="14"
|
||||
viewBox="0 0 16 16"
|
||||
width="14"
|
||||
>
|
||||
<svg
|
||||
data-icon="filter"
|
||||
height="14"
|
||||
viewBox="0 0 16 16"
|
||||
width="14"
|
||||
>
|
||||
<desc>
|
||||
filter
|
||||
</desc>
|
||||
<path
|
||||
d="M13.99.99h-12a1.003 1.003 0 00-.71 1.71l4.71 4.71V14a1.003 1.003 0 001.71.71l2-2c.18-.18.29-.43.29-.71V7.41L14.7 2.7a1.003 1.003 0 00-.71-1.71z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<desc>
|
||||
filter
|
||||
</desc>
|
||||
<path
|
||||
d="M13.99.99h-12a1.003 1.003 0 00-.71 1.71l4.71 4.71V14a1.003 1.003 0 001.71.71l2-2c.18-.18.29-.43.29-.71V7.41L14.7 2.7a1.003 1.003 0 00-.71-1.71z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@ -75,36 +71,32 @@ exports[`query output matches snapshot 1`] = `
|
||||
class="rt-resizable-header-content"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper clickable-cell"
|
||||
class="clickable-cell bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
Count
|
||||
<span
|
||||
class="bp3-icon bp3-icon-filter"
|
||||
icon="filter"
|
||||
>
|
||||
Count
|
||||
<span
|
||||
class="bp3-icon bp3-icon-filter"
|
||||
icon="filter"
|
||||
<svg
|
||||
data-icon="filter"
|
||||
height="14"
|
||||
viewBox="0 0 16 16"
|
||||
width="14"
|
||||
>
|
||||
<svg
|
||||
data-icon="filter"
|
||||
height="14"
|
||||
viewBox="0 0 16 16"
|
||||
width="14"
|
||||
>
|
||||
<desc>
|
||||
filter
|
||||
</desc>
|
||||
<path
|
||||
d="M13.99.99h-12a1.003 1.003 0 00-.71 1.71l4.71 4.71V14a1.003 1.003 0 001.71.71l2-2c.18-.18.29-.43.29-.71V7.41L14.7 2.7a1.003 1.003 0 00-.71-1.71z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
<desc>
|
||||
filter
|
||||
</desc>
|
||||
<path
|
||||
d="M13.99.99h-12a1.003 1.003 0 00-.71 1.71l4.71 4.71V14a1.003 1.003 0 001.71.71l2-2c.18-.18.29-.43.29-.71V7.41L14.7 2.7a1.003 1.003 0 00-.71-1.71z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@ -121,17 +113,13 @@ exports[`query output matches snapshot 1`] = `
|
||||
class="rt-resizable-header-content"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper clickable-cell"
|
||||
class="clickable-cell bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
dist_language
|
||||
</div>
|
||||
</span>
|
||||
dist_language
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@ -148,17 +136,13 @@ exports[`query output matches snapshot 1`] = `
|
||||
class="rt-resizable-header-content"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-wrapper clickable-cell"
|
||||
class="clickable-cell bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
<div
|
||||
class=""
|
||||
>
|
||||
language_filtered_count
|
||||
</div>
|
||||
</span>
|
||||
language_filtered_count
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@ -186,16 +170,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell null"
|
||||
>
|
||||
<span
|
||||
class="table-cell null"
|
||||
>
|
||||
null
|
||||
</span>
|
||||
null
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -207,16 +187,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
6881
|
||||
</span>
|
||||
6881
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -228,16 +204,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
1
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -249,16 +221,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
0
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -280,16 +248,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
JavaScript
|
||||
</span>
|
||||
JavaScript
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -301,16 +265,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
166
|
||||
</span>
|
||||
166
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -322,16 +282,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
1
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -343,16 +299,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
0
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -374,16 +326,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
Python
|
||||
</span>
|
||||
Python
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -395,16 +343,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
62
|
||||
</span>
|
||||
62
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -416,16 +360,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
1
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -437,16 +377,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
0
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -468,16 +404,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
HTML
|
||||
</span>
|
||||
HTML
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -489,16 +421,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
46
|
||||
</span>
|
||||
46
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -510,16 +438,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
1
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -531,16 +455,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell plain"
|
||||
>
|
||||
<span
|
||||
class="table-cell plain"
|
||||
>
|
||||
0
|
||||
</span>
|
||||
0
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -562,16 +482,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell null"
|
||||
>
|
||||
<span
|
||||
class="table-cell null"
|
||||
>
|
||||
null
|
||||
</span>
|
||||
null
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -583,16 +499,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell null"
|
||||
>
|
||||
<span
|
||||
class="table-cell null"
|
||||
>
|
||||
null
|
||||
</span>
|
||||
null
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -604,16 +516,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell null"
|
||||
>
|
||||
<span
|
||||
class="table-cell null"
|
||||
>
|
||||
null
|
||||
</span>
|
||||
null
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@ -625,16 +533,12 @@ exports[`query output matches snapshot 1`] = `
|
||||
>
|
||||
<div>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
class="table-cell null"
|
||||
>
|
||||
<span
|
||||
class="table-cell null"
|
||||
>
|
||||
null
|
||||
</span>
|
||||
null
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -69,7 +69,7 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.bp3-popover-target {
|
||||
.bp3-popover2-target {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
@ -16,8 +16,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Icon, Menu, MenuItem, Popover } from '@blueprintjs/core';
|
||||
import { Icon, Menu, MenuItem } from '@blueprintjs/core';
|
||||
import { IconName, IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
QueryResult,
|
||||
@ -396,14 +397,14 @@ export const QueryOutput = React.memo(function QueryOutput(props: QueryOutputPro
|
||||
? () => <ColumnRenameInput initialName={h} onDone={renameColumnTo} />
|
||||
: () => {
|
||||
return (
|
||||
<Popover className="clickable-cell" content={getHeaderMenu(h, i)}>
|
||||
<Popover2 className="clickable-cell" content={getHeaderMenu(h, i)}>
|
||||
<div>
|
||||
{h}
|
||||
{hasFilterOnHeader(h, i) && (
|
||||
<Icon icon={IconNames.FILTER} iconSize={14} />
|
||||
)}
|
||||
</div>
|
||||
</Popover>
|
||||
</Popover2>
|
||||
);
|
||||
},
|
||||
headerClassName: getHeaderClassName(h, i),
|
||||
@ -412,7 +413,7 @@ export const QueryOutput = React.memo(function QueryOutput(props: QueryOutputPro
|
||||
const value = row.value;
|
||||
return (
|
||||
<div>
|
||||
<Popover content={getCellMenu(h, i, value)}>
|
||||
<Popover2 content={getCellMenu(h, i, value)}>
|
||||
{numericColumnBraces[i] ? (
|
||||
<BracedText
|
||||
text={String(value)}
|
||||
@ -422,7 +423,7 @@ export const QueryOutput = React.memo(function QueryOutput(props: QueryOutputPro
|
||||
) : (
|
||||
<TableCell value={value} unlimited />
|
||||
)}
|
||||
</Popover>
|
||||
</Popover2>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -32,14 +32,6 @@ describe('QueryView', () => {
|
||||
expect(sqlView).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('.trimSemicolon', () => {
|
||||
expect(QueryView.trimSemicolon('SELECT * FROM tbl;')).toEqual('SELECT * FROM tbl');
|
||||
expect(QueryView.trimSemicolon('SELECT * FROM tbl; ')).toEqual('SELECT * FROM tbl ');
|
||||
expect(QueryView.trimSemicolon('SELECT * FROM tbl; --hello ')).toEqual(
|
||||
'SELECT * FROM tbl --hello ',
|
||||
);
|
||||
});
|
||||
|
||||
it('.formatStr', () => {
|
||||
expect(QueryView.formatStr(null, 'csv')).toEqual('"null"');
|
||||
expect(QueryView.formatStr('hello\nworld', 'csv')).toEqual('"hello world"');
|
||||
|
@ -16,7 +16,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Code, Intent, Switch, Tooltip } from '@blueprintjs/core';
|
||||
import { Code, Intent, Switch } from '@blueprintjs/core';
|
||||
import { Tooltip2 } from '@blueprintjs/popover2';
|
||||
import classNames from 'classnames';
|
||||
import { QueryResult, QueryRunner, SqlQuery } from 'druid-query-toolkit';
|
||||
import Hjson from 'hjson';
|
||||
@ -26,34 +27,31 @@ import React, { RefObject } from 'react';
|
||||
import SplitterLayout from 'react-splitter-layout';
|
||||
|
||||
import { Loader } from '../../components';
|
||||
import { QueryPlanDialog } from '../../dialogs';
|
||||
import { EditContextDialog } from '../../dialogs/edit-context-dialog/edit-context-dialog';
|
||||
import { QueryHistoryDialog } from '../../dialogs/query-history-dialog/query-history-dialog';
|
||||
import { Api, AppToaster } from '../../singletons';
|
||||
import {
|
||||
BasicQueryExplanation,
|
||||
ColumnMetadata,
|
||||
downloadFile,
|
||||
DruidError,
|
||||
findEmptyLiteralPosition,
|
||||
getDruidErrorMessage,
|
||||
localStorageGet,
|
||||
localStorageGetJson,
|
||||
LocalStorageKeys,
|
||||
localStorageSet,
|
||||
localStorageSetJson,
|
||||
parseQueryPlan,
|
||||
queryDruidSql,
|
||||
QueryManager,
|
||||
QueryState,
|
||||
QueryWithContext,
|
||||
RowColumn,
|
||||
SemiJoinQueryExplanation,
|
||||
stringifyValue,
|
||||
} from '../../utils';
|
||||
import { isEmptyContext, QueryContext } from '../../utils/query-context';
|
||||
import { QueryRecord, QueryRecordUtil } from '../../utils/query-history';
|
||||
|
||||
import { ColumnTree } from './column-tree/column-tree';
|
||||
import { ExplainDialog } from './explain-dialog/explain-dialog';
|
||||
import {
|
||||
LIVE_QUERY_MODES,
|
||||
LiveQueryMode,
|
||||
@ -76,12 +74,6 @@ const parser = memoizeOne((sql: string): SqlQuery | undefined => {
|
||||
}
|
||||
});
|
||||
|
||||
interface QueryWithContext {
|
||||
queryString: string;
|
||||
queryContext: QueryContext;
|
||||
wrapQueryLimit: number | undefined;
|
||||
}
|
||||
|
||||
export interface QueryViewProps {
|
||||
initQuery: string | undefined;
|
||||
defaultQueryContext?: Record<string, any>;
|
||||
@ -99,8 +91,7 @@ export interface QueryViewState {
|
||||
|
||||
queryResultState: QueryState<QueryResult, DruidError>;
|
||||
|
||||
explainDialogOpen: boolean;
|
||||
explainResultState: QueryState<BasicQueryExplanation | SemiJoinQueryExplanation | string>;
|
||||
explainDialogQuery?: QueryWithContext;
|
||||
|
||||
defaultSchema?: string;
|
||||
defaultTable?: string;
|
||||
@ -111,25 +102,10 @@ export interface QueryViewState {
|
||||
}
|
||||
|
||||
export class QueryView extends React.PureComponent<QueryViewProps, QueryViewState> {
|
||||
static trimSemicolon(query: string): string {
|
||||
// Trims out a trailing semicolon while preserving space (https://bit.ly/1n1yfkJ)
|
||||
return query.replace(/;+((?:\s*--[^\n]*)?\s*)$/, '$1');
|
||||
}
|
||||
|
||||
static isEmptyQuery(query: string): boolean {
|
||||
return query.trim() === '';
|
||||
}
|
||||
|
||||
static isExplainQuery(query: string): boolean {
|
||||
return /EXPLAIN\sPLAN\sFOR/i.test(query);
|
||||
}
|
||||
|
||||
static wrapInExplainIfNeeded(query: string): string {
|
||||
query = QueryView.trimSemicolon(query);
|
||||
if (QueryView.isExplainQuery(query)) return query;
|
||||
return `EXPLAIN PLAN FOR (${query}\n)`;
|
||||
}
|
||||
|
||||
static isJsonLike(queryString: string): boolean {
|
||||
return queryString.trim().startsWith('{');
|
||||
}
|
||||
@ -158,10 +134,6 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
|
||||
private readonly metadataQueryManager: QueryManager<null, ColumnMetadata[]>;
|
||||
private readonly queryManager: QueryManager<QueryWithContext, QueryResult>;
|
||||
private readonly explainQueryManager: QueryManager<
|
||||
QueryWithContext,
|
||||
BasicQueryExplanation | SemiJoinQueryExplanation | string
|
||||
>;
|
||||
|
||||
private readonly queryInputRef: RefObject<QueryInput>;
|
||||
|
||||
@ -196,9 +168,6 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
|
||||
queryResultState: QueryState.INIT,
|
||||
|
||||
explainDialogOpen: false,
|
||||
explainResultState: QueryState.INIT,
|
||||
|
||||
editContextDialogOpen: false,
|
||||
historyDialogOpen: false,
|
||||
queryHistory,
|
||||
@ -260,37 +229,6 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
this.explainQueryManager = new QueryManager({
|
||||
processQuery: async (queryWithContext: QueryWithContext) => {
|
||||
const { queryString, queryContext, wrapQueryLimit } = queryWithContext;
|
||||
|
||||
let context: Record<string, any> | undefined;
|
||||
if (!isEmptyContext(queryContext) || wrapQueryLimit || mandatoryQueryContext) {
|
||||
context = { ...queryContext, ...(mandatoryQueryContext || {}) };
|
||||
if (typeof wrapQueryLimit !== 'undefined') {
|
||||
context.sqlOuterLimit = wrapQueryLimit + 1;
|
||||
}
|
||||
}
|
||||
|
||||
let result: QueryResult | undefined;
|
||||
try {
|
||||
result = await queryRunner.runQuery({
|
||||
query: QueryView.wrapInExplainIfNeeded(queryString),
|
||||
extraQueryContext: context,
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(getDruidErrorMessage(e));
|
||||
}
|
||||
|
||||
return parseQueryPlan(result.rows[0][0]);
|
||||
},
|
||||
onStateChange: explainResultState => {
|
||||
this.setState({
|
||||
explainResultState,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
@ -307,10 +245,9 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
componentWillUnmount(): void {
|
||||
this.metadataQueryManager.terminate();
|
||||
this.queryManager.terminate();
|
||||
this.explainQueryManager.terminate();
|
||||
}
|
||||
|
||||
prettyPrintJson(): void {
|
||||
private prettyPrintJson(): void {
|
||||
this.setState(prevState => {
|
||||
let parsed: any;
|
||||
try {
|
||||
@ -367,21 +304,22 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
);
|
||||
};
|
||||
|
||||
renderExplainDialog() {
|
||||
const { explainDialogOpen, explainResultState } = this.state;
|
||||
if (explainResultState.loading || !explainDialogOpen) return;
|
||||
private renderExplainDialog() {
|
||||
const { mandatoryQueryContext } = this.props;
|
||||
const { explainDialogQuery } = this.state;
|
||||
if (!explainDialogQuery) return;
|
||||
|
||||
return (
|
||||
<QueryPlanDialog
|
||||
explainResult={explainResultState.data}
|
||||
explainError={explainResultState.error}
|
||||
<ExplainDialog
|
||||
queryWithContext={explainDialogQuery}
|
||||
mandatoryQueryContext={mandatoryQueryContext}
|
||||
setQueryString={this.handleQueryStringChange}
|
||||
onClose={() => this.setState({ explainDialogOpen: false })}
|
||||
onClose={() => this.setState({ explainDialogQuery: undefined })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
renderHistoryDialog() {
|
||||
private renderHistoryDialog() {
|
||||
const { historyDialogOpen, queryHistory } = this.state;
|
||||
if (!historyDialogOpen) return;
|
||||
|
||||
@ -397,7 +335,7 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
);
|
||||
}
|
||||
|
||||
renderEditContextDialog() {
|
||||
private renderEditContextDialog() {
|
||||
const { editContextDialogOpen, queryContext } = this.state;
|
||||
if (!editContextDialogOpen) return;
|
||||
|
||||
@ -412,7 +350,7 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
);
|
||||
}
|
||||
|
||||
renderLiveQueryModeSelector() {
|
||||
private renderLiveQueryModeSelector() {
|
||||
const { liveQueryMode, queryString } = this.state;
|
||||
if (QueryView.isJsonLike(queryString)) return;
|
||||
|
||||
@ -425,12 +363,12 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
);
|
||||
}
|
||||
|
||||
renderWrapQueryLimitSelector() {
|
||||
private renderWrapQueryLimitSelector() {
|
||||
const { wrapQueryLimit, queryString } = this.state;
|
||||
if (QueryView.isJsonLike(queryString)) return;
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
<Tooltip2
|
||||
content="Automatically wrap the query with a limit to protect against queries with very large result sets."
|
||||
hoverOpenDelay={800}
|
||||
>
|
||||
@ -440,11 +378,11 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
label="Auto limit"
|
||||
onChange={() => this.handleWrapQueryLimitChange(wrapQueryLimit ? undefined : 100)}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Tooltip2>
|
||||
);
|
||||
}
|
||||
|
||||
renderMainArea() {
|
||||
private renderMainArea() {
|
||||
const { queryString, queryContext, queryResultState, columnMetadataState } = this.state;
|
||||
const emptyQuery = QueryView.isEmptyQuery(queryString);
|
||||
const queryResult = queryResultState.data;
|
||||
@ -636,11 +574,12 @@ export class QueryView extends React.PureComponent<QueryViewProps, QueryViewStat
|
||||
private readonly handleExplain = () => {
|
||||
const { queryString, queryContext, wrapQueryLimit } = this.state;
|
||||
|
||||
this.setState({ explainDialogOpen: true });
|
||||
this.explainQueryManager.runQuery({
|
||||
queryString,
|
||||
queryContext,
|
||||
wrapQueryLimit,
|
||||
this.setState({
|
||||
explainDialogQuery: {
|
||||
queryString,
|
||||
queryContext,
|
||||
wrapQueryLimit,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -36,36 +36,32 @@ exports[`run button matches snapshot loading state 1`] = `
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button bp3-intent-primary"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button bp3-intent-primary"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-more"
|
||||
icon="more"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-more"
|
||||
icon="more"
|
||||
<svg
|
||||
data-icon="more"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="more"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
more
|
||||
</desc>
|
||||
<path
|
||||
d="M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
more
|
||||
</desc>
|
||||
<path
|
||||
d="M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
@ -104,36 +100,32 @@ exports[`run button matches snapshot non-loading state 1`] = `
|
||||
</span>
|
||||
</button>
|
||||
<span
|
||||
class="bp3-popover-wrapper"
|
||||
class="bp3-popover2-target"
|
||||
>
|
||||
<span
|
||||
class="bp3-popover-target"
|
||||
<button
|
||||
class="bp3-button bp3-intent-primary"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="bp3-button bp3-intent-primary"
|
||||
type="button"
|
||||
<span
|
||||
class="bp3-icon bp3-icon-more"
|
||||
icon="more"
|
||||
>
|
||||
<span
|
||||
class="bp3-icon bp3-icon-more"
|
||||
icon="more"
|
||||
<svg
|
||||
data-icon="more"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<svg
|
||||
data-icon="more"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
width="16"
|
||||
>
|
||||
<desc>
|
||||
more
|
||||
</desc>
|
||||
<path
|
||||
d="M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
<desc>
|
||||
more
|
||||
</desc>
|
||||
<path
|
||||
d="M2 6.03a2 2 0 100 4 2 2 0 100-4zM14 6.03a2 2 0 100 4 2 2 0 100-4zM8 6.03a2 2 0 100 4 2 2 0 100-4z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
@ -23,11 +23,11 @@ import {
|
||||
Menu,
|
||||
MenuDivider,
|
||||
MenuItem,
|
||||
Popover,
|
||||
Position,
|
||||
useHotkeys,
|
||||
} from '@blueprintjs/core';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { Popover2 } from '@blueprintjs/popover2';
|
||||
import React from 'react';
|
||||
|
||||
import { MenuCheckbox } from '../../../components';
|
||||
@ -127,7 +127,7 @@ const RunButtonExtraMenu = (props: RunButtonProps) => {
|
||||
};
|
||||
|
||||
export const RunButton = React.memo(function RunButton(props: RunButtonProps) {
|
||||
const { runeMode, onRun, loading } = props;
|
||||
const { runeMode, onRun, loading, onExplain } = props;
|
||||
|
||||
const handleRun = React.useCallback(() => {
|
||||
if (!onRun) return;
|
||||
@ -139,12 +139,29 @@ export const RunButton = React.memo(function RunButton(props: RunButtonProps) {
|
||||
{
|
||||
allowInInput: true,
|
||||
global: true,
|
||||
combo: 'ctrl + enter',
|
||||
group: 'Query',
|
||||
combo: 'mod + enter',
|
||||
label: 'Runs the current query',
|
||||
onKeyDown: handleRun,
|
||||
},
|
||||
{
|
||||
allowInInput: true,
|
||||
global: true,
|
||||
group: 'Query',
|
||||
combo: 'mod + e',
|
||||
label: 'Explain the current query',
|
||||
onKeyDown: onExplain,
|
||||
},
|
||||
{
|
||||
allowInInput: true,
|
||||
global: true,
|
||||
group: 'X-Legacy', // This is prefixed with X so it appears in the bottom of the list
|
||||
combo: 'ctrl + enter',
|
||||
label: 'Runs the current query (old shortcut)',
|
||||
onKeyDown: handleRun,
|
||||
},
|
||||
];
|
||||
}, [handleRun]);
|
||||
}, [handleRun, onExplain]);
|
||||
|
||||
useHotkeys(hotkeys);
|
||||
|
||||
@ -167,13 +184,13 @@ export const RunButton = React.memo(function RunButton(props: RunButtonProps) {
|
||||
disabled
|
||||
/>
|
||||
)}
|
||||
<Popover position={Position.BOTTOM_LEFT} content={<RunButtonExtraMenu {...props} />}>
|
||||
<Popover2 position={Position.BOTTOM_LEFT} content={<RunButtonExtraMenu {...props} />}>
|
||||
<Button
|
||||
className={runeMode ? 'rune-button' : undefined}
|
||||
icon={IconNames.MORE}
|
||||
intent={onRun ? Intent.PRIMARY : undefined}
|
||||
/>
|
||||
</Popover>
|
||||
</Popover2>
|
||||
</ButtonGroup>
|
||||
);
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user