diff options
author | Dennis Tang <dennis@dennistang.net> | 2018-04-19 14:43:20 +0000 |
---|---|---|
committer | Rémy Coutable <remy@rymai.me> | 2018-04-19 14:43:20 +0000 |
commit | 49b85262c5a1944d7fdb50e43900a4adb13aba6c (patch) | |
tree | 9a24b282724d1fcfd9eec599db891a0cb2c6e517 /app/assets/javascripts | |
parent | 79905b5a0507bc75d6a960d9d85cce281e9041b3 (diff) | |
download | gitlab-ce-49b85262c5a1944d7fdb50e43900a4adb13aba6c.tar.gz |
Resolve "Improve tooltips of collapsed sidebars"
Diffstat (limited to 'app/assets/javascripts')
13 files changed, 166 insertions, 43 deletions
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js index 842a4255f08..4164149dd06 100644 --- a/app/assets/javascripts/due_date_select.js +++ b/app/assets/javascripts/due_date_select.js @@ -2,7 +2,9 @@ import $ from 'jquery'; import Pikaday from 'pikaday'; +import { __ } from '~/locale'; import axios from './lib/utils/axios_utils'; +import { timeFor } from './lib/utils/datetime_utility'; import { parsePikadayDate, pikadayToString } from './lib/utils/datefix'; class DueDateSelect { @@ -14,6 +16,7 @@ class DueDateSelect { this.$dropdownParent = $dropdownParent; this.$datePicker = $dropdownParent.find('.js-due-date-calendar'); this.$block = $block; + this.$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon'); this.$selectbox = $dropdown.closest('.selectbox'); this.$value = $block.find('.value'); this.$valueContent = $block.find('.value-content'); @@ -128,7 +131,8 @@ class DueDateSelect { submitSelectedDate(isDropdown) { const selectedDateValue = this.datePayload[this.abilityName].due_date; - const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value'; + const hasDueDate = this.displayedDate !== 'No due date'; + const displayedDateStyle = hasDueDate ? 'bold' : 'no-value'; this.$loading.removeClass('hidden').fadeIn(); @@ -145,10 +149,13 @@ class DueDateSelect { return axios.put(this.issueUpdateURL, this.datePayload) .then(() => { + const tooltipText = hasDueDate ? `${__('Due date')}<br />${selectedDateValue} (${timeFor(selectedDateValue)})` : __('Due date'); if (isDropdown) { this.$dropdown.trigger('loaded.gl.dropdown'); this.$dropdown.dropdown('toggle'); } + this.$sidebarCollapsedValue.attr('data-original-title', tooltipText); + return this.$loading.fadeOut(); }); } diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js index d0050abb8e9..9b62cfb8206 100644 --- a/app/assets/javascripts/labels_select.js +++ b/app/assets/javascripts/labels_select.js @@ -83,7 +83,7 @@ export default class LabelsSelect { $dropdown.trigger('loading.gl.dropdown'); axios.put(issueUpdateURL, data) .then(({ data }) => { - var labelCount, template, labelTooltipTitle, labelTitles; + var labelCount, template, labelTooltipTitle, labelTitles, formattedLabels; $loading.fadeOut(); $dropdown.trigger('loaded.gl.dropdown'); $selectbox.hide(); @@ -115,8 +115,7 @@ export default class LabelsSelect { labelTooltipTitle = labelTitles.join(', '); } else { - labelTooltipTitle = ''; - $sidebarLabelTooltip.tooltip('destroy'); + labelTooltipTitle = __('Labels'); } $sidebarLabelTooltip diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index d0a2b27b0e6..7e9a50a885d 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -4,6 +4,7 @@ import $ from 'jquery'; import _ from 'underscore'; +import { __ } from '~/locale'; import axios from './lib/utils/axios_utils'; import { timeFor } from './lib/utils/datetime_utility'; import ModalStore from './boards/stores/modal_store'; @@ -25,7 +26,7 @@ export default class MilestoneSelect { } $els.each((i, dropdown) => { - let collapsedSidebarLabelTemplate, milestoneLinkNoneTemplate, milestoneLinkTemplate, selectedMilestone, selectedMilestoneDefault; + let milestoneLinkNoneTemplate, milestoneLinkTemplate, selectedMilestone, selectedMilestoneDefault; const $dropdown = $(dropdown); const projectId = $dropdown.data('projectId'); const milestonesUrl = $dropdown.data('milestones'); @@ -52,7 +53,6 @@ export default class MilestoneSelect { if (issueUpdateURL) { milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>'); milestoneLinkNoneTemplate = '<span class="no-value">None</span>'; - collapsedSidebarLabelTemplate = _.template('<span class="has-tooltip" data-container="body" title="<%- name %><br /><%- remaining %>" data-placement="left" data-html="true"> <%- title %> </span>'); } return $dropdown.glDropdown({ showMenuAbove: showMenuAbove, @@ -214,10 +214,16 @@ export default class MilestoneSelect { data.milestone.remaining = timeFor(data.milestone.due_date); data.milestone.name = data.milestone.title; $value.html(milestoneLinkTemplate(data.milestone)); - return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); + return $sidebarCollapsedValue + .attr('data-original-title', `${data.milestone.name}<br />${data.milestone.remaining}`) + .find('span') + .text(data.milestone.title); } else { $value.html(milestoneLinkNoneTemplate); - return $sidebarCollapsedValue.find('span').text('No'); + return $sidebarCollapsedValue + .attr('data-original-title', __('Milestone')) + .find('span') + .text(__('None')); } }) .catch(() => { diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js index 2088a49590a..6eb0b62fa1c 100644 --- a/app/assets/javascripts/right_sidebar.js +++ b/app/assets/javascripts/right_sidebar.js @@ -5,6 +5,7 @@ import _ from 'underscore'; import Cookies from 'js-cookie'; import flash from './flash'; import axios from './lib/utils/axios_utils'; +import { __ } from './locale'; function Sidebar(currentUser) { this.toggleTodo = this.toggleTodo.bind(this); @@ -41,12 +42,14 @@ Sidebar.prototype.addEventListeners = function() { }; Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { - var $allGutterToggleIcons, $this, $thisIcon; + var $allGutterToggleIcons, $this, isExpanded, tooltipLabel; e.preventDefault(); $this = $(this); - $thisIcon = $this.find('i'); + isExpanded = $this.find('i').hasClass('fa-angle-double-right'); + tooltipLabel = isExpanded ? __('Expand sidebar') : __('Collapse sidebar'); $allGutterToggleIcons = $('.js-sidebar-toggle i'); - if ($thisIcon.hasClass('fa-angle-double-right')) { + + if (isExpanded) { $allGutterToggleIcons.removeClass('fa-angle-double-right').addClass('fa-angle-double-left'); $('aside.right-sidebar').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); $('.layout-page').removeClass('right-sidebar-expanded').addClass('right-sidebar-collapsed'); @@ -57,6 +60,9 @@ Sidebar.prototype.sidebarToggleClicked = function (e, triggered) { if (gl.lazyLoader) gl.lazyLoader.loadCheck(); } + + $this.attr('data-original-title', tooltipLabel); + if (!triggered) { Cookies.set("collapsed_gutter", $('.right-sidebar').hasClass('right-sidebar-collapsed')); } diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue index 1e7f46454bf..2d00e8ac7e0 100644 --- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue +++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue @@ -1,6 +1,12 @@ <script> +import { __ } from '~/locale'; +import tooltip from '~/vue_shared/directives/tooltip'; + export default { name: 'Assignees', + directives: { + tooltip, + }, props: { rootPath: { type: String, @@ -14,6 +20,11 @@ export default { type: Boolean, required: true, }, + issuableType: { + type: String, + require: true, + default: 'issue', + }, }, data() { return { @@ -62,6 +73,12 @@ export default { names.push(`+ ${this.users.length - maxRender} more`); } + if (!this.users.length) { + const emptyTooltipLabel = this.issuableType === 'issue' ? + __('Assignee(s)') : __('Assignee'); + names.push(emptyTooltipLabel); + } + return names.join(', '); }, sidebarAvatarCounter() { @@ -109,7 +126,8 @@ export default { <div> <div class="sidebar-collapsed-icon sidebar-collapsed-user" - :class="{ 'multiple-users': hasMoreThanOneAssignee, 'has-tooltip': hasAssignees }" + :class="{ 'multiple-users': hasMoreThanOneAssignee }" + v-tooltip data-container="body" data-placement="left" :title="collapsedTooltipTitle" diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue index 3c6b9c27814..b04a2eff798 100644 --- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue +++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue @@ -1,9 +1,9 @@ <script> -import Flash from '../../../flash'; +import Flash from '~/flash'; +import eventHub from '~/sidebar/event_hub'; +import Store from '~/sidebar/stores/sidebar_store'; import AssigneeTitle from './assignee_title.vue'; import Assignees from './assignees.vue'; -import Store from '../../stores/sidebar_store'; -import eventHub from '../../event_hub'; export default { name: 'SidebarAssignees', @@ -25,6 +25,11 @@ export default { required: false, default: false, }, + issuableType: { + type: String, + require: true, + default: 'issue', + }, }, data() { return { @@ -90,6 +95,7 @@ export default { :users="store.assignees" :editable="store.editable" @assign-self="assignSelf" + :issuable-type="issuableType" /> </div> </template> diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue index ceb02309959..7f0de722f61 100644 --- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue @@ -1,15 +1,19 @@ <script> -import Flash from '../../../flash'; +import { __ } from '~/locale'; +import Flash from '~/flash'; +import tooltip from '~/vue_shared/directives/tooltip'; +import Icon from '~/vue_shared/components/icon.vue'; +import eventHub from '~/sidebar/event_hub'; import editForm from './edit_form.vue'; -import Icon from '../../../vue_shared/components/icon.vue'; -import { __ } from '../../../locale'; -import eventHub from '../../event_hub'; export default { components: { editForm, Icon, }, + directives: { + tooltip, + }, props: { isConfidential: { required: true, @@ -33,6 +37,9 @@ export default { confidentialityIcon() { return this.isConfidential ? 'eye-slash' : 'eye'; }, + tooltipLabel() { + return this.isConfidential ? __('Confidential') : __('Not confidential'); + }, }, created() { eventHub.$on('closeConfidentialityForm', this.toggleForm); @@ -65,6 +72,10 @@ export default { <div class="sidebar-collapsed-icon" @click="toggleForm" + v-tooltip + data-container="body" + data-placement="left" + :title="tooltipLabel" > <icon :name="confidentialityIcon" diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue index e4893451af3..1a5e7b67eca 100644 --- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue +++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue @@ -1,15 +1,22 @@ <script> +import { __ } from '~/locale'; import Flash from '~/flash'; +import tooltip from '~/vue_shared/directives/tooltip'; +import issuableMixin from '~/vue_shared/mixins/issuable'; +import Icon from '~/vue_shared/components/icon.vue'; +import eventHub from '~/sidebar/event_hub'; import editForm from './edit_form.vue'; -import issuableMixin from '../../../vue_shared/mixins/issuable'; -import Icon from '../../../vue_shared/components/icon.vue'; -import eventHub from '../../event_hub'; export default { components: { editForm, Icon, }, + + directives: { + tooltip, + }, + mixins: [issuableMixin], props: { @@ -44,6 +51,10 @@ export default { isLockDialogOpen() { return this.mediator.store.isLockDialogOpen; }, + + tooltipLabel() { + return this.isLocked ? __('Locked') : __('Unlocked'); + }, }, created() { @@ -85,6 +96,10 @@ export default { <div class="sidebar-collapsed-icon" @click="toggleForm" + v-tooltip + data-container="body" + data-placement="left" + :title="tooltipLabel" > <icon :name="lockIcon" diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue index 006a6d2905d..6d95153af28 100644 --- a/app/assets/javascripts/sidebar/components/participants/participants.vue +++ b/app/assets/javascripts/sidebar/components/participants/participants.vue @@ -1,9 +1,13 @@ <script> - import { __, n__, sprintf } from '../../../locale'; - import loadingIcon from '../../../vue_shared/components/loading_icon.vue'; - import userAvatarImage from '../../../vue_shared/components/user_avatar/user_avatar_image.vue'; + import { __, n__, sprintf } from '~/locale'; + import tooltip from '~/vue_shared/directives/tooltip'; + import loadingIcon from '~/vue_shared/components/loading_icon.vue'; + import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue'; export default { + directives: { + tooltip, + }, components: { loadingIcon, userAvatarImage, @@ -72,7 +76,13 @@ <template> <div> - <div class="sidebar-collapsed-icon"> + <div + class="sidebar-collapsed-icon" + v-tooltip + data-container="body" + data-placement="left" + :title="participantLabel" + > <i class="fa fa-users" aria-hidden="true" diff --git a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue index 3b86f1145d1..9d9ee9dea4d 100644 --- a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue +++ b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue @@ -1,12 +1,17 @@ <script> - import icon from '../../../vue_shared/components/icon.vue'; - import { abbreviateTime } from '../../../lib/utils/pretty_time'; + import { __, sprintf } from '~/locale'; + import { abbreviateTime } from '~/lib/utils/pretty_time'; + import icon from '~/vue_shared/components/icon.vue'; + import tooltip from '~/vue_shared/directives/tooltip'; export default { name: 'TimeTrackingCollapsedState', components: { icon, }, + directives: { + tooltip, + }, props: { showComparisonState: { type: Boolean, @@ -79,6 +84,21 @@ return ''; }, + timeTrackedTooltipText() { + let title; + if (this.showComparisonState) { + title = __('Time remaining'); + } else if (this.showEstimateOnlyState) { + title = __('Estimated'); + } else if (this.showSpentOnlyState) { + title = __('Time spent'); + } + + return sprintf('%{title}: %{text}', ({ title, text: this.text })); + }, + tooltipText() { + return this.showNoTimeTrackingState ? __('Time tracking') : this.timeTrackedTooltipText; + }, }, methods: { abbreviateTime(timeStr) { @@ -89,7 +109,13 @@ </script> <template> - <div class="sidebar-collapsed-icon"> + <div + class="sidebar-collapsed-icon" + v-tooltip + data-container="body" + data-placement="left" + :title="tooltipText" + > <icon name="timer" /> <div class="time-tracking-collapsed-summary"> <div :class="divClass"> diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index 9f5d852260e..26eb4cffba3 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -27,6 +27,7 @@ function mountAssigneesComponent(mediator) { mediator, field: el.dataset.field, signedIn: el.hasAttribute('data-signed-in'), + issuableType: gl.utils.isInIssuePage() ? 'issue' : 'merge_request', }, }), }); diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js index 520a0b3f424..8486019897d 100644 --- a/app/assets/javascripts/users_select.js +++ b/app/assets/javascripts/users_select.js @@ -5,6 +5,7 @@ import $ from 'jquery'; import _ from 'underscore'; import axios from './lib/utils/axios_utils'; +import { __ } from './locale'; import ModalStore from './boards/stores/modal_store'; // TODO: remove eventHub hack after code splitting refactor @@ -182,7 +183,7 @@ function UsersSelect(currentUser, els, options = {}) { return axios.put(issueURL, data) .then(({ data }) => { - var user; + var user, tooltipTitle; $dropdown.trigger('loaded.gl.dropdown'); $loading.fadeOut(); if (data.assignee) { @@ -191,15 +192,17 @@ function UsersSelect(currentUser, els, options = {}) { username: data.assignee.username, avatar: data.assignee.avatar_url }; + tooltipTitle = _.escape(user.name); } else { user = { name: 'Unassigned', username: '', avatar: '' }; + tooltipTitle = __('Assignee'); } $value.html(assigneeTemplate(user)); - $collapsedSidebar.attr('title', _.escape(user.name)).tooltip('fixTitle'); + $collapsedSidebar.attr('title', tooltipTitle).tooltip('fixTitle'); return $collapsedSidebar.html(collapsedAssigneeTemplate(user)); }); }; diff --git a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue index 8211d425b1f..de6f8c32e74 100644 --- a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue +++ b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue @@ -1,18 +1,29 @@ <script> - export default { - name: 'ToggleSidebar', - props: { - collapsed: { - type: Boolean, - required: true, - }, +import { __ } from '~/locale'; +import tooltip from '~/vue_shared/directives/tooltip'; + +export default { + name: 'ToggleSidebar', + directives: { + tooltip, + }, + props: { + collapsed: { + type: Boolean, + required: true, + }, + }, + computed: { + tooltipLabel() { + return this.collapsed ? __('Expand sidebar') : __('Collapse sidebar'); }, - methods: { - toggle() { - this.$emit('toggle'); - }, + }, + methods: { + toggle() { + this.$emit('toggle'); }, - }; + }, +}; </script> <template> @@ -20,6 +31,10 @@ type="button" class="btn btn-blank gutter-toggle btn-sidebar-action" @click="toggle" + v-tooltip + data-container="body" + data-placement="left" + :title="tooltipLabel" > <i aria-label="toggle collapse" |