Add permalinks to task detail popup

This adds a link icon to the title of the modal dialog that opens
when the task status is clicked.  It uses the URL hash to supply
a path to the task which will cause all the parent tasks to be
expanded and the dialog open when a user visits the url with the
hash.

Change-Id: I3cf5b7115b775de103b9af7986ac0b8318688b42
This commit is contained in:
James E. Blair
2019-08-07 17:26:02 -07:00
parent 53df072eb0
commit 5b0bbb6ca5
3 changed files with 51 additions and 9 deletions

View File

@@ -60,6 +60,20 @@ function hasInterestingKeys (obj, keys) {
return ret
}
function makeTaskPath (path) {
return path.join('/')
}
function taskPathMatches (ref, test) {
if (test.length < ref.length)
return false
for (let i=0; i < ref.length; i++) {
if (ref[i] !== test[i])
return false
}
return true
}
class TaskOutput extends React.Component {
static propTypes = {
data: PropTypes.object,
@@ -180,6 +194,8 @@ class HostTask extends React.Component {
task: PropTypes.object,
host: PropTypes.object,
errorIds: PropTypes.object,
taskPath: PropTypes.array,
displayPath: PropTypes.array,
}
state = {
@@ -201,13 +217,16 @@ class HostTask extends React.Component {
constructor (props) {
super(props)
const { host } = this.props
const { host, taskPath, displayPath } = this.props
hostTaskStats(this.state, host)
if (taskPathMatches(taskPath, displayPath))
this.state.showModal = true
}
render () {
const { hostname, task, host, errorIds } = this.props
const { hostname, task, host, taskPath, errorIds } = this.props
const ai = []
if (this.state.skipped) {
@@ -244,6 +263,7 @@ class HostTask extends React.Component {
)
const expand = errorIds.has(task.task.id)
let name = task.task.name
if (!name) {
name = host.action
@@ -286,7 +306,13 @@ class HostTask extends React.Component {
>
<Icon type="pf" name="close" />
</button>
<Modal.Title>{hostname}</Modal.Title>
<Modal.Title>{hostname}
<span className="zuul-console-modal-header-link">
<a href={'#'+makeTaskPath(taskPath)}>
<Icon type="fa" name="link" title="Permalink" />
</a>
</span>
</Modal.Title>
</Modal.Header>
<Modal.Body>
<TaskOutput data={host}/>
@@ -301,13 +327,17 @@ class PlayBook extends React.Component {
static propTypes = {
playbook: PropTypes.object,
errorIds: PropTypes.object,
taskPath: PropTypes.array,
displayPath: PropTypes.array,
}
render () {
const { playbook, errorIds } = this.props
const { playbook, errorIds, taskPath, displayPath } = this.props
const expandAll = (playbook.phase === 'run')
const expand = (expandAll || errorIds.has(playbook.phase + playbook.index))
const expand = (expandAll ||
errorIds.has(playbook.phase + playbook.index) ||
taskPathMatches(taskPath, displayPath))
const ai = []
if (playbook.trusted) {
@@ -340,7 +370,10 @@ class PlayBook extends React.Component {
<Row key={idx2+hostname}>
<Col sm={12}>
<HostTask hostname={hostname}
task={task} host={host} errorIds={errorIds}/>
taskPath={taskPath.concat([
idx.toString(), idx2.toString(), hostname])}
displayPath={displayPath} task={task} host={host}
errorIds={errorIds}/>
</Col>
</Row>
))))}
@@ -354,6 +387,7 @@ class PlayBook extends React.Component {
class Console extends React.Component {
static propTypes = {
output: PropTypes.array,
displayPath: PropTypes.array,
}
constructor (props) {
@@ -394,13 +428,14 @@ class Console extends React.Component {
}
render () {
const { output } = this.props
const { output, displayPath } = this.props
return (
<React.Fragment>
<ListView key="playbooks" className="zuul-console">
{output.map((playbook, idx) => (
<PlayBook key={idx} playbook={playbook} errorIds={this.errorIds}/>))}
<PlayBook key={idx} playbook={playbook} taskPath={[idx.toString()]}
displayPath={displayPath} errorIds={this.errorIds}/>))}
</ListView>
</React.Fragment>
)

View File

@@ -226,6 +226,11 @@ pre.version {
{
cursor: default;
}
.zuul-console-modal-header-link
{
margin-left: 2em;
font-size: 18px;
}
.zuul-console-task-detail
{
width: 80%;

View File

@@ -42,6 +42,8 @@ class BuildConsolePage extends Refreshable {
render () {
const { remoteData } = this.props
const build = remoteData.builds[this.props.match.params.buildId]
const hash = this.props.location.hash.substring(1).split('/')
return (
<React.Fragment>
<div style={{float: 'right'}}>
@@ -49,7 +51,7 @@ class BuildConsolePage extends Refreshable {
</div>
{build && build.output &&
<Build build={build} active='console'>
<Console output={build.output}/>
<Console output={build.output} displayPath={hash.length>0?hash:undefined}/>
</Build>}
</React.Fragment>
)