Commit 6cb575d6 authored by Eric Eastwood's avatar Eric Eastwood

Add support for multiple tooltips in the same Vue component

Fix https://gitlab.com/gitlab-org/gitlab-ce/issues/33223
parent bf0b3d83
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import playIconSvg from 'icons/_icon_play.svg'; import playIconSvg from 'icons/_icon_play.svg';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
props: { props: {
...@@ -12,6 +13,10 @@ export default { ...@@ -12,6 +13,10 @@ export default {
}, },
}, },
directives: {
tooltip,
},
components: { components: {
loadingIcon, loadingIcon,
}, },
...@@ -33,8 +38,6 @@ export default { ...@@ -33,8 +38,6 @@ export default {
onClickAction(endpoint) { onClickAction(endpoint) {
this.isLoading = true; this.isLoading = true;
$(this.$refs.tooltip).tooltip('destroy');
eventHub.$emit('postAction', endpoint); eventHub.$emit('postAction', endpoint);
}, },
...@@ -53,11 +56,11 @@ export default { ...@@ -53,11 +56,11 @@ export default {
class="btn-group" class="btn-group"
role="group"> role="group">
<button <button
v-tooltip
type="button" type="button"
class="dropdown btn btn-default dropdown-new js-dropdown-play-icon-container has-tooltip" class="dropdown btn btn-default dropdown-new js-dropdown-play-icon-container"
data-container="body" data-container="body"
data-toggle="dropdown" data-toggle="dropdown"
ref="tooltip"
:title="title" :title="title"
:aria-label="title" :aria-label="title"
:disabled="isLoading"> :disabled="isLoading">
......
<script> <script>
import tooltip from '../../vue_shared/directives/tooltip';
/** /**
* Renders the external url link in environments table. * Renders the external url link in environments table.
*/ */
...@@ -10,6 +12,10 @@ export default { ...@@ -10,6 +12,10 @@ export default {
}, },
}, },
directives: {
tooltip,
},
computed: { computed: {
title() { title() {
return 'Open'; return 'Open';
...@@ -19,7 +25,8 @@ export default { ...@@ -19,7 +25,8 @@ export default {
</script> </script>
<template> <template>
<a <a
class="btn external-url has-tooltip" v-tooltip
class="btn external-url"
data-container="body" data-container="body"
target="_blank" target="_blank"
rel="noopener noreferrer nofollow" rel="noopener noreferrer nofollow"
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
/** /**
* Renders the Monitoring (Metrics) link in environments table. * Renders the Monitoring (Metrics) link in environments table.
*/ */
import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
props: { props: {
monitoringUrl: { monitoringUrl: {
...@@ -10,6 +12,10 @@ export default { ...@@ -10,6 +12,10 @@ export default {
}, },
}, },
directives: {
tooltip,
},
computed: { computed: {
title() { title() {
return 'Monitoring'; return 'Monitoring';
...@@ -19,7 +25,8 @@ export default { ...@@ -19,7 +25,8 @@ export default {
</script> </script>
<template> <template>
<a <a
class="btn monitoring-url has-tooltip hidden-xs hidden-sm" v-tooltip
class="btn monitoring-url hidden-xs hidden-sm"
data-container="body" data-container="body"
rel="noopener noreferrer nofollow" rel="noopener noreferrer nofollow"
:href="monitoringUrl" :href="monitoringUrl"
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
props: { props: {
...@@ -14,6 +15,10 @@ export default { ...@@ -14,6 +15,10 @@ export default {
}, },
}, },
directives: {
tooltip,
},
data() { data() {
return { return {
isLoading: false, isLoading: false,
...@@ -46,8 +51,9 @@ export default { ...@@ -46,8 +51,9 @@ export default {
</script> </script>
<template> <template>
<button <button
v-tooltip
type="button" type="button"
class="btn stop-env-link has-tooltip hidden-xs hidden-sm" class="btn stop-env-link hidden-xs hidden-sm"
data-container="body" data-container="body"
@click="onClick" @click="onClick"
:disabled="isLoading" :disabled="isLoading"
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* Used in environments table. * Used in environments table.
*/ */
import terminalIconSvg from 'icons/_icon_terminal.svg'; import terminalIconSvg from 'icons/_icon_terminal.svg';
import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
props: { props: {
...@@ -14,6 +15,10 @@ export default { ...@@ -14,6 +15,10 @@ export default {
}, },
}, },
directives: {
tooltip,
},
data() { data() {
return { return {
terminalIconSvg, terminalIconSvg,
...@@ -29,7 +34,8 @@ export default { ...@@ -29,7 +34,8 @@ export default {
</script> </script>
<template> <template>
<a <a
class="btn terminal-button has-tooltip hidden-xs hidden-sm" v-tooltip
class="btn terminal-button hidden-xs hidden-sm"
data-container="body" data-container="body"
:title="title" :title="title"
:aria-label="title" :aria-label="title"
......
<script> <script>
import tooltipMixin from '../../../vue_shared/mixins/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
export default { export default {
mixins: [ directives: {
tooltipMixin, tooltip,
], },
props: { props: {
formState: { formState: {
type: Object, type: Object,
...@@ -71,9 +71,9 @@ ...@@ -71,9 +71,9 @@
data-placeholder="Move to a different project" /> data-placeholder="Move to a different project" />
</div> </div>
<span <span
v-tooltip
data-placement="auto top" data-placement="auto top"
title="Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location." title="Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.">
ref="tooltip">
<i <i
class="fa fa-question-circle" class="fa fa-question-circle"
aria-hidden="true"> aria-hidden="true">
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltipMixin from '../../vue_shared/mixins/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
props: { props: {
...@@ -28,12 +28,12 @@ export default { ...@@ -28,12 +28,12 @@ export default {
required: false, required: false,
}, },
}, },
directives: {
tooltip,
},
components: { components: {
loadingIcon, loadingIcon,
}, },
mixins: [
tooltipMixin,
],
data() { data() {
return { return {
isLoading: false, isLoading: false,
...@@ -58,7 +58,6 @@ export default { ...@@ -58,7 +58,6 @@ export default {
makeRequest() { makeRequest() {
this.isLoading = true; this.isLoading = true;
$(this.$refs.tooltip).tooltip('destroy');
eventHub.$emit('postAction', this.endpoint); eventHub.$emit('postAction', this.endpoint);
}, },
}, },
...@@ -67,6 +66,7 @@ export default { ...@@ -67,6 +66,7 @@ export default {
<template> <template>
<button <button
v-tooltip
type="button" type="button"
@click="onClick" @click="onClick"
:class="buttonClass" :class="buttonClass"
...@@ -74,7 +74,6 @@ export default { ...@@ -74,7 +74,6 @@ export default {
:aria-label="title" :aria-label="title"
data-container="body" data-container="body"
data-placement="top" data-placement="top"
ref="tooltip"
:disabled="isLoading"> :disabled="isLoading">
<i <i
:class="iconClass" :class="iconClass"
......
<script> <script>
import getActionIcon from '../../../vue_shared/ci_action_icons'; import getActionIcon from '../../../vue_shared/ci_action_icons';
import tooltipMixin from '../../../vue_shared/mixins/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
/** /**
* Renders either a cancel, retry or play icon pointing to the given path. * Renders either a cancel, retry or play icon pointing to the given path.
...@@ -29,9 +29,9 @@ ...@@ -29,9 +29,9 @@
}, },
}, },
mixins: [ directives: {
tooltipMixin, tooltip,
], },
computed: { computed: {
actionIconSvg() { actionIconSvg() {
...@@ -46,12 +46,11 @@ ...@@ -46,12 +46,11 @@
</script> </script>
<template> <template>
<a <a
v-tooltip
:data-method="actionMethod" :data-method="actionMethod"
:title="tooltipText" :title="tooltipText"
:href="link" :href="link"
ref="tooltip"
class="ci-action-icon-container" class="ci-action-icon-container"
data-toggle="tooltip"
data-container="body"> data-container="body">
<i <i
......
<script> <script>
import getActionIcon from '../../../vue_shared/ci_action_icons'; import getActionIcon from '../../../vue_shared/ci_action_icons';
import tooltipMixin from '../../../vue_shared/mixins/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
/** /**
* Renders either a cancel, retry or play icon pointing to the given path. * Renders either a cancel, retry or play icon pointing to the given path.
...@@ -29,9 +29,9 @@ ...@@ -29,9 +29,9 @@
}, },
}, },
mixins: [ directives: {
tooltipMixin, tooltip,
], },
computed: { computed: {
actionIconSvg() { actionIconSvg() {
...@@ -42,13 +42,12 @@ ...@@ -42,13 +42,12 @@
</script> </script>
<template> <template>
<a <a
v-tooltip
:data-method="actionMethod" :data-method="actionMethod"
:title="tooltipText" :title="tooltipText"
:href="link" :href="link"
ref="tooltip"
rel="nofollow" rel="nofollow"
class="ci-action-icon-wrapper js-ci-status-icon" class="ci-action-icon-wrapper js-ci-status-icon"
data-toggle="tooltip"
data-container="body" data-container="body"
v-html="actionIconSvg" v-html="actionIconSvg"
aria-label="Job's action"> aria-label="Job's action">
......
<script> <script>
import jobNameComponent from './job_name_component.vue'; import jobNameComponent from './job_name_component.vue';
import jobComponent from './job_component.vue'; import jobComponent from './job_component.vue';
import tooltipMixin from '../../../vue_shared/mixins/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
/** /**
* Renders the dropdown for the pipeline graph. * Renders the dropdown for the pipeline graph.
...@@ -34,9 +34,9 @@ ...@@ -34,9 +34,9 @@
}, },
}, },
mixins: [ directives: {
tooltipMixin, tooltip,
], },
components: { components: {
jobComponent, jobComponent,
...@@ -53,12 +53,12 @@ ...@@ -53,12 +53,12 @@
<template> <template>
<div> <div>
<button <button
v-tooltip
type="button" type="button"
data-toggle="dropdown" data-toggle="dropdown"
data-container="body" data-container="body"
class="dropdown-menu-toggle build-content" class="dropdown-menu-toggle build-content"
:title="tooltipText" :title="tooltipText">
ref="tooltip">
<job-name-component <job-name-component
:name="job.name" :name="job.name"
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import actionComponent from './action_component.vue'; import actionComponent from './action_component.vue';
import dropdownActionComponent from './dropdown_action_component.vue'; import dropdownActionComponent from './dropdown_action_component.vue';
import jobNameComponent from './job_name_component.vue'; import jobNameComponent from './job_name_component.vue';
import tooltipMixin from '../../../vue_shared/mixins/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
/** /**
* Renders the badge for the pipeline graph and the job's dropdown. * Renders the badge for the pipeline graph and the job's dropdown.
...@@ -54,9 +54,9 @@ ...@@ -54,9 +54,9 @@
jobNameComponent, jobNameComponent,
}, },
mixins: [ directives: {
tooltipMixin, tooltip,
], },
computed: { computed: {
tooltipText() { tooltipText() {
...@@ -77,12 +77,11 @@ ...@@ -77,12 +77,11 @@
<template> <template>
<div> <div>
<a <a
v-tooltip
v-if="job.status.details_path" v-if="job.status.details_path"
:href="job.status.details_path" :href="job.status.details_path"
:title="tooltipText" :title="tooltipText"
:class="cssClassJobName" :class="cssClassJobName"
ref="tooltip"
data-toggle="tooltip"
data-container="body"> data-container="body">
<job-name-component <job-name-component
...@@ -93,10 +92,9 @@ ...@@ -93,10 +92,9 @@
<div <div
v-else v-else
v-tooltip
:title="tooltipText" :title="tooltipText"
:class="cssClassJobName" :class="cssClassJobName"
ref="tooltip"
data-toggle="tooltip"
data-container="body"> data-container="body">
<job-name-component <job-name-component
......
<script> <script>
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import tooltipMixin from '../../vue_shared/mixins/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
props: { props: {
...@@ -12,9 +12,9 @@ export default { ...@@ -12,9 +12,9 @@ export default {
components: { components: {
userAvatarLink, userAvatarLink,
}, },
mixins: [ directives: {
tooltipMixin, tooltip,
], },
computed: { computed: {
user() { user() {
return this.pipeline.user; return this.pipeline.user;
...@@ -45,16 +45,16 @@ export default { ...@@ -45,16 +45,16 @@ export default {
<div class="label-container"> <div class="label-container">
<span <span
v-if="pipeline.flags.latest" v-if="pipeline.flags.latest"
v-tooltip
class="js-pipeline-url-latest label label-success" class="js-pipeline-url-latest label label-success"
title="Latest pipeline for this branch" title="Latest pipeline for this branch">
ref="tooltip">
latest latest
</span> </span>
<span <span
v-if="pipeline.flags.yaml_errors" v-if="pipeline.flags.yaml_errors"
v-tooltip
class="js-pipeline-url-yaml label label-danger" class="js-pipeline-url-yaml label label-danger"
:title="pipeline.yaml_errors" :title="pipeline.yaml_errors">
ref="tooltip">
yaml invalid yaml invalid
</span> </span>
<span <span
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import playIconSvg from 'icons/_icon_play.svg'; import playIconSvg from 'icons/_icon_play.svg';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
props: { props: {
...@@ -12,6 +13,9 @@ ...@@ -12,6 +13,9 @@
required: true, required: true,
}, },
}, },
directives: {
tooltip,
},
components: { components: {
loadingIcon, loadingIcon,
}, },
...@@ -25,8 +29,6 @@ ...@@ -25,8 +29,6 @@
onClickAction(endpoint) { onClickAction(endpoint) {
this.isLoading = true; this.isLoading = true;
$(this.$refs.tooltip).tooltip('destroy');
eventHub.$emit('postAction', endpoint); eventHub.$emit('postAction', endpoint);
}, },
...@@ -43,13 +45,13 @@ ...@@ -43,13 +45,13 @@
<template> <template>
<div class="btn-group"> <div class="btn-group">
<button <button
v-tooltip
type="button" type="button"
class="dropdown-new btn btn-default has-tooltip js-pipeline-dropdown-manual-actions" class="dropdown-new btn btn-default js-pipeline-dropdown-manual-actions"
title="Manual job" title="Manual job"
data-toggle="dropdown" data-toggle="dropdown"
data-placement="top" data-placement="top"
aria-label="Manual job" aria-label="Manual job"
ref="tooltip"
:disabled="isLoading"> :disabled="isLoading">
<span v-html="playIconSvg"></span> <span v-html="playIconSvg"></span>
<i <i
......
<script> <script>
import tooltipMixin from '../../vue_shared/mixins/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
props: { props: {
...@@ -8,9 +8,9 @@ ...@@ -8,9 +8,9 @@
required: true, required: true,
}, },
}, },
mixins: [ directives: {
tooltipMixin, tooltip,
], },
}; };
</script> </script>
<template> <template>
...@@ -18,12 +18,12 @@ ...@@ -18,12 +18,12 @@
class="btn-group" class="btn-group"
role="group"> role="group">
<button <button
v-tooltip
class="dropdown-toggle btn btn-default build-artifacts js-pipeline-dropdown-download" class="dropdown-toggle btn btn-default build-artifacts js-pipeline-dropdown-download"
title="Artifacts" title="Artifacts"
data-placement="top" data-placement="top"
data-toggle="dropdown" data-toggle="dropdown"
aria-label="Artifacts" aria-label="Artifacts">
ref="tooltip">
<i <i
class="fa fa-download" class="fa fa-download"
aria-hidden="true"> aria-hidden="true">
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
/* global Flash */ /* global Flash */
import { borderlessStatusIconEntityMap } from '../../vue_shared/ci_status_icons'; import { borderlessStatusIconEntityMap } from '../../vue_shared/ci_status_icons';
import loadingIcon from '../../vue_shared/components/loading_icon.vue'; import loadingIcon from '../../vue_shared/components/loading_icon.vue';
import tooltipMixin from '../../vue_shared/mixins/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
export default { export default {
props: { props: {
...@@ -32,9 +32,9 @@ export default { ...@@ -32,9 +32,9 @@ export default {
}, },
}, },
mixins: [ directives: {
tooltipMixin, tooltip,
], },
data() { data() {
return { return {
...@@ -132,7 +132,7 @@ export default { ...@@ -132,7 +132,7 @@ export default {
<template> <template>
<div class="dropdown"> <div class="dropdown">
<button <button
ref="tooltip" v-tooltip
:class="triggerButtonClass" :class="triggerButtonClass"
@click="onClickStage" @click="onClickStage"
class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button" class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
......
<script> <script>
import iconTimerSvg from 'icons/_icon_timer.svg'; import iconTimerSvg from 'icons/_icon_timer.svg';
import '../../lib/utils/datetime_utility'; import '../../lib/utils/datetime_utility';
import tooltipMixin from '../../vue_shared/mixins/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
import timeagoMixin from '../../vue_shared/mixins/timeago'; import timeagoMixin from '../../vue_shared/mixins/timeago';
export default { export default {
...@@ -16,9 +16,11 @@ ...@@ -16,9 +16,11 @@
}, },
}, },
mixins: [ mixins: [
tooltipMixin,
timeagoMixin, timeagoMixin,
], ],
directives: {
tooltip,
},
data() { data() {
return { return {
iconTimerSvg, iconTimerSvg,
...@@ -81,7 +83,7 @@ ...@@ -81,7 +83,7 @@
</i> </i>
<time <time
ref="tooltip" v-tooltip
data-placement="top" data-placement="top"
data-container="body" data-container="body"
:title="tooltipTitle(finishedTime)"> :title="tooltipTitle(finishedTime)">
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import ciIconBadge from './ci_badge_link.vue'; import ciIconBadge from './ci_badge_link.vue';
import loadingIcon from './loading_icon.vue'; import loadingIcon from './loading_icon.vue';
import timeagoTooltip from './time_ago_tooltip.vue'; import timeagoTooltip from './time_ago_tooltip.vue';
import tooltipMixin from '../mixins/tooltip'; import tooltip from '../directives/tooltip';
import userAvatarImage from './user_avatar/user_avatar_image.vue'; import userAvatarImage from './user_avatar/user_avatar_image.vue';
/** /**
...@@ -47,9 +47,9 @@ export default { ...@@ -47,9 +47,9 @@ export default {
}, },
}, },
mixins: [ directives: {
tooltipMixin, tooltip,
], },
components: { components: {
ciIconBadge, ciIconBadge,
...@@ -90,10 +90,10 @@ export default { ...@@ -90,10 +90,10 @@ export default {
<template v-if="user"> <template v-if="user">
<a <a
v-tooltip
:href="user.path" :href="user.path"
:title="user.email" :title="user.email"
class="js-user-link commit-committer-link" class="js-user-link commit-committer-link">
ref="tooltip">
<user-avatar-image <user-avatar-image
:img-src="user.avatar_url" :img-src="user.avatar_url"
......
<script> <script>
import tooltipMixin from '../../mixins/tooltip'; import tooltip from '../../directives/tooltip';
import toolbarButton from './toolbar_button.vue'; import toolbarButton from './toolbar_button.vue';
export default { export default {
mixins: [
tooltipMixin,
],
props: { props: {
previewMarkdown: { previewMarkdown: {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
}, },
directives: {
tooltip,
},
components: { components: {
toolbarButton, toolbarButton,
}, },
...@@ -94,13 +94,13 @@ ...@@ -94,13 +94,13 @@
</div> </div>
<div class="toolbar-group"> <div class="toolbar-group">
<button <button
v-tooltip
aria-label="Go full screen" aria-label="Go full screen"
class="toolbar-btn js-zen-enter" class="toolbar-btn js-zen-enter"
data-container="body" data-container="body"
tabindex="-1" tabindex="-1"
title="Go full screen" title="Go full screen"
type="button" type="button">
ref="tooltip">
<i <i
aria-hidden="true" aria-hidden="true"
class="fa fa-arrows-alt fa-fw"> class="fa fa-arrows-alt fa-fw">
......
<script> <script>
import tooltipMixin from '../../mixins/tooltip'; import tooltip from '../../directives/tooltip';
export default { export default {
mixins: [
tooltipMixin,
],
props: { props: {
buttonTitle: { buttonTitle: {
type: String, type: String,
...@@ -29,6 +26,9 @@ ...@@ -29,6 +26,9 @@
default: false, default: false,
}, },
}, },
directives: {
tooltip,
},
computed: { computed: {
iconClass() { iconClass() {
return `fa-${this.icon}`; return `fa-${this.icon}`;
...@@ -39,10 +39,10 @@ ...@@ -39,10 +39,10 @@
<template> <template>
<button <button
v-tooltip
type="button" type="button"
class="toolbar-btn js-md hidden-xs" class="toolbar-btn js-md hidden-xs"
tabindex="-1" tabindex="-1"
ref="tooltip"
data-container="body" data-container="body"
:data-md-tag="tag" :data-md-tag="tag"
:data-md-block="tagBlock" :data-md-block="tagBlock"
......
<script> <script>
import tooltipMixin from '../mixins/tooltip'; import tooltip from '../directives/tooltip';
import timeagoMixin from '../mixins/timeago'; import timeagoMixin from '../mixins/timeago';
import '../../lib/utils/datetime_utility'; import '../../lib/utils/datetime_utility';
...@@ -28,19 +28,21 @@ export default { ...@@ -28,19 +28,21 @@ export default {
}, },
mixins: [ mixins: [
tooltipMixin,
timeagoMixin, timeagoMixin,
], ],
directives: {
tooltip,
},
}; };
</script> </script>
<template> <template>
<time <time
v-tooltip
:class="cssClass" :class="cssClass"
class="js-vue-timeago"
:title="tooltipTitle(time)" :title="tooltipTitle(time)"
:data-placement="tooltipPlacement" :data-placement="tooltipPlacement"
data-container="body" data-container="body">
ref="tooltip">
{{timeFormated(time)}} {{timeFormated(time)}}
</time> </time>
</template> </template>
...@@ -16,11 +16,10 @@ ...@@ -16,11 +16,10 @@
*/ */
import defaultAvatarUrl from 'images/no_avatar.png'; import defaultAvatarUrl from 'images/no_avatar.png';
import TooltipMixin from '../../mixins/tooltip'; import tooltip from '../../directives/tooltip';
export default { export default {
name: 'UserAvatarImage', name: 'UserAvatarImage',
mixins: [TooltipMixin],
props: { props: {
imgSrc: { imgSrc: {
type: String, type: String,
...@@ -53,6 +52,9 @@ export default { ...@@ -53,6 +52,9 @@ export default {
default: 'top', default: 'top',
}, },
}, },
directives: {
tooltip,
},
computed: { computed: {
tooltipContainer() { tooltipContainer() {
return this.tooltipText ? 'body' : null; return this.tooltipText ? 'body' : null;
...@@ -72,6 +74,7 @@ export default { ...@@ -72,6 +74,7 @@ export default {
<template> <template>
<img <img
v-tooltip
class="avatar" class="avatar"
:class="[avatarSizeClass, cssClasses]" :class="[avatarSizeClass, cssClasses]"
:src="imageSource" :src="imageSource"
...@@ -81,6 +84,5 @@ export default { ...@@ -81,6 +84,5 @@ export default {
:data-container="tooltipContainer" :data-container="tooltipContainer"
:data-placement="tooltipPlacement" :data-placement="tooltipPlacement"
:title="tooltipText" :title="tooltipText"
ref="tooltip"
/> />
</template> </template>
export default {
bind(el) {
$(el).tooltip();
},
componentUpdated(el) {
$(el).tooltip('fixTitle');
},
unbind(el) {
$(el).tooltip('destroy');
},
};
export default {
mounted() {
$(this.$refs.tooltip).tooltip();
},
updated() {
$(this.$refs.tooltip).tooltip('fixTitle');
},
beforeDestroy() {
$(this.$refs.tooltip).tooltip('destroy');
},
};
...@@ -463,20 +463,24 @@ A forEach will cause side effects, it will be mutating the array being iterated. ...@@ -463,20 +463,24 @@ A forEach will cause side effects, it will be mutating the array being iterated.
1. `destroyed` 1. `destroyed`
#### Vue and Boostrap #### Vue and Boostrap
1. Tooltips: Do not rely on `has-tooltip` class name for vue components 1. Tooltips: Do not rely on `has-tooltip` class name for Vue components
```javascript ```javascript
// bad // bad
<span class="has-tooltip"> <span
class="has-tooltip"
title="Some tooltip text">
Text Text
</span> </span>
// good // good
<span data-toggle="tooltip"> <span
v-tooltip
title="Some tooltip text">
Text Text
</span> </span>
``` ```
1. Tooltips: When using a tooltip, include the tooltip mixin 1. Tooltips: When using a tooltip, include the tooltip directive, `./app/assets/javascripts/vue_shared/directives/tooltip.js`
1. Don't change `data-original-title`. 1. Don't change `data-original-title`.
```javascript ```javascript
......
...@@ -32,9 +32,16 @@ describe('Actions Component', () => { ...@@ -32,9 +32,16 @@ describe('Actions Component', () => {
}).$mount(); }).$mount();
}); });
describe('computed', () => {
it('title', () => {
expect(component.title).toEqual('Deploy to...');
});
});
it('should render a dropdown button with icon and title attribute', () => { it('should render a dropdown button with icon and title attribute', () => {
expect(component.$el.querySelector('.fa-caret-down')).toBeDefined(); expect(component.$el.querySelector('.fa-caret-down')).toBeDefined();
expect(component.$el.querySelector('.dropdown-new').getAttribute('title')).toEqual('Deploy to...'); expect(component.$el.querySelector('.dropdown-new').getAttribute('data-original-title')).toEqual('Deploy to...');
expect(component.$el.querySelector('.dropdown-new').getAttribute('aria-label')).toEqual('Deploy to...');
}); });
it('should render a dropdown with the provided list of actions', () => { it('should render a dropdown with the provided list of actions', () => {
......
...@@ -3,21 +3,30 @@ import monitoringComp from '~/environments/components/environment_monitoring.vue ...@@ -3,21 +3,30 @@ import monitoringComp from '~/environments/components/environment_monitoring.vue
describe('Monitoring Component', () => { describe('Monitoring Component', () => {
let MonitoringComponent; let MonitoringComponent;
let component;
const monitoringUrl = 'https://gitlab.com';
beforeEach(() => { beforeEach(() => {
MonitoringComponent = Vue.extend(monitoringComp); MonitoringComponent = Vue.extend(monitoringComp);
});
it('should render a link to environment monitoring page', () => { component = new MonitoringComponent({
const monitoringUrl = 'https://gitlab.com';
const component = new MonitoringComponent({
propsData: { propsData: {
monitoringUrl, monitoringUrl,
}, },
}).$mount(); }).$mount();
});
describe('computed', () => {
it('title', () => {
expect(component.title).toEqual('Monitoring');
});
});
it('should render a link to environment monitoring page', () => {
expect(component.$el.getAttribute('href')).toEqual(monitoringUrl); expect(component.$el.getAttribute('href')).toEqual(monitoringUrl);
expect(component.$el.querySelector('.fa-area-chart')).toBeDefined(); expect(component.$el.querySelector('.fa-area-chart')).toBeDefined();
expect(component.$el.getAttribute('title')).toEqual('Monitoring'); expect(component.$el.getAttribute('data-original-title')).toEqual('Monitoring');
expect(component.$el.getAttribute('aria-label')).toEqual('Monitoring');
}); });
}); });
...@@ -17,8 +17,15 @@ describe('Stop Component', () => { ...@@ -17,8 +17,15 @@ describe('Stop Component', () => {
}).$mount(); }).$mount();
}); });
describe('computed', () => {
it('title', () => {
expect(component.title).toEqual('Stop');
});
});
it('should render a button to stop the environment', () => { it('should render a button to stop the environment', () => {
expect(component.$el.tagName).toEqual('BUTTON'); expect(component.$el.tagName).toEqual('BUTTON');
expect(component.$el.getAttribute('title')).toEqual('Stop'); expect(component.$el.getAttribute('data-original-title')).toEqual('Stop');
expect(component.$el.getAttribute('aria-label')).toEqual('Stop');
}); });
}); });
...@@ -16,9 +16,16 @@ describe('Stop Component', () => { ...@@ -16,9 +16,16 @@ describe('Stop Component', () => {
}).$mount(); }).$mount();
}); });
describe('computed', () => {
it('title', () => {
expect(component.title).toEqual('Terminal');
});
});
it('should render a link to open a web terminal with the provided path', () => { it('should render a link to open a web terminal with the provided path', () => {
expect(component.$el.tagName).toEqual('A'); expect(component.$el.tagName).toEqual('A');
expect(component.$el.getAttribute('title')).toEqual('Terminal'); expect(component.$el.getAttribute('data-original-title')).toEqual('Terminal');
expect(component.$el.getAttribute('aria-label')).toEqual('Terminal');
expect(component.$el.getAttribute('href')).toEqual(terminalPath); expect(component.$el.getAttribute('href')).toEqual(terminalPath);
}); });
}); });
...@@ -22,7 +22,6 @@ describe('Time ago with tooltip component', () => { ...@@ -22,7 +22,6 @@ describe('Time ago with tooltip component', () => {
}).$mount(); }).$mount();
expect(vm.$el.tagName).toEqual('TIME'); expect(vm.$el.tagName).toEqual('TIME');
expect(vm.$el.classList.contains('js-vue-timeago')).toEqual(true);
expect( expect(
vm.$el.getAttribute('data-original-title'), vm.$el.getAttribute('data-original-title'),
).toEqual(gl.utils.formatDate('2017-05-08T14:57:39.781Z')); ).toEqual(gl.utils.formatDate('2017-05-08T14:57:39.781Z'));
......
import Vue from 'vue';
import tooltip from '~/vue_shared/directives/tooltip';
describe('Tooltip directive', () => {
let vm;
afterEach(() => {
if (vm) {
vm.$destroy();
}
});
describe('with a single tooltip', () => {
beforeEach(() => {
const SomeComponent = Vue.extend({
directives: {
tooltip,
},
template: `
<div
v-tooltip
title="foo">
</div>
`,
});
vm = new SomeComponent().$mount();
});
it('should have tooltip plugin applied', () => {
expect($(vm.$el).data('bs.tooltip')).toBeDefined();
});
});
describe('with multiple tooltips', () => {
beforeEach(() => {
const SomeComponent = Vue.extend({
directives: {
tooltip,
},
template: `
<div>
<div
v-tooltip
class="js-look-for-tooltip"
title="foo">
</div>
<div
v-tooltip
title="bar">
</div>
</div>
`,
});
vm = new SomeComponent().$mount();
});
it('should have tooltip plugin applied to all instances', () => {
expect($(vm.$el).find('.js-look-for-tooltip').data('bs.tooltip')).toBeDefined();
});
});
});
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