mirror of https://github.com/apache/druid.git
Web console: Druid status displayed in a table (#8484)
* Retrieved data from endpoint and displayed on table * Added view raw button, removed json, and fixed formatting for table * Remove error var * Fixed snapshot for updated status dialog * Made changes based on PR review * Added version tag * Updated snapshot to match changes * Made more changes based on review * Fix filter and formatting * Fix filter and add unit test * fix styling of dialog * Fix footer height * Fixed testing of filtering
This commit is contained in:
parent
7dcbaca658
commit
8650ee9fd0
|
@ -1,132 +1,3 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`status 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 status-dialog"
|
||||
>
|
||||
<div
|
||||
class="bp3-dialog-header"
|
||||
>
|
||||
<h4
|
||||
class="bp3-heading"
|
||||
>
|
||||
Status
|
||||
</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="status-dialog-main-area"
|
||||
>
|
||||
<div
|
||||
class="show-json"
|
||||
>
|
||||
<div
|
||||
class="top-actions"
|
||||
>
|
||||
<div
|
||||
class="bp3-button-group right-buttons"
|
||||
>
|
||||
<button
|
||||
class="bp3-button bp3-disabled bp3-minimal"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Save
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="bp3-button bp3-disabled bp3-minimal"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Copy
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="bp3-button bp3-disabled bp3-minimal"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
View raw
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="main-area"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="bp3-dialog-footer"
|
||||
>
|
||||
<div
|
||||
class="bp3-dialog-footer-actions"
|
||||
>
|
||||
<button
|
||||
class="bp3-button bp3-intent-primary"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
class="bp3-button-text"
|
||||
>
|
||||
Close
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
exports[`status dialog matches snapshot 1`] = `<div />`;
|
||||
|
|
|
@ -22,16 +22,23 @@ $side-bar-width: 120px;
|
|||
margin-top: 5vh;
|
||||
top: 5%;
|
||||
width: 60vw;
|
||||
|
||||
max-height: 95vh;
|
||||
.status-dialog-main-area {
|
||||
height: 60vh;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.bp3-dialog-footer {
|
||||
position: relative;
|
||||
padding-top: 5px;
|
||||
|
||||
display: inline;
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
.viewRawButton {
|
||||
float: left;
|
||||
}
|
||||
.closeButton {
|
||||
float: right;
|
||||
}
|
||||
.footer-actions-left {
|
||||
position: absolute;
|
||||
left: $side-bar-width;
|
||||
|
|
|
@ -27,4 +27,18 @@ describe('status dialog', () => {
|
|||
render(statusDialog);
|
||||
expect(document.body.lastChild).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('filters data that contains input', () => {
|
||||
const data = [
|
||||
'org.apache.druid.common.gcp.GcpModule',
|
||||
'org.apache.druid.common.aws.AWSModule',
|
||||
'io.imply.druid.UtilityBeltModule',
|
||||
];
|
||||
|
||||
expect(StatusDialog.anywhereMatcher({ id: '0', value: 'common' }, data)).toEqual(true);
|
||||
expect(StatusDialog.anywhereMatcher({ id: '1', value: 'common' }, data)).toEqual(true);
|
||||
expect(StatusDialog.anywhereMatcher({ id: '0', value: 'org' }, data)).toEqual(true);
|
||||
expect(StatusDialog.anywhereMatcher({ id: '1', value: 'org' }, data)).toEqual(true);
|
||||
expect(StatusDialog.anywhereMatcher({ id: '2', value: 'common' }, data)).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -16,11 +16,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Button, Classes, Dialog, Intent } from '@blueprintjs/core';
|
||||
import { Button, Classes, Dialog, FormGroup, InputGroup, Intent } from '@blueprintjs/core';
|
||||
import axios from 'axios';
|
||||
import React from 'react';
|
||||
import ReactTable, { Filter } from 'react-table';
|
||||
|
||||
import { ShowJson } from '../../components';
|
||||
import { Loader } from '../../components/loader/loader';
|
||||
import { UrlBaser } from '../../singletons/url-baser';
|
||||
import { QueryManager } from '../../utils';
|
||||
|
||||
import './status-dialog.scss';
|
||||
|
||||
|
@ -28,17 +31,89 @@ interface StatusDialogProps {
|
|||
onClose: () => void;
|
||||
}
|
||||
|
||||
export class StatusDialog extends React.PureComponent<StatusDialogProps> {
|
||||
interface StatusDialogState {
|
||||
response: any;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export class StatusDialog extends React.PureComponent<StatusDialogProps, StatusDialogState> {
|
||||
static anywhereMatcher(filter: Filter, row: any) {
|
||||
return String(row[filter.id]).includes(filter.value);
|
||||
}
|
||||
|
||||
private showStatusQueryManager: QueryManager<null, any>;
|
||||
constructor(props: StatusDialogProps, context: any) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
response: [],
|
||||
loading: false,
|
||||
};
|
||||
this.showStatusQueryManager = new QueryManager({
|
||||
processQuery: async () => {
|
||||
const endpoint = UrlBaser.base(`/status`);
|
||||
const resp = await axios.get(endpoint);
|
||||
return resp.data;
|
||||
},
|
||||
onStateChange: ({ result, loading }) => {
|
||||
this.setState({
|
||||
loading,
|
||||
response: result,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this.showStatusQueryManager.runQuery(null);
|
||||
}
|
||||
|
||||
render(): JSX.Element {
|
||||
const { onClose } = this.props;
|
||||
|
||||
const { response, loading } = this.state;
|
||||
if (loading) return <Loader />;
|
||||
return (
|
||||
<Dialog className={'status-dialog'} onClose={onClose} isOpen title="Status">
|
||||
<div className={'status-dialog-main-area'}>
|
||||
<ShowJson endpoint={UrlBaser.base(`/status`)} downloadFilename={'status'} />
|
||||
<FormGroup label="Version" labelFor="version" inline>
|
||||
<InputGroup id="version" defaultValue={response.version} readOnly />
|
||||
</FormGroup>
|
||||
<ReactTable
|
||||
data={response.modules}
|
||||
columns={[
|
||||
{
|
||||
columns: [
|
||||
{
|
||||
Header: 'Extension name',
|
||||
accessor: 'artifact',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
Header: 'Fully qualified name',
|
||||
accessor: 'name',
|
||||
},
|
||||
{
|
||||
Header: 'Version',
|
||||
accessor: 'version',
|
||||
width: 200,
|
||||
},
|
||||
],
|
||||
},
|
||||
]}
|
||||
loading={loading}
|
||||
filterable
|
||||
defaultFilterMethod={StatusDialog.anywhereMatcher}
|
||||
/>
|
||||
</div>
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
<div className={Classes.DIALOG_FOOTER_ACTIONS}>
|
||||
<div className="viewRawButton">
|
||||
<Button
|
||||
text="View raw"
|
||||
disabled={!response}
|
||||
minimal
|
||||
onClick={() => window.open(UrlBaser.base(UrlBaser.base(`/status`)), '_blank')}
|
||||
/>
|
||||
</div>
|
||||
<div className="closeButton">
|
||||
<Button text="Close" intent={Intent.PRIMARY} onClick={onClose} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue