Commit 6708cac0 authored by Bryce Johnson's avatar Bryce Johnson

Implement 'timetracking v1.1'.

 - Break vue component into smaller components and bundle them
 - Improve the help state UX to look like this: #985 (comment 16056031)
 - Create helpers for props existence checking !870 (comment 18534273)
 - Standardize on camelCase where possible, improve naming with computed values
 - Be clear about using human values from the server vs client
 - Consider & address the accessibility impact of only displaying the remaining
  time in a tooltip and the percentage of time spent in a colored meter.
 - Fix help 'Learn more' URLAppend state computed values with 'State'.
parent 62a4637d
//= require ./time_tracking/time_tracking_bundle
//= require lib/utils/pretty_time
((gl) => {
const PrettyTime = gl.PrettyTime;
Vue.component('time-tracking-collapsed-state', {
name: 'time-tracking-collapsed-state',
props: ['showComparisonState', 'showSpentOnlyState', 'showEstimateOnlyState', 'showNoTimeTrackingState', 'timeSpentHuman', 'timeEstimateHuman', 'stopwatchSvg'],
methods: {
abbreviateTime(timeStr) {
return PrettyTime.abbreviateTime(timeStr);
},
},
template: `
<div class='sidebar-collapsed-icon'>
<div v-html='stopwatchSvg'></div>
<div class='time-tracking-collapsed-summary'>
<div class='compare' v-if='showComparisonState'>
<span>{{ abbreviateTime(timeSpentHuman) }} / {{ abbreviateTime(timeEstimateHuman) }}</span>
</div>
<div class='estimate-only' v-if='showEstimateOnlyState'>
<span class='bold'>-- / {{ abbreviateTime(timeEstimateHuman) }}</span>
</div>
<div class='spend-only' v-if='showSpentOnlyState'>
<span class='bold'>{{ abbreviateTime(timeSpentHuman) }} / --</span>
</div>
<div class='no-tracking' v-if='showNoTimeTrackingState'>
<span class='no-value'>None</span>
</div>
</div>
</div>
`,
});
})(window.gl || (window.gl = {}));
//= require lib/utils/pretty_time
((gl) => {
const PrettyTime = gl.PrettyTime;
Vue.component('time-tracking-no-tracking-pane', {
name: 'time-tracking-no-tracking-pane',
template: '<div class="time-tracking-no-tracking-pane"><span class="no-value">No estimate or time spent</span></div>',
});
Vue.component('time-tracking-estimate-only-pane', {
name: 'time-tracking-estimate-only-pane',
props: ['timeEstimateHuman'],
template: '<div class="time-tracking-estimate-only-pane"><span class="bold">Estimated:</span> {{ timeEstimateHuman }}</div>',
});
Vue.component('time-tracking-spent-only-pane', {
name: 'time-tracking-spent-only-pane',
props: ['timeSpentHuman'],
template: '<div class="time-tracking-spend-only-pane"><span class="bold">Spent:</span> {{ timeSpentHuman }}</div>',
});
Vue.component('time-tracking-comparison-pane', {
name: 'time-tracking-comparison-pane',
props: ['timeSpent', 'timeEstimate', 'timeSpentHuman', 'timeEstimateHuman'],
computed: {
parsedRemaining() {
const diffSeconds = this.timeEstimate - this.timeSpent;
return PrettyTime.parseSeconds(diffSeconds);
},
timeRemainingHuman() {
return PrettyTime.stringifyTime(this.parsedRemaining);
},
timeRemainingTooltip() {
const prefix = this.timeRemainingMinutes < 0 ? 'Over by' : 'Time remaining:';
return `${prefix} ${this.timeRemainingHuman}`;
},
/* Diff values for comparison meter */
timeRemainingMinutes() {
return this.timeEstimate - this.timeSpent;
},
timeRemainingPercent() {
return `${Math.floor(((this.timeSpent / this.timeEstimate) * 100))}%`;
},
timeRemainingStatusClass() {
return this.timeEstimate >= this.timeSpent ? 'within_estimate' : 'over_estimate';
},
/* Parsed time values */
parsedEstimate() {
return PrettyTime.parseSeconds(this.timeEstimate);
},
parsedSpent() {
return PrettyTime.parseSeconds(this.timeSpent);
},
},
template: `
<div class='time-tracking-comparison-pane'>
<div class='compare-meter' data-toggle='tooltip' data-placement='top' role='timeRemainingDisplay'
:aria-valuenow='timeRemainingTooltip'
:title='timeRemainingTooltip'
:data-original-title='timeRemainingTooltip'
:class='timeRemainingStatusClass'>
<div class='meter-container' role='timeSpentPercent' :aria-valuenow='timeRemainingPercent'>
<div :style='{ width: timeRemainingPercent }' class='meter-fill'></div>
</div>
<div class='compare-display-container'>
<div class='compare-display pull-left'>
<span class='compare-label'>Spent</span>
<span class='compare-value spent'>{{ timeSpentHuman }}</span>
</div>
<div class='compare-display estimated pull-right'>
<span class='compare-label'>Est</span>
<span class='compare-value'>{{ timeEstimateHuman }}</span>
</div>
</div>
</div>
</div>
`,
});
})(window.gl || (window.gl = {}));
(() => {
Vue.component('time-tracking-help-state', {
name: 'time-tracking-help-state',
data() {
return {
docsUrl: '/help/workflow/time_tracking.md',
};
},
template: `
<div class='time-tracking-help-state'>
<div class='time-tracking-info'>
<h4>Track time with slash commands</h4>
<p>Slash commands can be used in the issues description and comment boxes.</p>
<p>
<code>/estimate</code>
will update the estimated time with the latest command.
</p>
<p>
<code>/spend</code>
will update the sum of the time spent.
</p>
<a class='btn btn-default learn-more-button' :href='docsUrl'>Learn more</a>
</div>
</div>
`,
});
})();
//= require vue
(() => {
gl.IssuableTimeTracker = Vue.component('issuable-time-tracker', {
name: 'issuable-time-tracker',
props: ['time_estimate', 'time_spent', 'human_time_estimate', 'human_time_spent', 'stopwatchSvg'],
data() {
return {
showHelp: false,
};
},
computed: {
timeSpent() {
return this.time_spent;
},
timeEstimate() {
return this.time_estimate;
},
timeEstimateHuman() {
return this.human_time_estimate;
},
timeSpentHuman() {
return this.human_time_spent;
},
hasTimeSpent() {
return !!this.timeSpent;
},
hasTimeEstimate() {
return !!this.timeEstimate;
},
showComparisonState() {
return this.hasTimeEstimate && this.hasTimeSpent;
},
showEstimateOnlyState() {
return this.hasTimeEstimate && !this.hasTimeSpent;
},
showSpentOnlyState() {
return this.hasTimeSpent && !this.hasTimeEstimate;
},
showNoTimeTrackingState() {
return !this.hasTimeEstimate && !this.hasTimeSpent;
},
showHelpState() {
return !!this.showHelp;
},
},
methods: {
toggleHelpState(show) {
this.showHelp = show;
},
},
template: `
<div class='time_tracker time-tracking-component-wrap' v-cloak>
<time-tracking-collapsed-state
:showComparisonState='showComparisonState'
:showHelpState='showHelpState'
:showSpentOnlyState='showSpentOnlyState'
:showEstimateOnlyState='showEstimateOnlyState'
:timeSpentHuman='timeSpentHuman'
:timeEstimateHuman='timeEstimateHuman'
:stopwatch-svg='stopwatchSvg'>
</time-tracking-collapsed-state>
<div class='title hide-collapsed'>
Time tracking
<div class='help-button pull-right'
v-if='!showHelpState'
@click='toggleHelpState(true)'>
<i class='fa fa-question-circle'></i>
</div>
<div class='close-help-button pull-right'
v-if='showHelpState'
@click='toggleHelpState(false)'>
<i class='fa fa-close'></i>
</div>
</div>
<div class='time-tracking-content hide-collapsed'>
<time-tracking-estimate-only-pane
v-if='showEstimateOnlyState'
:timeEstimateHuman='timeEstimateHuman'>
</time-tracking-estimate-only-pane>
<time-tracking-spent-only-pane
v-if='showSpentOnlyState'
:timeSpentHuman='timeSpentHuman'>
</time-tracking-spent-only-pane>
<time-tracking-no-tracking-pane
v-if='showNoTimeTrackingState'>
</time-tracking-no-tracking-pane>
<time-tracking-comparison-pane
v-if='showComparisonState'
:timeEstimate='timeEstimate'
:timeSpent='timeSpent'
:timeSpentHuman='timeSpentHuman'
:timeEstimateHuman='timeEstimateHuman'>
</time-tracking-comparison-pane>
<transition name='help-state-toggle'>
<time-tracking-help-state
v-if='showHelpState'>
</time-tracking-help-state>
</transition>
</div>
</div>
`,
});
})();
/* eslint-disable */
//= require vue
//= require issuable_time_tracker
//= require_directory ./components
//= require smart_interval
//= require subbable_resource
......@@ -29,15 +29,6 @@
url: gl.IssuableResource.endpoint,
});
},
initPolling() {
return new gl.SmartInterval({
callback: this.fetchIssuable,
startingInterval: 1000,
maxInterval: 10000,
incrementByFactorOf: 10,
lazyStart: false,
});
},
updateState(data) {
this.issuable = data;
},
......@@ -59,7 +50,6 @@
this.fetchIssuable();
},
mounted() {
this.initPolling();
this.subscribeToUpdates();
this.listenForSlashCommands();
},
......
/* eslint-disable */
//= require vue
//= require lib/utils/pretty_time
(() => {
const PrettyTime = gl.PrettyTime;
gl.IssuableTimeTracker = Vue.component('issuable-time-tracker', {
name: 'issuable-time-tracker',
props: ['time_estimate', 'time_spent', 'human_time_estimate', 'human_time_spent'],
data() {
return {
displayHelp: false,
};
},
computed: {
/* Select panels to show */
showComparison() {
return !!this.time_estimate && !!this.time_spent;
},
showEstimateOnly() {
return !!this.time_estimate && !this.time_spent;
},
showSpentOnly() {
return !!this.time_spent && !this.time_estimate;
},
showNoTimeTracking() {
return !this.time_estimate && !this.time_spent;
},
showHelp() {
return !!this.displayHelp;
},
/* Parsed time values */
parsedEstimate() {
return PrettyTime.parseSeconds(this.time_estimate);
},
parsedSpent() {
return PrettyTime.parseSeconds(this.time_spent);
},
parsedRemaining() {
const diffSeconds = this.time_estimate - this.time_spent;
return PrettyTime.parseSeconds(diffSeconds);
},
/* Human readable time values */
estimatedPretty() {
return this.human_time_estimate || PrettyTime.stringifyTime(this.parsedEstimate);
},
spentPretty() {
return this.human_time_spent || PrettyTime.stringifyTime(this.parsedSpent);
},
remainingPretty() {
return PrettyTime.stringifyTime(this.parsedRemaining);
},
remainingTooltipPretty() {
const prefix = this.diffMinutes < 0 ? 'Over by' : 'Time remaining:';
return `${prefix} ${this.remainingPretty}`;
},
/* Diff values for comparison meter */
diffMinutes() {
return this.time_estimate - this.time_spent;
},
diffPercent() {
return `${Math.floor(((this.time_spent / this.time_estimate) * 100))}%`;
},
diffStatusClass() {
return this.time_estimate >= this.time_spent ? 'within_estimate' : 'over_estimate';
},
},
methods: {
toggleHelpState(show) {
this.displayHelp = show;
},
abbreviateTime(timeStr) {
return PrettyTime.abbreviateTime(timeStr);
},
},
template: `
<div class='time_tracker time-tracking-component-wrap' v-cloak>
<div class='sidebar-collapsed-icon'>
<slot name='stopwatch'></slot>
<div class='time-tracking-collapsed-summary'>
<div class='compare' v-if='showComparison'>
<span>{{ abbreviateTime(spentPretty) }} / {{ abbreviateTime(estimatedPretty) }}</span>
</div>
<div class='estimate-only' v-if='showEstimateOnly'>
<span class='bold'>-- / {{ abbreviateTime(estimatedPretty) }}</span>
</div>
<div class='spend-only' v-if='showSpentOnly'>
<span class='bold'>{{ abbreviateTime(spentPretty) }} / --</span>
</div>
<div class='no-tracking' v-if='showNoTimeTracking'>
<span class='no-value'>None</span>
</div>
</div>
</div>
<div class='title hide-collapsed'>
Time tracking
<div class='help-button pull-right' v-if='!showHelp' @click='toggleHelpState(true)'>
<i class='fa fa-question-circle'></i>
</div>
</div>
<div class='time-tracking-content hide-collapsed'>
<div class='time-tracking-pane-compare' v-if='showComparison'>
<div class='compare-meter' data-toggle='tooltip' data-placement='top' :title='remainingTooltipPretty' :data-original-title='remainingTooltipPretty' :class='diffStatusClass' >
<div class='meter-container'>
<div :style='{ width: diffPercent }' class='meter-fill'></div>
</div>
<div class='compare-display-container'>
<div class='compare-display pull-left'>
<span class='compare-label'>Spent</span>
<span class='compare-value spent'>{{ spentPretty }}</span>
</div>
<div class='compare-display estimated pull-right'>
<span class='compare-label'>Est</span>
<span class='compare-value'>{{ estimatedPretty }}</span>
</div>
</div>
</div>
</div>
<div class='time-tracking-estimate-only' v-if='showEstimateOnly'>
<span class='bold'>Estimated:</span>
{{ estimatedPretty }}
</div>
<div class='time-tracking-spend-only' v-if='showSpentOnly'>
<span class='bold'>Spent:</span>
{{ spentPretty }}
</div>
<div class='time-tracking-no-tracking' v-if='showNoTimeTracking'>
<span class='no-value'>No estimate or time spent</span>
</div>
<div class='time-tracking-help-state' v-if='showHelp'>
<div class='close-help-button pull-right' @click='toggleHelpState(false)'>
<i class='fa fa-close'></i>
</div>
<div class='time-tracking-info'>
<h4>Track time with slash commands</h4>
<p>Slash commands can be used in the issues description and comment boxes.</p>
<p>
<code>/estimate</code>
will update the estimated time with the latest command.
</p>
<p>
<code>/spend</code>
will update the sum of the time spent.
</p>
<a class='btn btn-default learn-more-button' href='https://docs.gitlab.com/ee/workflow/time_tracking.html'>Learn more</a>
</div>
</div>
</div>
</div>
`,
});
})(window.gl || (window.gl = {}));
......@@ -494,4 +494,17 @@
color: $btn-white-active;
}
}
.help-state-toggle-enter-active {
transition: all .8s ease;
}
.help-state-toggle-leave-active {
transition: all .5s ease;
}
.help-state-toggle-enter,
.help-state-toggle-leave-active {
opacity: 0;
}
}
- todo = issuable_todo(issuable)
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('issuable/issuable_bundle.js')
%aside.right-sidebar{ class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar
- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
......@@ -74,15 +76,11 @@
= dropdown_tag('Milestone', options: { title: 'Assign milestone', toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: 'Search milestones', data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true }})
- if issuable.has_attribute?(:time_estimate)
#issuable-time-tracker.block
%issuable-time-tracker{ ':time_estimate' => 'issuable.time_estimate', ':time_spent' => 'issuable.total_time_spent', ':human_time_estimate' => 'issuable.human_time_estimate', ':human_time_spent' => 'issuable.human_total_time_spent' }
%issuable-time-tracker{ ':time_estimate' => 'issuable.time_estimate', ':time_spent' => 'issuable.total_time_spent', ':human_time_estimate' => 'issuable.human_time_estimate', ':human_time_spent' => 'issuable.human_total_time_spent', 'stopwatch-svg' => custom_icon('icon_stopwatch') }
// Fallback while content is loading
.title.hide-collapsed
Time tracking
= icon('spinner spin')
// TODO: Remove this when we have webpack/svg solution implemented
.stopwatch-svg{ slot:'stopwatch' }
%span.hide
= custom_icon('icon_stopwatch')
- if issuable.has_attribute?(:due_date)
.block.due_date
.sidebar-collapsed-icon
......
......@@ -94,6 +94,7 @@ module Gitlab
config.assets.precompile << "profile/profile_bundle.js"
config.assets.precompile << "protected_branches/protected_branches_bundle.js"
config.assets.precompile << "diff_notes/diff_notes_bundle.js"
config.assets.precompile << "issuable/issuable_bundle.js"
config.assets.precompile << "boards/boards_bundle.js"
config.assets.precompile << "cycle_analytics/cycle_analytics_bundle.js"
config.assets.precompile << "merge_conflicts/merge_conflicts_bundle.js"
......
/* eslint-disable */
//= require jquery
//= require vue
//= require issuable_time_tracker
//= require issuable/time_tracking/time_tracking_bundle
function initComponent(time_estimate = 100000, time_spent = 5000 ) {
function initComponent(time_estimate = 100000, time_spent = 5000, human_time_estimate = '3d 3h 46m', human_time_spent = '1h 23m') {
fixture.set(`
<div>
<div id="mock-container"></div>
......@@ -12,7 +12,13 @@ function initComponent(time_estimate = 100000, time_spent = 5000 ) {
this.initialData = {
time_estimate,
time_spent
time_spent,
human_time_estimate,
human_time_spent,
timeEstimateHuman: human_time_estimate,
timeSpentHuman: human_time_spent,
timeEstimate: time_estimate,
timeSpent: time_spent,
};
this.timeTracker = new gl.IssuableTimeTracker({
......@@ -48,21 +54,20 @@ function initComponent(time_estimate = 100000, time_spent = 5000 ) {
});
it('should show the "Comparison" pane when time_estimate and time_spent are truthy', function() {
const $comparisonPane = this.timeTracker.$el.querySelector('.time-tracking-pane-compare');
expect(this.timeTracker.showComparison).toBe(true);
const $comparisonPane = this.timeTracker.$el.querySelector('.time-tracking-comparison-pane');
expect(this.timeTracker.showComparisonState).toBe(true);
expect($comparisonPane).toBeVisible();
});
it('should display the human readable version of time estimated', function() {
const estimateText = this.timeTracker.$el.querySelector('.time-tracking-pane-compare .estimated .compare-value').innerText;
const estimateText = this.timeTracker.$el.querySelector('.time-tracking-comparison-pane .estimated .compare-value').innerText;
const correctText = '3d 3h 46m';
expect(estimateText).toBe(correctText);
});
it('should display the human readable version of time spent', function() {
const spentText = this.timeTracker.$el.querySelector('.time-tracking-pane-compare .compare-value.spent').innerText;
const spentText = this.timeTracker.$el.querySelector('.time-tracking-comparison-pane .compare-value.spent').innerText;
const correctText = '1h 23m';
expect(spentText).toBe(correctText);
......@@ -70,14 +75,14 @@ function initComponent(time_estimate = 100000, time_spent = 5000 ) {
describe('Remaining meter', function() {
it('should display the remaining meter with the correct width', function() {
const meterWidth = this.timeTracker.$el.querySelector('.time-tracking-pane-compare .meter-fill').style.width;
const meterWidth = this.timeTracker.$el.querySelector('.time-tracking-comparison-pane .meter-fill').style.width;
const correctWidth = '5%';
expect(meterWidth).toBe(correctWidth);
});
it('should display the remaining meter with the correct background color when within estimate', function() {
const styledMeter = $(this.timeTracker.$el).find('.time-tracking-pane-compare .within_estimate .meter-fill');
const styledMeter = $(this.timeTracker.$el).find('.time-tracking-comparison-pane .within_estimate .meter-fill');
expect(styledMeter.length).toBe(1);
});
......@@ -85,7 +90,7 @@ function initComponent(time_estimate = 100000, time_spent = 5000 ) {
this.timeTracker.time_estimate = 1;
this.timeTracker.time_spent = 2;
Vue.nextTick(() => {
const styledMeter = $(this.timeTracker.$el).find('.time-tracking-pane-compare .over_estimate .meter-fill');
const styledMeter = $(this.timeTracker.$el).find('.time-tracking-comparison-pane .over_estimate .meter-fill');
expect(styledMeter.length).toBe(1);
});
});
......@@ -94,18 +99,18 @@ function initComponent(time_estimate = 100000, time_spent = 5000 ) {
describe("Estimate only pane", function() {
beforeEach(function() {
initComponent.apply(this, [10000, 0]);
initComponent.apply(this, [10000, 0, '2h 46m', 0]);
});
it('should only show the "Estimate only" pane when time_estimate is truthy and time_spent is falsey', function() {
const $estimateOnlyPane = this.timeTracker.$el.querySelector('.time-tracking-estimate-only');
const $estimateOnlyPane = this.timeTracker.$el.querySelector('.time-tracking-estimate-only-pane');
expect(this.timeTracker.showEstimateOnly).toBe(true);
expect(this.timeTracker.showEstimateOnlyState).toBe(true);
expect($estimateOnlyPane).toBeVisible();
});
it('should display the human readable version of time estimated', function() {
const estimateText = this.timeTracker.$el.querySelector('.time-tracking-estimate-only').innerText;
const estimateText = this.timeTracker.$el.querySelector('.time-tracking-estimate-only-pane').innerText;
const correctText = 'Estimated: 2h 46m';
expect(estimateText).toBe(correctText);
......@@ -118,14 +123,14 @@ function initComponent(time_estimate = 100000, time_spent = 5000 ) {
});
// Look for the value
it('should only show the "Spent only" pane when time_estimate is falsey and time_spent is truthy', function() {
const $spentOnlyPane = this.timeTracker.$el.querySelector('.time-tracking-spend-only');
const $spentOnlyPane = this.timeTracker.$el.querySelector('.time-tracking-spend-only-pane');
expect(this.timeTracker.showSpentOnly).toBe(true);
expect(this.timeTracker.showSpentOnlyState).toBe(true);
expect($spentOnlyPane).toBeVisible();
});
it('should display the human readable version of time spent', function() {
const spentText = this.timeTracker.$el.querySelector('.time-tracking-spend-only').innerText;
const spentText = this.timeTracker.$el.querySelector('.time-tracking-spend-only-pane').innerText;
const correctText = 'Spent: 1h 23m';
expect(spentText).toBe(correctText);
......@@ -134,23 +139,18 @@ function initComponent(time_estimate = 100000, time_spent = 5000 ) {
describe('No time tracking pane', function() {
beforeEach(function() {
initComponent.apply(this, [0, 0]);
initComponent.apply(this, [0, 0, 0, 0]);
});
it('should only show the "No time tracking" pane when both time_estimate and time_spent are falsey', function() {
const $noTrackingPane = this.timeTracker.$el.querySelector('.time-tracking-no-tracking');
expect(this.timeTracker.showNoTimeTracking).toBe(true);
expect($noTrackingPane).toBeVisible();
});
it('should display the status text', function() {
const noTrackingText = this.timeTracker.$el.querySelector('.time-tracking-no-tracking .no-value').innerText;
const $noTrackingPane = this.timeTracker.$el.querySelector('.time-tracking-no-tracking-pane');
const noTrackingText =$noTrackingPane.innerText;
const correctText = 'No estimate or time spent';
expect(this.timeTracker.showNoTimeTrackingState).toBe(true);
expect($noTrackingPane).toBeVisible();
expect(noTrackingText).toBe(correctText);
});
});
describe("Help pane", function() {
......@@ -161,12 +161,12 @@ function initComponent(time_estimate = 100000, time_spent = 5000 ) {
it('should not show the "Help" pane by default', function() {
const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
expect(this.timeTracker.showHelp).toBe(false);
expect(this.timeTracker.showHelpState).toBe(false);
expect($helpPane).toBeNull();
});
it('should link to the correct documentation', function(done) {
const correctUrl = 'https://docs.gitlab.com/ee/workflow/time_tracking.html';
const correctUrl = '/help/workflow/time_tracking.md'
$(this.timeTracker.$el).find('.help-button').click();
......@@ -183,7 +183,7 @@ function initComponent(time_estimate = 100000, time_spent = 5000 ) {
Vue.nextTick(() => {
const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
expect(this.timeTracker.showHelp).toBe(true);
expect(this.timeTracker.showHelpState).toBe(true);
expect($helpPane).toBeVisible();
done();
});
......@@ -192,19 +192,19 @@ function initComponent(time_estimate = 100000, time_spent = 5000 ) {
it('should not show the "Help" pane when help button is clicked and then closed', function(done) {
$(this.timeTracker.$el).find('.help-button').click();
Vue.nextTick(() => {
setTimeout(() => {
$(this.timeTracker.$el).find('.close-help-button').click();
Vue.nextTick(() => {
setTimeout(() => {
const $helpPane = this.timeTracker.$el.querySelector('.time-tracking-help-state');
expect(this.timeTracker.showHelp).toBe(false);
expect(this.timeTracker.showHelpState).toBe(false);
expect($helpPane).toBeNull();
done();
});
});
}, 1000);
}, 1000);
});
});
});
......
shared_examples 'issuable time tracker' do
it 'renders the sidebar component empty state' do
page.within '.issuable-sidebar' do
page.within '.time-tracking-no-tracking-pane' do
expect(page).to have_content 'No estimate or time spent'
end
end
......@@ -8,7 +8,7 @@ shared_examples 'issuable time tracker' do
it 'updates the sidebar component when estimate is added' do
submit_time('/estimate 3w 1d 1h')
page.within '.time-tracking-estimate-only' do
page.within '.time-tracking-estimate-only-pane' do
expect(page).to have_content '3w 1d 1h'
end
end
......@@ -16,7 +16,7 @@ shared_examples 'issuable time tracker' do
it 'updates the sidebar component when spent is added' do
submit_time('/spend 3w 1d 1h')
page.within '.time-tracking-spend-only' do
page.within '.time-tracking-spend-only-pane' do
expect(page).to have_content '3w 1d 1h'
end
end
......@@ -25,7 +25,7 @@ shared_examples 'issuable time tracker' do
submit_time('/estimate 3w 1d 1h')
submit_time('/spend 3w 1d 1h')
page.within '.time-tracking-pane-compare' do
page.within '.time-tracking-comparison-pane' do
expect(page).to have_content '3w 1d 1h'
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment