Commit 9fe7b97d authored by Martin Wortschack's avatar Martin Wortschack

Merge branch '207054-truncate-long-value-stream-analytics-names-in-the-stage-list' into 'master'

Truncate long value stream analytics names in the stage list

Closes #207054

See merge request gitlab-org/gitlab!25943
parents 09f08640 a59901b0
......@@ -45,10 +45,13 @@ export default {
:class="{ active: isActive }"
class="stage-nav-item d-flex pl-4 pr-4 m-0 mb-1 ml-2 rounded border-color-default border-style-solid border-width-1px"
>
<div class="stage-nav-item-cell stage-name p-0" :class="{ 'font-weight-bold': isActive }">
<div
class="stage-nav-item-cell stage-name w-50 pr-2"
:class="{ 'font-weight-bold': isActive }"
>
{{ title }}
</div>
<div class="stage-nav-item-cell stage-median mr-4">
<div class="stage-nav-item-cell stage-median w-50">
<template v-if="isUserAllowed">
<span v-if="hasValue">{{ value }}</span>
<span v-else class="stage-empty">{{ __('Not enough data') }}</span>
......
......@@ -51,11 +51,11 @@
}
.stage-header {
width: 18.5%;
width: 20.5%;
}
.median-header {
width: 21.5%;
width: 19.5%;
}
.event-header {
......
<script>
import { GlButton } from '@gitlab/ui';
import { GlButton, GlIcon, GlTooltip } from '@gitlab/ui';
import { approximateDuration } from '~/lib/utils/datetime_utility';
import Icon from '~/vue_shared/components/icon.vue';
import StageCardListItem from './stage_card_list_item.vue';
export default {
name: 'StageNavItem',
components: {
StageCardListItem,
Icon,
GlIcon,
GlButton,
GlTooltip,
},
props: {
isDefaultStage: {
......@@ -40,6 +40,7 @@ export default {
data() {
return {
isHover: false,
isTitleOverflowing: false,
};
},
computed: {
......@@ -52,6 +53,15 @@ export default {
editable() {
return this.canEdit;
},
menuOpen() {
return this.canEdit && this.isHover;
},
openMenuClasses() {
return this.menuOpen ? 'd-flex justify-content-end' : '';
},
},
mounted() {
this.checkIfTitleOverflows();
},
methods: {
handleDropdownAction(action) {
......@@ -67,61 +77,68 @@ export default {
handleHover(hoverState = false) {
this.isHover = hoverState;
},
checkIfTitleOverflows() {
const [titleEl] = this.$refs.title?.children;
if (titleEl) {
this.isTitleOverflowing = titleEl.scrollWidth > this.$refs.title.offsetWidth;
}
},
},
};
</script>
<template>
<li @click="handleSelectStage" @mouseover="handleHover(true)" @mouseleave="handleHover()">
<stage-card-list-item :is-active="isActive" :can-edit="editable">
<div class="stage-nav-item-cell stage-name p-0" :class="{ 'font-weight-bold': isActive }">
{{ title }}
</div>
<div class="stage-nav-item-cell stage-median mr-4">
<span v-if="hasValue">{{ median }}</span>
<span v-else class="stage-empty">{{ __('Not enough data') }}</span>
<stage-card-list-item
:is-active="isActive"
:can-edit="editable"
class="d-flex justify-space-between"
>
<div
ref="title"
class="stage-nav-item-cell stage-name text-truncate w-50 pr-2"
:class="{ 'font-weight-bold': isActive }"
>
<gl-tooltip v-if="isTitleOverflowing" :target="() => $refs.titleSpan">
{{ title }}
</gl-tooltip>
<span ref="titleSpan">{{ title }}</span>
</div>
<div v-show="canEdit && isHover" ref="dropdown" class="dropdown">
<gl-button
:title="__('More actions')"
class="more-actions-toggle btn btn-transparent p-0"
data-toggle="dropdown"
>
<icon class="icon" name="ellipsis_v" />
</gl-button>
<ul class="more-actions-dropdown dropdown-menu dropdown-open-left">
<template v-if="isDefaultStage">
<li>
<button
type="button"
class="btn-default btn-transparent"
@click="handleDropdownAction('hide', $event)"
>
{{ __('Hide stage') }}
</button>
</li>
</template>
<template v-else>
<li>
<button
type="button"
class="btn-default btn-transparent"
@click="handleDropdownAction('edit', $event)"
>
{{ __('Edit stage') }}
</button>
</li>
<li>
<button
type="button"
class="btn-danger danger"
@click="handleDropdownAction('remove', $event)"
>
{{ __('Remove stage') }}
</button>
</li>
</template>
</ul>
<div class="stage-nav-item-cell w-50 d-flex justify-content-between">
<div ref="median" class="stage-median w-75 align-items-start">
<span v-if="hasValue">{{ median }}</span>
<span v-else class="stage-empty">{{ __('Not enough data') }}</span>
</div>
<div v-show="menuOpen" ref="dropdown" :class="[openMenuClasses]" class="dropdown w-25">
<gl-button
:title="__('More actions')"
class="more-actions-toggle btn btn-transparent p-0"
data-toggle="dropdown"
>
<gl-icon class="icon" name="ellipsis_v" />
</gl-button>
<ul class="more-actions-dropdown dropdown-menu dropdown-open-left">
<template v-if="isDefaultStage">
<li>
<gl-button @click="handleDropdownAction('hide', $event)">
{{ __('Hide stage') }}
</gl-button>
</li>
</template>
<template v-else>
<li>
<gl-button @click="handleDropdownAction('edit', $event)">
{{ __('Edit stage') }}
</gl-button>
</li>
<li>
<gl-button @click="handleDropdownAction('remove', $event)">
{{ __('Remove stage') }}
</gl-button>
</li>
</template>
</ul>
</div>
</div>
</stage-card-list-item>
</li>
......
// NOTE: more tests will be added in https://gitlab.com/gitlab-org/gitlab/issues/121613
import { GlTooltip } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import StageNavItem from 'ee/analytics/cycle_analytics/components/stage_nav_item.vue';
import { approximateDuration } from '~/lib/utils/datetime_utility';
......@@ -7,31 +8,27 @@ describe('StageNavItem', () => {
const title = 'Rad stage';
const median = 50;
const $sel = {
title: '.stage-name',
median: '.stage-median',
};
function createComponent(props) {
function createComponent({ props = {}, opts = {} } = {}) {
return shallowMount(StageNavItem, {
propsData: {
title,
value: median,
...props,
},
...opts,
});
}
let wrapper = null;
const findStageTitle = () => wrapper.find($sel.title);
const findStageMedian = () => wrapper.find($sel.median);
const findStageTitle = () => wrapper.find({ ref: 'title' });
const findStageMedian = () => wrapper.find({ ref: 'median' });
afterEach(() => {
wrapper.destroy();
});
it('with no median value', () => {
wrapper = createComponent({ value: null });
wrapper = createComponent({ props: { value: null } });
expect(findStageMedian().text()).toEqual('Not enough data');
});
......@@ -48,4 +45,31 @@ describe('StageNavItem', () => {
expect(findStageTitle().text()).toEqual(title);
});
});
describe('with a really long name', () => {
const longTitle = 'This is a very long stage name that is intended to break the ui';
beforeEach(() => {
wrapper = createComponent({
props: { title: longTitle },
opts: {
data() {
return { isTitleOverflowing: true };
},
methods: {
// making tbis a noop so it wont toggle 'isTitleOverflowing' on mount
checkIfTitleOverflows: () => {},
},
},
});
});
it('renders the tooltip', () => {
expect(wrapper.find(GlTooltip).exists()).toBe(true);
});
it('tooltip has the correct stage title', () => {
expect(wrapper.find(GlTooltip).text()).toBe(longTitle);
});
});
});
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