Provide in-dashboard help for creating changes

When an authenticated user's "Outgoing Changes" section is empty,
provide a message and dialogs with step-by-step instructions and command
samples for how to create a new change.

Change-Id: Id8f05a3b6bec399dd8ad3f226185f03d8663cc80
This commit is contained in:
Wyatt Allen
2018-09-07 12:14:36 -07:00
parent f2cb22dcf7
commit f13642b4d1
13 changed files with 436 additions and 3 deletions

View File

@@ -67,13 +67,18 @@ limitations under the License.
</td>
</tr>
</template>
<template is="dom-if" if="[[!changeSection.results.length]]">
<template is="dom-if" if="[[_isEmpty(changeSection)]]">
<tr class="noChanges">
<td class="leftPadding"></td>
<td class="star" hidden$="[[!showStar]]" hidden></td>
<td class="cell"
colspan$="[[_computeColspan(changeTableColumns, labelNames)]]">
No changes
<template is="dom-if" if="[[_isOutgoing(changeSection)]]">
<slot name="empty-outgoing"></slot>
</template>
<template is="dom-if" if="[[!_isOutgoing(changeSection)]]">
No changes
</template>
</td>
</tr>
</template>

View File

@@ -347,5 +347,13 @@
this.$.cursor.stops = this._getListItems();
this.$.cursor.moveToStart();
},
_isOutgoing(section) {
return !!section.isOutgoing;
},
_isEmpty(section) {
return !section.results.length;
},
});
})();

View File

