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:
Evan Ren 2019-09-17 14:26:51 -07:00 committed by David Lim
parent 7dcbaca658
commit 8650ee9fd0
4 changed files with 107 additions and 140 deletions

View File

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

View File

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

View File

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

View File

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