mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 06:51:10 +03:00
WIP - react virtualizer
This commit is contained in:
parent
859c364fbe
commit
da92889323
76
awx/ui_next/package-lock.json
generated
76
awx/ui_next/package-lock.json
generated
@ -4576,6 +4576,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"clsx": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.0.4.tgz",
|
||||
"integrity": "sha512-1mQ557MIZTrL/140j+JVdRM6e31/OA4vTYxXgqIIZlndyfjHpyawKZia1Im05Vp9BWmImkcNrNtFYQMyFcgJDg=="
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@ -5429,6 +5434,14 @@
|
||||
"esutils": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"dom-helpers": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
|
||||
"integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2"
|
||||
}
|
||||
},
|
||||
"dom-serializer": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
|
||||
@ -7174,7 +7187,8 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@ -7195,12 +7209,14 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@ -7215,17 +7231,20 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@ -7342,7 +7361,8 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@ -7354,6 +7374,7 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@ -7368,6 +7389,7 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@ -7375,12 +7397,14 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.3.5",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.2",
|
||||
"yallist": "^3.0.0"
|
||||
@ -7399,6 +7423,7 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@ -7479,7 +7504,8 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@ -7491,6 +7517,7 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@ -7576,7 +7603,8 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@ -7612,6 +7640,7 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@ -7631,6 +7660,7 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@ -7674,12 +7704,14 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.3",
|
||||
"bundled": true,
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -10790,6 +10822,11 @@
|
||||
"type-check": "~0.3.2"
|
||||
}
|
||||
},
|
||||
"linear-layout-vector": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/linear-layout-vector/-/linear-layout-vector-0.0.1.tgz",
|
||||
"integrity": "sha1-OYEU1zA7bsx/1rJzr3uEAdi6nHA="
|
||||
},
|
||||
"load-json-file": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
|
||||
@ -13130,8 +13167,7 @@
|
||||
"react-lifecycles-compat": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
|
||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
|
||||
},
|
||||
"react-router": {
|
||||
"version": "4.3.1",
|
||||
@ -13190,6 +13226,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-virtualized": {
|
||||
"version": "9.21.1",
|
||||
"resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.21.1.tgz",
|
||||
"integrity": "sha512-E53vFjRRMCyUTEKuDLuGH1ld/9TFzjf/fFW816PE4HFXWZorESbSTYtiZz1oAjra0MminaUU1EnvUxoGuEFFPA==",
|
||||
"requires": {
|
||||
"babel-runtime": "^6.26.0",
|
||||
"clsx": "^1.0.1",
|
||||
"dom-helpers": "^2.4.0 || ^3.0.0",
|
||||
"linear-layout-vector": "0.0.1",
|
||||
"loose-envify": "^1.3.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-lifecycles-compat": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"read-pkg": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
|
||||
|
@ -70,6 +70,7 @@
|
||||
"react-codemirror2": "^6.0.0",
|
||||
"react-dom": "^16.4.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-virtualized": "^9.21.1",
|
||||
"styled-components": "^4.2.0"
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,12 @@ class Jobs extends Base {
|
||||
readDetail(id, type) {
|
||||
return this.http.get(`/api/v2${BASE_URLS[type]}${id}/`);
|
||||
}
|
||||
|
||||
readJobEvents(id, params = {}) {
|
||||
return this.http.get(`${this.baseUrl}${id}/job_events/`, {
|
||||
params,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default Jobs;
|
||||
|
@ -1,31 +1,154 @@
|
||||
import React, { Component } from 'react';
|
||||
import { CardBody } from '@patternfly/react-core';
|
||||
import MenuControls from './shared/MenuControls';
|
||||
import styled from 'styled-components';
|
||||
import { JobsAPI } from '@api';
|
||||
import ContentError from '@components/ContentError';
|
||||
import ContentLoading from '@components/ContentLoading';
|
||||
import MenuControls from './shared/MenuControls';
|
||||
import { List, AutoSizer } from 'react-virtualized';
|
||||
|
||||
const OutputToolbar = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
const OutputWrapper = styled.div`
|
||||
height: calc(100vh - 325px);
|
||||
background-color: #fafafa;
|
||||
margin-top: 24px;
|
||||
`;
|
||||
const OutputRow = styled.div`
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
class JobOutput extends Component {
|
||||
listRef = React.createRef();
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
contentError: null,
|
||||
hasContentLoading: true,
|
||||
results: [],
|
||||
scrollToIndex: -1,
|
||||
startIndex: 0,
|
||||
stopIndex: 0,
|
||||
};
|
||||
|
||||
this.loadJobEvents = this.loadJobEvents.bind(this);
|
||||
this.renderRow = this.renderRow.bind(this);
|
||||
this.handleScrollTop = this.handleScrollTop.bind(this);
|
||||
this.handleScrollBottom = this.handleScrollBottom.bind(this);
|
||||
this.handleScrollNext = this.handleScrollNext.bind(this);
|
||||
this.handleScrollPrevious = this.handleScrollPrevious.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadJobEvents();
|
||||
}
|
||||
|
||||
async loadJobEvents() {
|
||||
const { job } = this.props;
|
||||
|
||||
try {
|
||||
const {
|
||||
data: { results = [] },
|
||||
} = await JobsAPI.readJobEvents(job.id);
|
||||
this.setState({ results, hasContentLoading: true });
|
||||
} catch (err) {
|
||||
this.setState({ contentError: err });
|
||||
} finally {
|
||||
this.setState({ hasContentLoading: false });
|
||||
}
|
||||
}
|
||||
|
||||
renderRow({ index, key, style }) {
|
||||
const { results } = this.state;
|
||||
return (
|
||||
<OutputRow key={key} style={style} className="row">
|
||||
<div className="id">{results[index].id}</div>
|
||||
<div className="content">{results[index].stdout}</div>
|
||||
</OutputRow>
|
||||
);
|
||||
}
|
||||
|
||||
onRowsRendered = ({ startIndex, stopIndex }) => {
|
||||
this.setState({ startIndex, stopIndex });
|
||||
};
|
||||
|
||||
handleScrollPrevious() {
|
||||
const { startIndex, stopIndex } = this.state;
|
||||
const index = startIndex - (stopIndex - startIndex);
|
||||
this.setState({ scrollToIndex: Math.max(0, index) });
|
||||
}
|
||||
|
||||
handleScrollNext() {
|
||||
const { stopIndex } = this.state;
|
||||
this.setState({ scrollToIndex: stopIndex + 1});
|
||||
}
|
||||
|
||||
handleScrollTop() {
|
||||
this.setState({ scrollToIndex: 0 });
|
||||
}
|
||||
|
||||
handleScrollBottom() {
|
||||
const { results } = this.state;
|
||||
this.setState({ scrollToIndex: results.length - 1 });
|
||||
}
|
||||
|
||||
render() {
|
||||
const { job } = this.props;
|
||||
const {
|
||||
results,
|
||||
hasContentLoading,
|
||||
contentError,
|
||||
scrollToIndex,
|
||||
startIndex,
|
||||
stopIndex
|
||||
} = this.state;
|
||||
|
||||
if (hasContentLoading) {
|
||||
return <ContentLoading />;
|
||||
}
|
||||
|
||||
if (contentError) {
|
||||
return <ContentError error={contentError} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<CardBody>
|
||||
<b>{job.name}</b>
|
||||
{/*Heading and Job Stats */}
|
||||
{/*Host Status Bar */}
|
||||
<OutputToolbar>
|
||||
{/* Filter and Pagination */}
|
||||
<b>Filter placeholder</b>
|
||||
<MenuControls />
|
||||
<MenuControls
|
||||
onScrollTop={this.handleScrollTop}
|
||||
onScrollBottom={this.handleScrollBottom}
|
||||
onScrollNext={this.handleScrollNext}
|
||||
onScrollPrevious={this.handleScrollPrevious}
|
||||
/>
|
||||
</OutputToolbar>
|
||||
<ul>
|
||||
<li>
|
||||
</li>
|
||||
</ul>
|
||||
<OutputWrapper>
|
||||
<AutoSizer>
|
||||
{({ width, height }) => {
|
||||
console.log('scroll to index', scrollToIndex);
|
||||
console.log('start index', startIndex);
|
||||
console.log('stop index', stopIndex);
|
||||
return (
|
||||
<List
|
||||
ref={this.listRef}
|
||||
width={width}
|
||||
height={height}
|
||||
rowHeight={50}
|
||||
rowRenderer={this.renderRow}
|
||||
rowCount={results.length}
|
||||
overscanRowCount={5}
|
||||
scrollToIndex={scrollToIndex}
|
||||
onRowsRendered={this.onRowsRendered}
|
||||
scrollToAlignment="start"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
</AutoSizer>
|
||||
</OutputWrapper>
|
||||
</CardBody>
|
||||
);
|
||||
}
|
||||
|
@ -23,22 +23,32 @@ const Button = styled(PFButton)`
|
||||
`;
|
||||
|
||||
class MenuControls extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
onScrollTop,
|
||||
onScrollBottom,
|
||||
onScrollNext,
|
||||
onScrollPrevious,
|
||||
} = this.props;
|
||||
return (
|
||||
<Wrapper>
|
||||
<Button variant="plain">
|
||||
<PlusIcon />
|
||||
</Button>
|
||||
<Button variant="plain">
|
||||
<Button onClick={onScrollPrevious} variant="plain">
|
||||
<AngleUpIcon />
|
||||
</Button>
|
||||
<Button variant="plain">
|
||||
<Button onClick={onScrollNext} variant="plain">
|
||||
<AngleDownIcon />
|
||||
</Button>
|
||||
<Button variant="plain">
|
||||
<Button onClick={onScrollTop} variant="plain">
|
||||
<AngleDoubleUpIcon />
|
||||
</Button>
|
||||
<Button variant="plain">
|
||||
<Button onClick={onScrollBottom} variant="plain">
|
||||
<AngleDoubleDownIcon />
|
||||
</Button>
|
||||
</Wrapper>
|
||||
|
@ -15,7 +15,7 @@ const findChildren = () => {
|
||||
AngleDoubleDownIcon = wrapper.find('AngleDoubleDownIcon');
|
||||
AngleUpIcon = wrapper.find('AngleUpIcon');
|
||||
AngleDownIcon = wrapper.find('AngleDownIcon');
|
||||
}
|
||||
};
|
||||
|
||||
describe('MenuControls', () => {
|
||||
test('should render successfully', () => {
|
||||
|
@ -1 +1 @@
|
||||
export { default as MenuControls } from './MenuControls';
|
||||
export { default } from './MenuControls';
|
||||
|
Loading…
Reference in New Issue
Block a user