@@ -270,6 +270,32 @@ limitations under the License.
assert.equal(noChangesMsg.length, 2);
});
suite('empty outgoing', () => {
test('not shown on empty non-outgoing sections', () => {
const section = {results: []};
assert.isTrue(element._isEmpty(section));
assert.isFalse(element._isOutgoing(section));
});
test('shown on empty outgoing sections', () => {
const section = {results: [], isOutgoing: true};
assert.isTrue(element._isEmpty(section));
assert.isTrue(element._isOutgoing(section));
});
test('not shown on non-empty outgoing sections', () => {
const section = {isOutgoing: true, results: [
{_number: 0, labels: {Verified: {approved: {}}}}]};
assert.isFalse(element._isEmpty(section));
assert.isTrue(element._isOutgoing(section));
});
});
test('_isOutgoing', () => {
assert.isTrue(element._isOutgoing({results: [], isOutgoing: true}));
assert.isFalse(element._isOutgoing({results: []}));
});
suite('empty column preference', () => {
let element;

View File

@@ -0,0 +1,87 @@
<!--
@license
Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
<link rel="import" href="../../shared/gr-shell-command/gr-shell-command.html">
<dom-module id="gr-create-commands-dialog">
<template>
<style include="shared-styles">
ol {
list-style: decimal;
margin-left: 1em;
}
p {
margin-bottom: .75em;
}
#commandsDialog {
max-width: 40em;
}
</style>
<gr-overlay id="commandsOverlay" with-backdrop>
<gr-dialog
id="commandsDialog"
confirm-label="Done"
cancel-label=""
confirm-on-enter
on-confirm="_handleClose">
<div class="header" slot="header">
Create change commands
</div>
<div class="main" slot="main">
<ol>
<li>
<p>
Make the changes to the files on your machine
</p>
</li>
<li>
<p>
If you are making a new commit use
</p>
<gr-shell-command command="[[_createNewCommitCommand]]"></gr-shell-command>
<p>
Or to amend an existing commit use
</p>
<gr-shell-command command="[[_amendExistingCommitCommand]]"></gr-shell-command>
<p>
Please make sure you add a commit message as it becomes the
description for your change.
</p>
</li>
<li>
<p>
Push the change for code review
</p>
<gr-shell-command command="[[_pushCommand]]"></gr-shell-command>
</li>
<li>
<p>
Close this dialog and you should be able to see your recently
created change in ``Outgoing changes'' section on
``Your changes'' page.
</p>
</li>
</ol>
</div>
</gr-dialog>
</gr-overlay>
</template>
<script src="gr-create-commands-dialog.js"></script>
</dom-module>

View File

@@ -0,0 +1,58 @@
/**
* @license
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
'use strict';
const Commands = {
CREATE: 'git commit',
AMEND: 'git commit --amend',
PUSH_PREFIX: 'git push origin HEAD:refs/for/',
};
Polymer({
is: 'gr-create-commands-dialog',
properties: {
branch: String,
_createNewCommitCommand: {
type: String,
readonly: true,
value: Commands.CREATE,
},
_amendExistingCommitCommand: {
type: String,
readonly: true,
value: Commands.AMEND,
},
_pushCommand: {
type: String,
computed: '_computePushCommand(branch)',
},
},
open() {
this.$.commandsOverlay.open();
},
_handleClose() {
this.$.commandsOverlay.close();
},
_computePushCommand(branch) {
return Commands.PUSH_PREFIX + branch;
},
});
})();

View File

@@ -0,0 +1,53 @@
<!DOCTYPE html>
<!--
@license
Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<title>gr-create-commands-dialog</title>
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
<script src="../../../bower_components/web-component-tester/browser.js"></script>
<link rel="import" href="../../../test/common-test-setup.html"/>
<link rel="import" href="gr-create-commands-dialog.html">
<script>void(0);</script>
<test-fixture id="basic">
<template>
<gr-create-commands-dialog></gr-create-commands-dialog>
</template>
</test-fixture>
<script>
suite('gr-create-commands-dialog tests', () => {
let element;
setup(() => {
element = fixture('basic');
});
test('_computePushCommand', () => {
element.branch = 'master';
assert.equal(element._pushCommand,
'git push origin HEAD:refs/for/master');
element.branch = 'stable-2.15';
assert.equal(element._pushCommand,
'git push origin HEAD:refs/for/stable-2.15');
});
});
</script>

View File

@@ -0,0 +1,48 @@
<!--
@license
Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
<link rel="import" href="../../shared/gr-repo-branch-picker/gr-repo-branch-picker.html">
<dom-module id="gr-create-destination-dialog">
<template>
<style include="shared-styles">
</style>
<gr-overlay id="createOverlay" with-backdrop>
<gr-dialog
confirm-label="View commands"
on-confirm="_pickerConfirm"
on-cancel="_handleClose"
disabled="[[!_repoAndBranchSelected]]">
<div class="header" slot="header">
Create change
</div>
<div class="main" slot="main">
<gr-repo-branch-picker
repo="{{_repo}}"
branch="{{_branch}}"></gr-repo-branch-picker>
<p>
If you haven't done so, you will need to clone the repository.
</p>
</div>
</gr-dialog>
</gr-overlay>
</template>
<script src="gr-create-destination-dialog.js"></script>
</dom-module>

View File

@@ -0,0 +1,58 @@
/**
* @license
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
'use strict';
/**
* Fired when a destination has been picked. Event details contain the repo
* name and the branch name.
*
* @event confirm
*/
Polymer({
is: 'gr-create-destination-dialog',
properties: {
_repo: String,
_branch: String,
_repoAndBranchSelected: {
type: Boolean,
value: false,
computed: '_computeRepoAndBranchSelected(_repo, _branch)',
},
},
open() {
this._repo = '';
this._branch = '';
this.$.createOverlay.open();
},
_handleClose() {
this.$.createOverlay.close();
},
_pickerConfirm() {
this.$.createOverlay.close();
const detail = {repo: this._repo, branch: this._branch};
this.dispatchEvent(new CustomEvent('confirm', {detail, bubbles: false}));
},
_computeRepoAndBranchSelected(repo, branch) {
return !!(repo && branch);
},
});
})();

View File

@@ -21,6 +21,8 @@ limitations under the License.
<link rel="import" href="../../change-list/gr-change-list/gr-change-list.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../gr-create-commands-dialog/gr-create-commands-dialog.html">
<link rel="import" href="../gr-create-destination-dialog/gr-create-destination-dialog.html">
<link rel="import" href="../gr-user-header/gr-user-header.html">
<dom-module id="gr-dashboard-view">
@@ -43,10 +45,42 @@ limitations under the License.
gr-user-header {
border-bottom: 1px solid var(--border-color);
}
#emptyOutgoing {
display: block;
}
#emptyOutgoing #graphic,
#emptyOutgoing #help {
display: inline-block;
margin: .5em;
}
#emptyOutgoing #graphic {
fill: var(--deemphasized-text-color);
max-width: 12em;
}
#emptyOutgoing #graphic svg {
display: block;
margin: 0 auto;
max-width: 100px;
}
#emptyOutgoing #graphic p {
text-align: center;
}
#emptyOutgoing #help {
vertical-align: top;
}
#emptyOutgoing #help h1 {
font-size: var(--font-size-large);
}
#emptyOutgoing #help p {
max-width: 35em;
}
@media only screen and (max-width: 50em) {
.loading {
padding: 0 var(--default-horizontal-margin);
}
#emptyOutgoing #graphic {
display: none;
}
}
</style>
<div class="loading" hidden$="[[!_loading]]">Loading...</div>
@@ -62,8 +96,32 @@ limitations under the License.
selected-index="{{viewState.selectedChangeIndex}}"
sections="[[_results]]"
on-toggle-star="_handleToggleStar"
on-toggle-reviewed="_handleToggleReviewed"></gr-change-list>
on-toggle-reviewed="_handleToggleReviewed">
<div id="emptyOutgoing" slot="empty-outgoing">
<div id="graphic">
<svg width="150" height="100">
<circle cx="50" cy="50" r="50"></circle>
</svg>
<p>
No outgoing changes yet
</p>
</div>
<div id="help">
<h1>Push your first changes for code review</h1>
<p>
Pushing a change for review is easy, but a little different from
other git code review tools. Click on the `Create Change' button
and follow the step by step instructions.
</p>
<gr-button on-tap="_createChangeTap">Create Change</gr-button>
</div>
</div>
</gr-change-list>
</div>
<gr-create-destination-dialog
id="destinationDialog"
on-confirm="_handleDestinationConfirm"></gr-create-destination-dialog>
<gr-create-commands-dialog id="commandsDialog"></gr-create-commands-dialog>
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
<gr-reporting id="reporting"></gr-reporting>
</template>

View File

@@ -45,6 +45,7 @@
// by the viewing user.
name: 'Outgoing reviews',
query: 'is:open owner:${user} -is:wip -is:ignored',
isOutgoing: true,
},
{
// Non-WIP open changes not owned by the viewed user, that the viewed user
@@ -249,6 +250,7 @@
sectionName: res.sections[i].name,
query: res.sections[i].query,
results,
isOutgoing: res.sections[i].isOutgoing,
})).filter((section, i) => !res.sections[i].hideIfEmpty ||
section.results.length);
});
@@ -267,5 +269,14 @@
this.$.restAPI.saveChangeReviewed(e.detail.change._number,
e.detail.reviewed);
},
_createChangeTap() {
this.$.destinationDialog.open();
},
_handleDestinationConfirm(e) {
this.$.commandsDialog.branch = e.detail.branch;
this.$.commandsDialog.open();
},
});
})();

View File

@@ -257,6 +257,22 @@ limitations under the License.
});
});
test('preserve isOutgoing sections', () => {
const sections = [
{name: 'test1', query: 'test1', isOutgoing: true},
{name: 'test2', query: 'test2'},
];
getChangesStub.restore();
sandbox.stub(element.$.restAPI, 'getChanges')
.returns(Promise.resolve([[], []]));
return element._fetchDashboardChanges({sections}).then(() => {
assert.equal(element._results.length, 2);
assert.isTrue(element._results[0].isOutgoing);
assert.isNotOk(element._results[1].isOutgoing);
});
});
test('_computeUserHeaderClass', () => {
assert.equal(element._computeUserHeaderClass(undefined), '');
assert.equal(element._computeUserHeaderClass(''), '');

View File

@@ -207,6 +207,10 @@ limitations under the License.
.size {
max-width: none;
}
.noChanges .cell {
display: block;
height: auto;
}
}
@media only screen and (min-width: 1450px) {
:host {

View File

@@ -56,6 +56,7 @@ limitations under the License.
'change-list/gr-change-list-item/gr-change-list-item_test.html',
'change-list/gr-change-list-view/gr-change-list-view_test.html',
'change-list/gr-change-list/gr-change-list_test.html',
'change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html',
'change-list/gr-dashboard-view/gr-dashboard-view_test.html',
'change-list/gr-user-header/gr-user-header_test.html',
'change/gr-account-entry/gr-account-entry_test.html',