Commit c5f8a7df authored by Phil Hughes's avatar Phil Hughes

Merge branch '7103-show-milestone-dates-within-tooltips' into 'master'

Show actual Milestone dates within tooltips for Milestones in Epics sidebar

Closes #7103

See merge request gitlab-org/gitlab-ee!8048
parents 5ba9095b cbd67f5a
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
const dateWords = dateInWords(date, true); const dateWords = dateInWords(date, true);
const parsedDateWords = dateWords ? dateWords.replace(',', '') : dateWords; const parsedDateWords = dateWords ? dateWords.replace(',', '') : dateWords;
return date ? parsedDateWords : 'None'; return date ? parsedDateWords : __('None');
}, },
tooltipText(dateType = 'min') { tooltipText(dateType = 'min') {
const defaultText = dateType === 'min' ? __('Start date') : __('Due date'); const defaultText = dateType === 'min' ? __('Start date') : __('Due date');
...@@ -72,7 +72,10 @@ ...@@ -72,7 +72,10 @@
`(${timeAgo})`, `(${timeAgo})`,
].join(' ') : ''; ].join(' ') : '';
return [defaultText, dateText].join('<br />'); if (date) {
return [defaultText, dateText].join('<br />');
}
return __('Start and due date');
}, },
}, },
}; };
......
...@@ -6,7 +6,7 @@ import EpicShowApp from './components/epic_show_app.vue'; ...@@ -6,7 +6,7 @@ import EpicShowApp from './components/epic_show_app.vue';
export default () => { export default () => {
const el = document.querySelector('#epic-show-app'); const el = document.querySelector('#epic-show-app');
const metaData = convertObjectPropsToCamelCase(JSON.parse(el.dataset.meta)); const metaData = convertObjectPropsToCamelCase(JSON.parse(el.dataset.meta), { deep: true });
const initialData = JSON.parse(el.dataset.initial); const initialData = JSON.parse(el.dataset.initial);
// Collapse the sidebar on mobile screens by default // Collapse the sidebar on mobile screens by default
...@@ -22,8 +22,9 @@ export default () => { ...@@ -22,8 +22,9 @@ export default () => {
components: { components: {
'epic-show-app': EpicShowApp, 'epic-show-app': EpicShowApp,
}, },
render: createElement => createElement('epic-show-app', { render: createElement =>
props, createElement('epic-show-app', {
}), props,
}),
}); });
}; };
...@@ -6,6 +6,7 @@ import Cookies from 'js-cookie'; ...@@ -6,6 +6,7 @@ import Cookies from 'js-cookie';
import flash from '~/flash'; import flash from '~/flash';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
import { dateInWords, parsePikadayDate } from '~/lib/utils/datetime_utility';
import ListLabel from '~/vue_shared/models/label'; import ListLabel from '~/vue_shared/models/label';
import SidebarTodo from '~/sidebar/components/todo_toggle/todo.vue'; import SidebarTodo from '~/sidebar/components/todo_toggle/todo.vue';
import SidebarCollapsedGroupedDatePicker from '~/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue'; import SidebarCollapsedGroupedDatePicker from '~/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue';
...@@ -85,11 +86,19 @@ export default { ...@@ -85,11 +86,19 @@ export default {
required: false, required: false,
default: '', default: '',
}, },
startDateSourcingMilestoneDates: {
type: Object,
required: true,
},
dueDateSourcingMilestoneTitle: { dueDateSourcingMilestoneTitle: {
type: String, type: String,
required: false, required: false,
default: '', default: '',
}, },
dueDateSourcingMilestoneDates: {
type: Object,
required: true,
},
initialLabels: { initialLabels: {
type: Array, type: Array,
required: true, required: true,
...@@ -233,17 +242,33 @@ export default { ...@@ -233,17 +242,33 @@ export default {
getDateTypeString(dateType) { getDateTypeString(dateType) {
return dateType === DateTypes.start ? s__('Epics|start') : s__('Epics|due'); return dateType === DateTypes.start ? s__('Epics|start') : s__('Epics|due');
}, },
getDateFromMilestonesTooltip(dateType = 'start') { getDateFromMilestonesTooltip(dateType = DateTypes.start) {
const { startDateTimeFromMilestones, dueDateTimeFromMilestones } = this.store; const { startDateTimeFromMilestones, dueDateTimeFromMilestones } = this.store;
const dateSourcingMilestoneTitle = this[`${dateType}DateSourcingMilestoneTitle`]; const dateSourcingMilestoneTitle = this[`${dateType}DateSourcingMilestoneTitle`];
const sourcingMilestoneDates =
dateType === DateTypes.start
? this.startDateSourcingMilestoneDates
: this.dueDateSourcingMilestoneDates;
if (startDateTimeFromMilestones && dueDateTimeFromMilestones) { if (startDateTimeFromMilestones && dueDateTimeFromMilestones) {
return dateSourcingMilestoneTitle; const startDate = parsePikadayDate(sourcingMilestoneDates.startDate);
const dueDate = parsePikadayDate(sourcingMilestoneDates.dueDate);
return `${dateSourcingMilestoneTitle}<br/><span class="text-tertiary">${dateInWords(
startDate,
true,
startDate.getFullYear() === dueDate.getFullYear(),
)}${dateInWords(dueDate, true)}</span>`;
} }
return sprintf(s__('Epics|To schedule your epic\'s %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic.'), { return sprintf(
epicDateType: this.getDateTypeString(dateType), s__(
}); "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic.",
),
{
epicDateType: this.getDateTypeString(dateType),
},
);
}, },
toggleSidebar() { toggleSidebar() {
this.collapsed = !this.collapsed; this.collapsed = !this.collapsed;
...@@ -293,9 +318,11 @@ export default { ...@@ -293,9 +318,11 @@ export default {
}) })
.catch(() => { .catch(() => {
this[savingBoolean] = false; this[savingBoolean] = false;
flash(sprintf(s__('Epics|An error occurred while saving %{epicDateType} date'), { flash(
epicDateType: this.getDateTypeString(dateType), sprintf(s__('Epics|An error occurred while saving %{epicDateType} date'), {
})); epicDateType: this.getDateTypeString(dateType),
}),
);
}); });
}, },
changeStartDateType(dateTypeIsFixed, typeChangeOnEdit) { changeStartDateType(dateTypeIsFixed, typeChangeOnEdit) {
......
...@@ -20,11 +20,19 @@ module EpicsHelper ...@@ -20,11 +20,19 @@ module EpicsHelper
start_date_fixed: epic.start_date_fixed, start_date_fixed: epic.start_date_fixed,
start_date_from_milestones: epic.start_date_from_milestones, start_date_from_milestones: epic.start_date_from_milestones,
start_date_sourcing_milestone_title: epic.start_date_sourcing_milestone&.title, start_date_sourcing_milestone_title: epic.start_date_sourcing_milestone&.title,
start_date_sourcing_milestone_dates: {
start_date: epic.start_date_sourcing_milestone&.start_date,
due_date: epic.start_date_sourcing_milestone&.due_date
},
due_date: epic.due_date, due_date: epic.due_date,
due_date_is_fixed: epic.due_date_is_fixed?, due_date_is_fixed: epic.due_date_is_fixed?,
due_date_fixed: epic.due_date_fixed, due_date_fixed: epic.due_date_fixed,
due_date_from_milestones: epic.due_date_from_milestones, due_date_from_milestones: epic.due_date_from_milestones,
due_date_sourcing_milestone_title: epic.due_date_sourcing_milestone&.title, due_date_sourcing_milestone_title: epic.due_date_sourcing_milestone&.title,
due_date_sourcing_milestone_dates: {
start_date: epic.due_date_sourcing_milestone&.start_date,
due_date: epic.due_date_sourcing_milestone&.due_date
},
end_date: epic.end_date, end_date: epic.end_date,
state: epic.state state: epic.state
} }
......
---
title: Show actual Milestone dates within tooltips for Milestones in Epics sidebar
merge_request: 8048
author:
type: added
...@@ -5,14 +5,15 @@ describe EpicsHelper do ...@@ -5,14 +5,15 @@ describe EpicsHelper do
describe '#epic_show_app_data' do describe '#epic_show_app_data' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:milestone) { create(:milestone, title: 'make me a sandwich') } let(:milestone1) { create(:milestone, title: 'make me a sandwich', start_date: '2010-01-01', due_date: '2019-12-31') }
let(:milestone2) { create(:milestone, title: 'make me a pizza', start_date: '2020-01-01', due_date: '2029-12-31') }
let!(:epic) do let!(:epic) do
create( create(
:epic, :epic,
author: user, author: user,
start_date_sourcing_milestone: milestone, start_date_sourcing_milestone: milestone1,
start_date: Date.new(2000, 1, 1), start_date: Date.new(2000, 1, 1),
due_date_sourcing_milestone: milestone, due_date_sourcing_milestone: milestone2,
due_date: Date.new(2000, 1, 2) due_date: Date.new(2000, 1, 2)
) )
end end
...@@ -32,6 +33,7 @@ describe EpicsHelper do ...@@ -32,6 +33,7 @@ describe EpicsHelper do
created author epic_id todo_exists todo_path state created author epic_id todo_exists todo_path state
start_date start_date_fixed start_date_is_fixed start_date_from_milestones start_date_sourcing_milestone_title start_date start_date_fixed start_date_is_fixed start_date_from_milestones start_date_sourcing_milestone_title
end_date due_date due_date_fixed due_date_is_fixed due_date_from_milestones due_date_sourcing_milestone_title end_date due_date due_date_fixed due_date_is_fixed due_date_from_milestones due_date_sourcing_milestone_title
start_date_sourcing_milestone_dates due_date_sourcing_milestone_dates
]) ])
expect(meta_data['author']).to eq({ expect(meta_data['author']).to eq({
'name' => user.name, 'name' => user.name,
...@@ -40,9 +42,13 @@ describe EpicsHelper do ...@@ -40,9 +42,13 @@ describe EpicsHelper do
'src' => 'icon_path' 'src' => 'icon_path'
}) })
expect(meta_data['start_date']).to eq('2000-01-01') expect(meta_data['start_date']).to eq('2000-01-01')
expect(meta_data['start_date_sourcing_milestone_title']).to eq(milestone.title) expect(meta_data['start_date_sourcing_milestone_title']).to eq(milestone1.title)
expect(meta_data['start_date_sourcing_milestone_dates']['start_date']).to eq(milestone1.start_date.to_s)
expect(meta_data['start_date_sourcing_milestone_dates']['due_date']).to eq(milestone1.due_date.to_s)
expect(meta_data['due_date']).to eq('2000-01-02') expect(meta_data['due_date']).to eq('2000-01-02')
expect(meta_data['due_date_sourcing_milestone_title']).to eq(milestone.title) expect(meta_data['due_date_sourcing_milestone_title']).to eq(milestone2.title)
expect(meta_data['due_date_sourcing_milestone_dates']['start_date']).to eq(milestone2.start_date.to_s)
expect(meta_data['due_date_sourcing_milestone_dates']['due_date']).to eq(milestone2.due_date.to_s)
end end
context 'when a user can update an epic' do context 'when a user can update an epic' do
...@@ -52,9 +58,9 @@ describe EpicsHelper do ...@@ -52,9 +58,9 @@ describe EpicsHelper do
create( create(
:epic, :epic,
author: user, author: user,
start_date_sourcing_milestone: milestone, start_date_sourcing_milestone: milestone1,
start_date: Date.new(2000, 1, 1), start_date: Date.new(2000, 1, 1),
due_date_sourcing_milestone: milestone, due_date_sourcing_milestone: milestone2,
due_date: Date.new(2000, 1, 2) due_date: Date.new(2000, 1, 2)
) )
end end
...@@ -71,11 +77,12 @@ describe EpicsHelper do ...@@ -71,11 +77,12 @@ describe EpicsHelper do
created author epic_id todo_exists todo_path state created author epic_id todo_exists todo_path state
start_date start_date_fixed start_date_is_fixed start_date_from_milestones start_date_sourcing_milestone_title start_date start_date_fixed start_date_is_fixed start_date_from_milestones start_date_sourcing_milestone_title
end_date due_date due_date_fixed due_date_is_fixed due_date_from_milestones due_date_sourcing_milestone_title end_date due_date due_date_fixed due_date_is_fixed due_date_from_milestones due_date_sourcing_milestone_title
start_date_sourcing_milestone_dates due_date_sourcing_milestone_dates
]) ])
expect(meta_data['start_date']).to eq('2000-01-01') expect(meta_data['start_date']).to eq('2000-01-01')
expect(meta_data['start_date_sourcing_milestone_title']).to eq(milestone.title) expect(meta_data['start_date_sourcing_milestone_title']).to eq(milestone1.title)
expect(meta_data['due_date']).to eq('2000-01-02') expect(meta_data['due_date']).to eq('2000-01-02')
expect(meta_data['due_date_sourcing_milestone_title']).to eq(milestone.title) expect(meta_data['due_date_sourcing_milestone_title']).to eq(milestone2.title)
end end
end end
end end
......
...@@ -57,7 +57,15 @@ export const contentProps = { ...@@ -57,7 +57,15 @@ export const contentProps = {
dueDateIsFixed: true, dueDateIsFixed: true,
dueDateFromMilestones: '', dueDateFromMilestones: '',
startDateSourcingMilestoneTitle: 'Milestone for Start Date', startDateSourcingMilestoneTitle: 'Milestone for Start Date',
startDateSourcingMilestoneDates: {
startDate: '2010-01-01',
dueDate: '2019-12-31',
},
dueDateSourcingMilestoneTitle: 'Milestone for End Date', dueDateSourcingMilestoneTitle: 'Milestone for End Date',
dueDateSourcingMilestoneDates: {
startDate: '2020-01-01',
dueDate: '2029-12-31',
},
labels: mockLabels, labels: mockLabels,
participants: mockParticipants, participants: mockParticipants,
subscribed: true, subscribed: true,
......
...@@ -32,7 +32,9 @@ describe('epicSidebar', () => { ...@@ -32,7 +32,9 @@ describe('epicSidebar', () => {
dueDateFixed, dueDateFixed,
dueDateFromMilestones, dueDateFromMilestones,
startDateSourcingMilestoneTitle, startDateSourcingMilestoneTitle,
startDateSourcingMilestoneDates,
dueDateSourcingMilestoneTitle, dueDateSourcingMilestoneTitle,
dueDateSourcingMilestoneDates,
} = props; } = props;
const defaultPropsData = { const defaultPropsData = {
...@@ -50,7 +52,9 @@ describe('epicSidebar', () => { ...@@ -50,7 +52,9 @@ describe('epicSidebar', () => {
dueDateFromMilestones, dueDateFromMilestones,
updatePath: updateEndpoint, updatePath: updateEndpoint,
startDateSourcingMilestoneTitle, startDateSourcingMilestoneTitle,
startDateSourcingMilestoneDates,
dueDateSourcingMilestoneTitle, dueDateSourcingMilestoneTitle,
dueDateSourcingMilestoneDates,
toggleSubscriptionPath, toggleSubscriptionPath,
labelsPath, labelsPath,
labelsWebUrl, labelsWebUrl,
...@@ -174,6 +178,44 @@ describe('epicSidebar', () => { ...@@ -174,6 +178,44 @@ describe('epicSidebar', () => {
it('returns tooltip string for milestone', () => { it('returns tooltip string for milestone', () => {
expect(vm.getDateFromMilestonesTooltip('start')).toBe('To schedule your epic\'s start date based on milestones, assign a milestone with a start date to any issue in the epic.'); expect(vm.getDateFromMilestonesTooltip('start')).toBe('To schedule your epic\'s start date based on milestones, assign a milestone with a start date to any issue in the epic.');
}); });
it('returns tooltip string with milestone dates', () => {
const vmDatesFromMilestones = mountComponent(
EpicSidebar,
Object.assign({}, defaultPropsData, {
startDateFromMilestones: startDateSourcingMilestoneDates.startDate,
dueDateFromMilestones: dueDateSourcingMilestoneDates.dueDate,
})
);
expect(vmDatesFromMilestones.getDateFromMilestonesTooltip('start')).toBe('Milestone for Start Date<br/><span class="text-tertiary">Jan 1, 2010 – Dec 31, 2019</span>');
vmDatesFromMilestones.$destroy();
});
it('returns tooltip string with milestone dates when dates are from same year', () => {
const startDate = '2018-01-01';
const dueDate = '2018-03-31';
const vmDatesFromMilestones = mountComponent(
EpicSidebar,
Object.assign({}, defaultPropsData, {
startDateSourcingMilestoneDates: {
startDate,
dueDate,
},
dueDateSourcingMilestoneDates: {
startDate,
dueDate,
},
startDateFromMilestones: startDate,
dueDateFromMilestones: dueDate,
})
);
expect(vmDatesFromMilestones.getDateFromMilestonesTooltip('start')).toBe('Milestone for Start Date<br/><span class="text-tertiary">Jan 1 – Mar 31, 2018</span>');
vmDatesFromMilestones.$destroy();
});
}); });
describe('toggleSidebar', () => { describe('toggleSidebar', () => {
......
...@@ -7458,6 +7458,9 @@ msgstr "" ...@@ -7458,6 +7458,9 @@ msgstr ""
msgid "Start a review" msgid "Start a review"
msgstr "" msgstr ""
msgid "Start and due date"
msgstr ""
msgid "Start date" msgid "Start date"
msgstr "" msgstr ""
......
...@@ -76,5 +76,11 @@ describe('collapsedGroupedDatePicker', () => { ...@@ -76,5 +76,11 @@ describe('collapsedGroupedDatePicker', () => {
expect(icons.length).toEqual(1); expect(icons.length).toEqual(1);
expect(icons[0].innerText.trim()).toEqual('None'); expect(icons[0].innerText.trim()).toEqual('None');
}); });
it('should have tooltip as `Start and due dates`', () => {
const icons = vm.$el.querySelectorAll('.sidebar-collapsed-icon');
expect(icons[0].dataset.originalTitle).toBe('Start and due date');
});
}); });
}); });
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