Merge pull request #310 from jenkinsci/feature/JENKINS-35828-pipeline-card

feature/jenkins 35828 - pipeline card
This commit is contained in:
Cliff Meyers 2016-07-07 12:11:26 -04:00 committed by GitHub
commit 03a301025c
13 changed files with 313 additions and 15 deletions

View File

@ -1,12 +1,13 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 160
[*.md]
trim_trailing_whitespace = false

View File

@ -35,7 +35,7 @@
"skin-deep": "^0.16.0"
},
"dependencies": {
"@jenkins-cd/design-language": "0.0.58",
"@jenkins-cd/design-language": "0.0.60",
"@jenkins-cd/js-extensions": "0.0.17-beta-1",
"@jenkins-cd/js-modules": "0.0.5",
"@jenkins-cd/sse-gateway": "0.0.5",

View File

@ -36,7 +36,7 @@ export default class PipelinePage extends Component {
<span> / </span>
<Link to={activityUrl}>{name}</Link>
</h1>
<Favorite darkTheme />
<Favorite className="dark-yellow" />
</Title>
<PageTabs base={baseUrl}>
<TabLink to="/activity">Activity</TabLink>

View File

@ -0,0 +1,10 @@
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 4
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
max_line_length = 160

View File

@ -13,14 +13,9 @@ builder.defineTask('test', function() {
var mocha = require('gulp-mocha');
var babel = require('babel-core/register');
// Allow running of a specific test
// e.g. gulp test --test pipelines
// will run the pipelines-spec.js
var filter = builder.args.argvValue('--test', '*');
builder.gulp.src('src/test/js/' + filter + '-spec.js')
builder.gulp.src('src/test/js/**/*-spec.jsx')
.pipe(mocha({
compilers: {js: babel}
compilers: { js: babel },
})).on('error', function(e) {
if (builder.isRetest()) {
// ignore test failures if we are running retest.

View File

@ -23,16 +23,18 @@
"babel-preset-react": "^6.5.0",
"babel-preset-stage-0": "^6.5.0",
"chai": "^3.5.0",
"enzyme": "^2.2.0",
"enzyme": "2.3.0",
"eslint": "2.8.0",
"eslint-plugin-react": "^5.0.1",
"eslint-to-editorconfig": "1.2.0",
"gulp": "^3.9.1",
"gulp-mocha": "^2.2.0",
"mocha": "^2.4.5",
"nock": "^8.0.0"
"nock": "^8.0.0",
"react-addons-test-utils": "15.0.1"
},
"dependencies": {
"@jenkins-cd/design-language": "0.0.58",
"@jenkins-cd/design-language": "0.0.60",
"@jenkins-cd/js-extensions": "0.0.17-beta-1",
"@jenkins-cd/js-modules": "0.0.5",
"@jenkins-cd/sse-gateway": "0.0.5",

View File

@ -0,0 +1,126 @@
/**
* Created by cmeyers on 6/28/16.
*/
import React, { Component, PropTypes } from 'react';
import { Icon } from 'react-material-icons-blue';
import { Favorite, LiveStatusIndicator } from '@jenkins-cd/design-language';
/**
* PipelineCard displays an informational card about a Pipeline and its status.
*
* Properties:
* status: 'result' or 'status' value e.g. 'success', 'failure', etc.
* percentage: for status=running, the percent complete
* organization: name of org
* pipeline: name of pipeline
* branch: name of branch
* commitId: ID of commit
* favorite: whether or not the pipeline is favorited
* onRunClick: callback invoked when 'Run Again' is clicked
* onFavoriteToggle: callback invokved when favorite checkbox is toggled.
*/
export class PipelineCard extends Component {
static _getBackgroundClass(status) {
return status !== null && status.length > 0 ?
`${status.toLowerCase()}-bg-lite` :
'';
}
constructor(props) {
super(props);
this.state = {
favorite: false,
};
}
componentWillMount() {
this._updateState(this.props);
}
componentWillReceiveProps(nextProps) {
if (this.props.favorite !== nextProps.favorite) {
this._updateState(nextProps);
}
}
_updateState(props) {
this.setState({
favorite: props.favorite,
});
}
_onRunClick() {
if (this.props.onRunClick) {
this.props.onRunClick();
}
}
_onFavoriteToggle() {
const value = !this.state.favorite;
this.setState({
favorite: value,
});
if (this.props.onFavoriteToggle) {
this.props.onFavoriteToggle(value);
}
}
render() {
const { status } = this.props;
const bgClass = PipelineCard._getBackgroundClass(status);
const showRun = status && status.toLowerCase() === 'failure' || status.toLowerCase() === 'aborted';
return (
<div className={`pipeline-card ${bgClass}`}>
<LiveStatusIndicator result={this.props.status} width={'20px'} height={'20px'} noBackground />
<span className="name">
{this.props.organization} / {this.props.pipeline}
</span>
{ this.props.branch &&
<span className="branch">
<span className="octicon octicon-git-branch"></span>
<span className="branchText">{this.props.branch}</span>
</span>
}
<span className="commit">
<span className="octicon octicon-git-commit"></span>
<pre className="commitId">#{this.props.commitId}</pre>
</span>
<span className="actions">
{ showRun &&
<a className="run" title="Run Again" onClick={() => this._onRunClick()}>
<Icon size={24} icon="replay" />
</a>
}
<Favorite checked={this.state.favorite} className="dark-white"
onToggle={() => this._onFavoriteToggle()}
/>
</span>
</div>
);
}
}
PipelineCard.propTypes = {
status: PropTypes.string,
percentage: PropTypes.number, // TODO: might need startTime and estimatedDuration
organization: PropTypes.string,
pipeline: PropTypes.string,
branch: PropTypes.string,
commitId: PropTypes.string,
favorite: PropTypes.bool,
onRunClick: PropTypes.func,
onFavoriteToggle: PropTypes.func,
};
PipelineCard.defaultProps = {
favorite: false,
};

View File

@ -0,0 +1,31 @@
/**
* Created by cmeyers on 6/28/16.
*/
import React from 'react';
import { action, storiesOf } from '@kadira/storybook';
import { PipelineCard } from '../components/PipelineCard';
const style = { padding: '10px' };
const style2 = { paddingBottom: '10px' };
storiesOf('PipelineCard', module)
.add('all states', () => {
const states = 'SUCCESS,QUEUED,RUNNING,FAILURE,ABORTED,UNSTABLE,NOT_BUILT,UNKNOWN'.split(',');
return (
<div style={style}>
{ states.map(s =>
<div key={s} style={style2}>
<PipelineCard status={s} organization="Jenkins" pipeline="blueocean"
branch="feature/JENKINS-123" commitId="447d8e1" favorite
onRunClick={action('run')} onFavoriteToggle={action('toggle')}
/>
</div>
) }
<PipelineCard status="RUNNING" organization="jenkinsci" pipeline="blueocean"
commitId="447d8e1" onRunClick={action('run')} onFavoriteToggle={action('toggle')}
/>
</div>
);
});

View File

@ -1,4 +1,5 @@
/**
* Created by cmeyers on 6/28/16.
*/
require('./ActionLinkStories.jsx');
require('./ActionLinkStories');
require('./PipelineCardStories');

View File

@ -0,0 +1,81 @@
.pipeline-card {
display: flex;
align-items: center;
border-radius: 4px;
color: white;
min-width: 400px;
padding: 15px;
.name, .branch, .commit {
flex: 1 1 auto;
margin-left: 10px;
}
.branch, .commit {
display: flex;
align-items: center;
}
.name, .branchText, .commitId {
height: 18px;
}
.commitId {
margin: 0 0 0 5px;
}
.actions {
display: flex;
align-items: center;
justify-content: flex-end;
min-width: 60px;
margin-left: 10px;
}
.run {
margin-right: 10px;
}
.svg-icon {
fill: white;
}
// remove empty space to attain centered vertical alignment
.run, .actions {
> * {
vertical-align: bottom;
}
}
.progress-spinner.running {
circle {
stroke: #bcd8f1;
}
path.running {
stroke: white;
}
}
.progress-spinner.queued {
circle {
stroke: white;
}
circle.inner {
stroke: white;
fill: white;
}
}
.progress-spinner.not_built {
circle {
stroke: white;
}
}
// force "favorite" checkbox to white when on unstable card
// FIXME: doesn't work in IE11
.checkbox {
filter: grayscale(100%) brightness(0%) invert(100%);
-webkit-filter: grayscale(100%) brightness(0%) invert(100%);
}
}

View File

@ -0,0 +1 @@
@import 'components/pipeline-card';

View File

@ -0,0 +1,50 @@
/**
* Created by cmeyers on 7/6/16.
*/
import React from 'react';
import { assert } from 'chai';
import { shallow } from 'enzyme';
import { PipelineCard } from '../../../main/js/components/PipelineCard';
describe('PipelineCard', () => {
it('renders basic child elements', () => {
const status = 'SUCCESS';
const wrapper = shallow(
<PipelineCard status={status} organization="Jenkins" pipeline="blueocean"
branch="feature/JENKINS-123" commitId="447d8e1" favorite
/>
);
assert.equal(wrapper.find('LiveStatusIndicator').length, 1);
assert.equal(wrapper.find('.name').length, 1);
assert.equal(wrapper.find('.name').text(), 'Jenkins / blueocean');
assert.equal(wrapper.find('.branch').length, 1);
assert.equal(wrapper.find('.branchText').text(), 'feature/JENKINS-123');
assert.equal(wrapper.find('.commit').length, 1);
assert.equal(wrapper.find('.commitId').text(), '#447d8e1');
assert.equal(wrapper.find('Favorite').length, 1);
});
it('renders "run" button after failure', () => {
const status = 'FAILURE';
const wrapper = shallow(
<PipelineCard status={status} organization="Jenkins" pipeline="blueocean"
branch="feature/JENKINS-123" commitId="447d8e1" favorite
/>
);
assert.equal(wrapper.find('.actions .run').length, 1);
});
it('renders no "run" button after success', () => {
const status = 'SUCCESS';
const wrapper = shallow(
<PipelineCard status={status} organization="Jenkins" pipeline="blueocean"
branch="feature/JENKINS-123" commitId="447d8e1" favorite
/>
);
assert.equal(wrapper.find('.actions .run').length, 0);
});
});

View File

@ -25,7 +25,7 @@
"zombie": "^4.2.1"
},
"dependencies": {
"@jenkins-cd/design-language": "0.0.58",
"@jenkins-cd/design-language": "0.0.60",
"@jenkins-cd/js-extensions": "0.0.17-beta-1",
"@jenkins-cd/js-modules": "0.0.5",
"history": "2.0.2",