Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
d1eec7ce
Commit
d1eec7ce
authored
Jun 24, 2020
by
Eulyeon Ko
Committed by
Paul Slaughter
Jun 24, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Allow Hiding/Collapsing of Milestone header on Roadmap
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/34357
parent
69b34501
Changes
10
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
480 additions
and
365 deletions
+480
-365
ee/app/assets/javascripts/roadmap/components/epic_item_details.vue
...sets/javascripts/roadmap/components/epic_item_details.vue
+21
-14
ee/app/assets/javascripts/roadmap/components/milestone_timeline.vue
...ets/javascripts/roadmap/components/milestone_timeline.vue
+17
-10
ee/app/assets/javascripts/roadmap/components/milestones_list_section.vue
...avascripts/roadmap/components/milestones_list_section.vue
+71
-4
ee/app/assets/stylesheets/pages/roadmap.scss
ee/app/assets/stylesheets/pages/roadmap.scss
+9
-0
ee/changelogs/unreleased/212494-allow-hiding-collapsing-of-milestone-header-on-roadmap.yml
...llow-hiding-collapsing-of-milestone-header-on-roadmap.yml
+5
-0
ee/spec/frontend/roadmap/components/epic_item_details_spec.js
...pec/frontend/roadmap/components/epic_item_details_spec.js
+217
-265
ee/spec/frontend/roadmap/components/milestone_timeline_spec.js
...ec/frontend/roadmap/components/milestone_timeline_spec.js
+31
-28
ee/spec/frontend/roadmap/components/milestones_list_section_spec.js
...ontend/roadmap/components/milestones_list_section_spec.js
+86
-34
locale/gitlab.pot
locale/gitlab.pot
+9
-4
spec/frontend/helpers/vue_mock_directive.js
spec/frontend/helpers/vue_mock_directive.js
+14
-6
No files found.
ee/app/assets/javascripts/roadmap/components/epic_item_details.vue
View file @
d1eec7ce
...
@@ -74,9 +74,7 @@ export default {
...
@@ -74,9 +74,7 @@ export default {
if
(
this
.
isEmptyChildrenWithFilter
)
{
if
(
this
.
isEmptyChildrenWithFilter
)
{
return
this
.
infoSearchLabel
;
return
this
.
infoSearchLabel
;
}
}
return
this
.
childrenFlags
[
this
.
itemId
].
itemExpanded
return
this
.
childrenFlags
[
this
.
itemId
].
itemExpanded
?
__
(
'
Collapse
'
)
:
__
(
'
Expand
'
);
?
__
(
'
Collapse child epics
'
)
:
__
(
'
Expand child epics
'
);
},
},
childrenFetchInProgress
()
{
childrenFetchInProgress
()
{
return
this
.
epic
.
hasChildren
&&
this
.
childrenFlags
[
this
.
itemId
].
itemChildrenFetchInProgress
;
return
this
.
epic
.
hasChildren
&&
this
.
childrenFlags
[
this
.
itemId
].
itemChildrenFetchInProgress
;
...
@@ -108,9 +106,12 @@ export default {
...
@@ -108,9 +106,12 @@ export default {
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"epic-details-cell"
data-qa-selector=
"epic_details_cell"
>
<div
<div
class=
"d-flex align-items-start p-2"
class=
"epic-details-cell gl-display-flex gl-flex-direction-column gl-justify-content-center"
data-qa-selector=
"epic_details_cell"
>
<div
class=
"gl-display-flex align-items-start gl-px-3 gl-mb-1"
:class=
"[epic.isChildEpic ? childMarginClassname : '']"
:class=
"[epic.isChildEpic ? childMarginClassname : '']"
>
>
<span
ref=
"expandCollapseInfo"
>
<span
ref=
"expandCollapseInfo"
>
...
@@ -130,38 +131,44 @@ export default {
...
@@ -130,38 +131,44 @@ export default {
</gl-button>
</gl-button>
</span>
</span>
<gl-tooltip
<gl-tooltip
v-if=
"isEmptyChildrenWithFilter"
v-if=
"!isExpandIconHidden"
ref=
"expandIconTooltip"
triggers=
"hover"
:target=
"() => $refs.expandCollapseInfo"
:target=
"() => $refs.expandCollapseInfo"
boundary=
"viewport"
boundary=
"viewport"
offset=
"
80
"
offset=
"
15
"
placement=
"topright"
placement=
"topright"
>
>
{{
infoSearch
Label
}}
{{
expandIcon
Label
}}
</gl-tooltip>
</gl-tooltip>
<div
class=
"overflow-hidden flex-grow-1 mx-2"
>
<div
class=
"overflow-hidden flex-grow-1 mx-2"
>
<a
:href=
"epic.webUrl"
:title=
"epic.title"
class=
"epic-title d-block text-body bold"
>
<a
:href=
"epic.webUrl"
:title=
"epic.title"
class=
"epic-title gl-mt-1 d-block text-body bold"
>
{{
epic
.
title
}}
{{
epic
.
title
}}
</a>
</a>
<div
class=
"epic-group-timeframe d-flex text-secondary"
>
<div
class=
"epic-group-timeframe d-flex text-secondary"
>
<
p
<
span
v-if=
"isEpicGroupDifferent && !epic.hasParent"
v-if=
"isEpicGroupDifferent && !epic.hasParent"
:title=
"epic.groupFullName"
:title=
"epic.groupFullName"
class=
"epic-group"
class=
"epic-group"
>
>
{{
epic
.
groupName
}}
{{
epic
.
groupName
}}
</
p
>
</
span
>
<span
v-if=
"isEpicGroupDifferent && !epic.hasParent"
class=
"mx-1"
aria-hidden=
"true"
<span
v-if=
"isEpicGroupDifferent && !epic.hasParent"
class=
"mx-1"
aria-hidden=
"true"
>
·
</span
>
·
</span
>
>
<
p
class=
"epic-timeframe"
:title=
"timeframeString"
>
{{
timeframeString
}}
</p
>
<
span
class=
"epic-timeframe"
:title=
"timeframeString"
>
{{
timeframeString
}}
</span
>
</div>
</div>
</div>
</div>
<template
v-if=
"allowSubEpics"
>
<template
v-if=
"allowSubEpics"
>
<div
ref=
"childEpicsCount"
class=
"d-flex text-secondary text-nowrap"
>
<div
ref=
"childEpicsCount"
class=
"
gl-mt-1
d-flex text-secondary text-nowrap"
>
<gl-icon
name=
"epic"
class=
"align-text-bottom mr-1"
aria-hidden=
"true"
/>
<gl-icon
name=
"epic"
class=
"align-text-bottom mr-1"
aria-hidden=
"true"
/>
<p
class=
"m-0"
:aria-label=
"childEpicsCountText"
>
{{
childEpicsCount
}}
</p>
<p
class=
"m-0"
:aria-label=
"childEpicsCountText"
>
{{
childEpicsCount
}}
</p>
</div>
</div>
<gl-tooltip
:target=
"() => $refs.childEpicsCount"
>
<gl-tooltip
ref=
"childEpicsCountTooltip"
:target=
"() => $refs.childEpicsCount"
>
<span
:class=
"
{ bold: hasFiltersApplied }">
{{
childEpicsCountText
}}
</span>
<span
:class=
"
{ bold: hasFiltersApplied }">
{{
childEpicsCountText
}}
</span>
<span
v-if=
"hasFiltersApplied"
class=
"d-block"
>
{{
childEpicsSearchText
}}
</span>
<span
v-if=
"hasFiltersApplied"
class=
"d-block"
>
{{
childEpicsSearchText
}}
</span>
</gl-tooltip>
</gl-tooltip>
...
...
ee/app/assets/javascripts/roadmap/components/milestone_timeline.vue
View file @
d1eec7ce
...
@@ -24,6 +24,10 @@ export default {
...
@@ -24,6 +24,10 @@ export default {
type
:
Number
,
type
:
Number
,
required
:
true
,
required
:
true
,
},
},
milestonesExpanded
:
{
type
:
Boolean
,
required
:
true
,
},
},
},
};
};
</
script
>
</
script
>
...
@@ -33,10 +37,12 @@ export default {
...
@@ -33,10 +37,12 @@ export default {
<span
<span
v-for=
"timeframeItem in timeframe"
v-for=
"timeframeItem in timeframe"
:key=
"timeframeItem.id"
:key=
"timeframeItem.id"
class=
"milestone-timeline-cell d-table-cell position-relative border-right border-bottom"
class=
"milestone-timeline-cell gl-display-table-cell gl-relative border-right border-bottom"
:class=
"
{ 'milestone-timeline-cell-empty': !milestonesExpanded }"
data-qa-selector="milestone_timeline_cell"
data-qa-selector="milestone_timeline_cell"
>
>
<current-day-indicator
:preset-type=
"presetType"
:timeframe-item=
"timeframeItem"
/>
<current-day-indicator
:preset-type=
"presetType"
:timeframe-item=
"timeframeItem"
/>
<template
v-if=
"milestonesExpanded"
>
<milestone-item
<milestone-item
v-for=
"milestone in milestones"
v-for=
"milestone in milestones"
:key=
"milestone.id"
:key=
"milestone.id"
...
@@ -46,6 +52,7 @@ export default {
...
@@ -46,6 +52,7 @@ export default {
:timeframe-item=
"timeframeItem"
:timeframe-item=
"timeframeItem"
:current-group-id=
"currentGroupId"
:current-group-id=
"currentGroupId"
/>
/>
</
template
>
</span>
</span>
</div>
</div>
</template>
</template>
ee/app/assets/javascripts/roadmap/components/milestones_list_section.vue
View file @
d1eec7ce
<
script
>
<
script
>
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
eventHub
from
'
../event_hub
'
;
import
eventHub
from
'
../event_hub
'
;
import
{
__
,
n__
}
from
'
~/locale
'
;
import
{
GlButton
,
GlIcon
,
GlTooltipDirective
}
from
'
@gitlab/ui
'
;
import
{
EPIC_DETAILS_CELL_WIDTH
,
EPIC_ITEM_HEIGHT
,
TIMELINE_CELL_MIN_WIDTH
}
from
'
../constants
'
;
import
{
EPIC_DETAILS_CELL_WIDTH
,
EPIC_ITEM_HEIGHT
,
TIMELINE_CELL_MIN_WIDTH
}
from
'
../constants
'
;
import
MilestoneTimeline
from
'
./milestone_timeline.vue
'
;
import
MilestoneTimeline
from
'
./milestone_timeline.vue
'
;
const
EXPAND_BUTTON_EXPANDED
=
{
name
:
'
chevron-down
'
,
iconLabel
:
__
(
'
Collapse milestones
'
),
tooltip
:
__
(
'
Collapse
'
),
};
const
EXPAND_BUTTON_COLLAPSED
=
{
name
:
'
chevron-right
'
,
iconLabel
:
__
(
'
Expand milestones
'
),
tooltip
:
__
(
'
Expand
'
),
};
export
default
{
export
default
{
components
:
{
components
:
{
MilestoneTimeline
,
MilestoneTimeline
,
GlButton
,
GlIcon
,
},
directives
:
{
GlTooltip
:
GlTooltipDirective
,
},
},
props
:
{
props
:
{
presetType
:
{
presetType
:
{
...
@@ -31,6 +50,7 @@ export default {
...
@@ -31,6 +50,7 @@ export default {
offsetLeft
:
0
,
offsetLeft
:
0
,
showBottomShadow
:
false
,
showBottomShadow
:
false
,
roadmapShellEl
:
null
,
roadmapShellEl
:
null
,
milestonesExpanded
:
true
,
};
};
},
},
computed
:
{
computed
:
{
...
@@ -48,6 +68,17 @@ export default {
...
@@ -48,6 +68,17 @@ export default {
left
:
`
${
this
.
offsetLeft
}
px`
,
left
:
`
${
this
.
offsetLeft
}
px`
,
};
};
},
},
expandButton
()
{
return
this
.
milestonesExpanded
?
EXPAND_BUTTON_EXPANDED
:
EXPAND_BUTTON_COLLAPSED
;
},
milestonesCount
()
{
return
this
.
milestones
.
length
;
},
milestonesCountText
()
{
return
Number
.
isInteger
(
this
.
milestonesCount
)
?
n__
(
`%d milestone`
,
`%d milestones`
,
this
.
milestonesCount
)
:
''
;
},
},
},
mounted
()
{
mounted
()
{
eventHub
.
$on
(
'
epicsListScrolled
'
,
this
.
handleEpicsListScroll
);
eventHub
.
$on
(
'
epicsListScrolled
'
,
this
.
handleEpicsListScroll
);
...
@@ -76,23 +107,59 @@ export default {
...
@@ -76,23 +107,59 @@ export default {
handleEpicsListScroll
({
scrollTop
,
clientHeight
,
scrollHeight
})
{
handleEpicsListScroll
({
scrollTop
,
clientHeight
,
scrollHeight
})
{
this
.
showBottomShadow
=
Math
.
ceil
(
scrollTop
)
+
clientHeight
<
scrollHeight
;
this
.
showBottomShadow
=
Math
.
ceil
(
scrollTop
)
+
clientHeight
<
scrollHeight
;
},
},
toggleMilestonesExpanded
()
{
this
.
milestonesExpanded
=
!
this
.
milestonesExpanded
;
},
},
},
};
};
</
script
>
</
script
>
<
template
>
<
template
>
<div
:style=
"sectionContainerStyles"
class=
"milestones-list-section d-table"
>
<div
<div
class=
"milestones-list-title d-table-cell bold border-bottom align-top position-sticky pt-2 pl-3"
:style=
"sectionContainerStyles"
class=
"milestones-list-section gl-display-table"
:class=
"
{ 'milestones-list-section-collapsed': !milestonesExpanded }"
>
>
<div
class=
"milestones-list-title gl-display-table-cell border-bottom gl-vertical-align-top position-sticky gl-p-3"
>
<div
class=
"gl-display-flex gl-align-items-center"
>
<span
v-gl-tooltip.hover.topright=
"
{
title: expandButton.tooltip,
offset: 15,
boundary: 'viewport',
}"
data-testid="expandButton"
>
<gl-button
:aria-label=
"expandButton.iconLabel"
variant=
"link"
@
click=
"toggleMilestonesExpanded"
>
<gl-icon
:name=
"expandButton.name"
class=
"text-secondary"
aria-hidden=
"true"
/>
</gl-button>
</span>
<div
class=
"gl-overflow-hidden gl-flex-grow-1 gl-mx-3 gl-font-weight-bold"
>
{{
__
(
'
Milestones
'
)
}}
{{
__
(
'
Milestones
'
)
}}
</div>
</div>
<div
class=
"milestones-list-items d-table-cell"
>
<div
v-gl-tooltip=
"milestonesCountText"
class=
"gl-display-flex gl-align-items-center gl-justify-content-center text-secondary gl-white-space-nowrap"
data-testid=
"count"
>
<gl-icon
name=
"clock"
class=
"gl-mr-2"
aria-hidden=
"true"
/>
<span
:aria-label=
"milestonesCountText"
>
{{
milestonesCount
}}
</span>
</div>
</div>
</div>
<div
class=
"milestones-list-items gl-display-table-cell"
>
<milestone-timeline
<milestone-timeline
:preset-type=
"presetType"
:preset-type=
"presetType"
:timeframe=
"timeframe"
:timeframe=
"timeframe"
:milestones=
"milestones"
:milestones=
"milestones"
:current-group-id=
"currentGroupId"
:current-group-id=
"currentGroupId"
:milestones-expanded=
"milestonesExpanded"
/>
/>
</div>
</div>
<div
v-show=
"showBottomShadow"
:style=
"shadowCellStyles"
class=
"scroll-bottom-shadow"
></div>
<div
v-show=
"showBottomShadow"
:style=
"shadowCellStyles"
class=
"scroll-bottom-shadow"
></div>
...
...
ee/app/assets/stylesheets/pages/roadmap.scss
View file @
d1eec7ce
$header-item-height
:
60px
;
$header-item-height
:
60px
;
$item-height
:
50px
;
$item-height
:
50px
;
$milestones-collapsed-height
:
38px
;
$details-cell-width
:
320px
;
$details-cell-width
:
320px
;
$timeline-cell-width
:
180px
;
$timeline-cell-width
:
180px
;
$border-style
:
1px
solid
$border-gray-normal
;
$border-style
:
1px
solid
$border-gray-normal
;
...
@@ -408,11 +409,19 @@ html.group-epics-roadmap-html {
...
@@ -408,11 +409,19 @@ html.group-epics-roadmap-html {
}
}
.milestones-list-section
{
.milestones-list-section
{
&
.milestones-list-section-collapsed
{
height
:
$milestones-collapsed-height
;
}
.milestones-list-items
{
.milestones-list-items
{
.milestone-timeline-cell
{
.milestone-timeline-cell
{
width
:
$timeline-cell-width
;
width
:
$timeline-cell-width
;
}
}
.milestone-timeline-cell-empty
{
height
:
$milestones-collapsed-height
;
}
.timeline-bar-wrapper
{
.timeline-bar-wrapper
{
height
:
32px
;
height
:
32px
;
color
:
$gray-700
;
color
:
$gray-700
;
...
...
ee/changelogs/unreleased/212494-allow-hiding-collapsing-of-milestone-header-on-roadmap.yml
0 → 100644
View file @
d1eec7ce
---
title
:
Allow Hiding/Collapsing of Milestone header on Roadmap
merge_request
:
34357
author
:
type
:
added
ee/spec/frontend/roadmap/components/epic_item_details_spec.js
View file @
d1eec7ce
import
{
GlButton
,
GlIcon
,
GlTooltip
}
from
'
@gitlab/ui
'
;
import
{
GlButton
,
GlIcon
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
createStore
from
'
ee/roadmap/store
'
;
import
createStore
from
'
ee/roadmap/store
'
;
import
EpicItemDetails
from
'
ee/roadmap/components/epic_item_details.vue
'
;
import
EpicItemDetails
from
'
ee/roadmap/components/epic_item_details.vue
'
;
...
@@ -10,70 +10,87 @@ import {
...
@@ -10,70 +10,87 @@ import {
mockFormattedChildEpic1
,
mockFormattedChildEpic1
,
}
from
'
ee_jest/roadmap/mock_data
'
;
}
from
'
ee_jest/roadmap/mock_data
'
;
let
store
;
describe
(
'
EpicItemDetails
'
,
()
=>
{
let
wrapper
;
const
createComponent
=
({
let
store
;
epic
=
mockFormattedEpic
,
currentGroupId
=
mockGroupId
,
beforeEach
(()
=>
{
timeframeString
=
'
Jul 10, 2017 – Jun 2, 2018
'
,
store
=
createStore
();
childLevel
=
0
,
});
childrenFlags
=
{
'
41
'
:
{
itemExpanded
:
false
}
},
hasFiltersApplied
=
false
,
afterEach
(()
=>
{
isChildrenEmpty
=
false
,
wrapper
.
destroy
();
}
=
{})
=>
{
wrapper
=
null
;
return
shallowMount
(
EpicItemDetails
,
{
});
const
createWrapper
=
(
props
=
{})
=>
{
wrapper
=
shallowMount
(
EpicItemDetails
,
{
store
,
store
,
propsData
:
{
propsData
:
{
epic
,
epic
:
mockFormattedEpic
,
currentGroupId
,
currentGroupId
:
mockGroupId
,
timeframeString
,
timeframeString
:
'
Jul 10, 2017 – Jun 2, 2018
'
,
childLevel
,
childLevel
:
0
,
childrenFlags
,
childrenFlags
:
{
'
41
'
:
{
itemExpanded
:
false
}
},
hasFiltersApplied
,
hasFiltersApplied
:
false
,
isChildrenEmpty
,
isChildrenEmpty
:
false
,
...
props
,
},
},
});
});
};
};
const
getTitle
=
wrapper
=>
wrapper
.
find
(
'
.epic-title
'
);
const
getTitle
=
()
=>
wrapper
.
find
(
'
.epic-title
'
);
const
getGroupName
=
wrapper
=>
wrapper
.
find
(
'
.epic-group
'
);
const
getGroupName
=
()
=>
wrapper
.
find
(
'
.epic-group
'
);
const
getExpandIconButton
=
wrapper
=>
wrapper
.
find
(
GlButton
)
;
const
getChildMarginClassName
=
()
=>
wrapper
.
vm
.
childMarginClassname
;
const
getChildEpicsCount
=
wrapper
=>
wrapper
.
find
({
ref
:
'
childEpicsCount
'
}
);
const
getExpandIconButton
=
()
=>
wrapper
.
find
(
GlButton
);
describe
(
'
EpicItemDetails
'
,
()
=>
{
const
getExpandIconTooltip
=
()
=>
wrapper
.
find
({
ref
:
'
expandIconTooltip
'
});
let
wrapper
;
beforeEach
(()
=>
{
const
getChildEpicsCount
=
()
=>
wrapper
.
find
({
ref
:
'
childEpicsCount
'
});
store
=
createStore
();
wrapper
=
createComponent
();
const
getChildEpicsCountTooltip
=
()
=>
wrapper
.
find
({
ref
:
'
childEpicsCountTooltip
'
});
const
getExpandButtonData
=
()
=>
({
icon
:
wrapper
.
find
(
GlIcon
).
attributes
(
'
name
'
),
iconLabel
:
getExpandIconButton
().
attributes
(
'
aria-label
'
),
tooltip
:
getExpandIconTooltip
().
text
(),
});
});
afterEach
(()
=>
{
const
getEpicTitleData
=
()
=>
({
wrapper
.
destroy
();
title
:
getTitle
().
text
(),
wrapper
=
null
;
link
:
getTitle
().
attributes
(
'
href
'
),
});
const
getEpicGroupNameData
=
()
=>
({
groupName
:
getGroupName
().
text
(),
title
:
getGroupName
().
attributes
(
'
title
'
),
});
const
createMockEpic
=
epic
=>
({
...
mockFormattedEpic
,
...
epic
,
});
});
describe
(
'
epic title
'
,
()
=>
{
describe
(
'
epic title
'
,
()
=>
{
it
(
'
is displayed
'
,
()
=>
{
beforeEach
(
()
=>
{
expect
(
getTitle
(
wrapper
).
text
()).
toBe
(
mockFormattedEpic
.
title
);
createWrapper
(
);
});
});
it
(
'
contains a link to the epic
'
,
()
=>
{
it
(
'
is displayed with a link to the epic
'
,
()
=>
{
expect
(
getTitle
(
wrapper
).
attributes
(
'
href
'
)).
toBe
(
mockFormattedEpic
.
webUrl
);
expect
(
getEpicTitleData
()).
toEqual
({
title
:
mockFormattedEpic
.
title
,
link
:
mockFormattedEpic
.
webUrl
,
});
});
});
});
});
describe
(
'
epic group name
'
,
()
=>
{
describe
(
'
epic group name
'
,
()
=>
{
describe
(
'
when the epic group ID is different from the current group ID
'
,
()
=>
{
const
epic
=
{
let
epic
;
beforeEach
(()
=>
{
epic
=
{
id
:
'
41
'
,
id
:
'
41
'
,
mockFormattedEpic
,
...
mockFormattedEpic
,
groupId
:
1
,
groupId
:
1
,
groupName
:
'
Bar
'
,
groupName
:
'
Bar
'
,
groupFullName
:
'
Foo / Bar
'
,
groupFullName
:
'
Foo / Bar
'
,
...
@@ -83,40 +100,27 @@ describe('EpicItemDetails', () => {
...
@@ -83,40 +100,27 @@ describe('EpicItemDetails', () => {
},
},
};
};
wrapper
.
setProps
({
epic
,
currentGroupId
:
2
});
describe
(
'
when the epic group ID is different from the current group ID
'
,
()
=>
{
it
(
'
is displayed and set to the title attribute
'
,
()
=>
{
createWrapper
({
epic
,
currentGroupId
:
2
});
expect
(
getEpicGroupNameData
()).
toEqual
({
groupName
:
epic
.
groupName
,
title
:
epic
.
groupFullName
,
});
});
it
(
'
is displayed
'
,
()
=>
{
expect
(
getGroupName
(
wrapper
).
text
()).
toContain
(
epic
.
groupName
);
});
it
(
'
is set to the title attribute
'
,
()
=>
{
expect
(
getGroupName
(
wrapper
).
attributes
(
'
title
'
)).
toBe
(
epic
.
groupFullName
);
});
});
});
});
describe
(
'
when the epic group ID is the same as the current group ID
'
,
()
=>
{
describe
(
'
when the epic group ID is the same as the current group ID
'
,
()
=>
{
let
epic
;
beforeEach
(()
=>
{
epic
=
{
...
mockFormattedEpic
,
groupId
:
1
,
groupName
:
'
Bar
'
,
groupFullName
:
'
Foo / Bar
'
,
};
wrapper
.
setProps
({
epic
,
currentGroupId
:
1
});
});
it
(
'
is hidden
'
,
()
=>
{
it
(
'
is hidden
'
,
()
=>
{
expect
(
getGroupName
(
wrapper
).
exists
()).
toBe
(
false
);
createWrapper
({
epic
,
currentGroupId
:
1
});
expect
(
getGroupName
().
exists
()).
toBe
(
false
);
});
});
});
});
});
});
describe
(
'
timeframe
'
,
()
=>
{
describe
(
'
timeframe
'
,
()
=>
{
it
(
'
is displayed
'
,
()
=>
{
it
(
'
is displayed
'
,
()
=>
{
createWrapper
();
const
timeframe
=
wrapper
.
find
(
'
.epic-timeframe
'
);
const
timeframe
=
wrapper
.
find
(
'
.epic-timeframe
'
);
expect
(
timeframe
.
text
()).
toBe
(
'
Jul 10, 2017 – Jun 2, 2018
'
);
expect
(
timeframe
.
text
()).
toBe
(
'
Jul 10, 2017 – Jun 2, 2018
'
);
...
@@ -125,13 +129,13 @@ describe('EpicItemDetails', () => {
...
@@ -125,13 +129,13 @@ describe('EpicItemDetails', () => {
describe
(
'
childMarginClassname
'
,
()
=>
{
describe
(
'
childMarginClassname
'
,
()
=>
{
it
(
'
childMarginClassname returns class for level 1 child is childLevel is 1
'
,
()
=>
{
it
(
'
childMarginClassname returns class for level 1 child is childLevel is 1
'
,
()
=>
{
wrapper
.
setProps
({
childLevel
:
1
});
createWrapper
({
childLevel
:
1
});
expect
(
wrapper
.
vm
.
childMarginClassname
).
toEqual
(
'
ml-4
'
);
expect
(
getChildMarginClassName
()
).
toEqual
(
'
ml-4
'
);
});
});
it
(
'
childMarginClassname returns class for level 2 child is childLevel is 2
'
,
()
=>
{
it
(
'
childMarginClassname returns class for level 2 child is childLevel is 2
'
,
()
=>
{
wrapper
.
setProps
({
childLevel
:
2
});
createWrapper
({
childLevel
:
2
});
expect
(
wrapper
.
vm
.
childMarginClassname
).
toEqual
(
'
ml-6
'
);
expect
(
getChildMarginClassName
()
).
toEqual
(
'
ml-6
'
);
});
});
});
});
...
@@ -141,204 +145,152 @@ describe('EpicItemDetails', () => {
...
@@ -141,204 +145,152 @@ describe('EpicItemDetails', () => {
});
});
describe
(
'
expand icon
'
,
()
=>
{
describe
(
'
expand icon
'
,
()
=>
{
it
(
'
is hidden when epic has no child epics
'
,
()
=>
{
it
(
'
is hidden when it is child epic
'
,
()
=>
{
const
epic
=
{
const
epic
=
createMockEpic
({
...
mockFormattedEpic
,
isChildEpic
:
true
,
hasChildren
:
false
,
});
};
createWrapper
({
epic
});
wrapper
=
createComponent
({
epic
});
expect
(
getExpandIconButton
().
classes
()).
toContain
(
'
invisible
'
);
});
expect
(
getExpandIconButton
(
wrapper
).
classes
()).
toContain
(
'
invisible
'
);
describe
(
'
when epic has no child epics
'
,
()
=>
{
beforeEach
(()
=>
{
const
epic
=
createMockEpic
({
hasChildren
:
false
,
descendantCounts
:
{
openedEpics
:
0
,
closedEpics
:
0
,
},
});
createWrapper
({
epic
});
});
it
(
'
is hidden
'
,
()
=>
{
expect
(
getExpandIconButton
().
classes
()).
toContain
(
'
invisible
'
);
});
describe
(
'
child epics count
'
,
()
=>
{
it
(
'
shows the count as 0
'
,
()
=>
{
expect
(
getChildEpicsCount
().
text
()).
toBe
(
'
0
'
);
});
});
});
});
it
(
'
is shown when epic has child epics
'
,
()
=>
{
describe
(
'
when epic has child epics
'
,
()
=>
{
const
epic
=
{
let
epic
;
...
mockFormattedEpic
,
beforeEach
(()
=>
{
epic
=
createMockEpic
({
id
:
41
,
hasChildren
:
true
,
hasChildren
:
true
,
children
:
{
children
:
{
edges
:
[
mockFormattedChildEpic1
],
edges
:
[
mockFormattedChildEpic1
],
},
},
};
descendantCounts
:
{
wrapper
=
createComponent
({
epic
});
openedEpics
:
0
,
closedEpics
:
1
,
expect
(
getExpandIconButton
(
wrapper
).
classes
()).
not
.
toContain
(
'
invisible
'
);
},
});
createWrapper
({
epic
});
});
});
it
(
'
shows "chevron-right" icon when child epics are not expanded
'
,
()
=>
{
it
(
'
is shown
'
,
()
=>
{
wrapper
=
createComponent
();
expect
(
getExpandIconButton
().
classes
()).
not
.
toContain
(
'
invisible
'
);
});
expect
(
wrapper
.
find
(
GlIcon
).
attributes
(
'
name
'
)).
toBe
(
'
chevron-right
'
);
it
(
'
emits toggleIsEpicExpanded event when clicked
'
,
()
=>
{
jest
.
spyOn
(
eventHub
,
'
$emit
'
).
mockImplementation
(()
=>
{});
getExpandIconButton
().
vm
.
$emit
(
'
click
'
);
expect
(
eventHub
.
$emit
).
toHaveBeenCalledWith
(
'
toggleIsEpicExpanded
'
,
epic
);
});
});
it
(
'
shows "chevron-down" icon when child epics are expanded
'
,
()
=>
{
describe
(
'
when child epics are expanded
'
,
()
=>
{
const
epic
=
{
const
childrenFlags
=
{
...
mockFormattedEpic
,
hasChildren
:
true
,
};
wrapper
=
createComponent
({
epic
,
childrenFlags
:
{
'
41
'
:
{
itemExpanded
:
true
},
'
41
'
:
{
itemExpanded
:
true
},
},
};
beforeEach
(()
=>
{
createWrapper
({
epic
,
childrenFlags
});
});
});
expect
(
wrapper
.
find
(
GlIcon
).
attributes
(
'
name
'
)).
toBe
(
'
chevron-down
'
);
it
(
'
shows collapse button
'
,
()
=>
{
expect
(
getExpandButtonData
()).
toEqual
({
icon
:
'
chevron-down
'
,
iconLabel
:
'
Collapse
'
,
tooltip
:
'
Collapse
'
,
});
});
});
it
(
'
shows "information-o" icon when child epics are expanded but no children are returned due to applied filters
'
,
()
=>
{
describe
(
'
when filters are applied
'
,
()
=>
{
const
epic
=
{
beforeEach
(()
=>
{
...
mockFormattedEpic
,
createWrapper
({
hasChildren
:
true
,
};
wrapper
=
createComponent
({
epic
,
epic
,
childrenFlags
:
{
childrenFlags
,
'
41
'
:
{
itemExpanded
:
true
},
},
hasFiltersApplied
:
true
,
hasFiltersApplied
:
true
,
isChildrenEmpty
:
true
,
isChildrenEmpty
:
true
,
});
});
expect
(
wrapper
.
find
(
GlIcon
).
attributes
(
'
name
'
)).
toBe
(
'
information-o
'
);
});
});
it
(
'
has "Expand child epics" label when child epics are not expanded
'
,
()
=>
{
it
(
'
shows child epics match filters button
'
,
()
=>
{
wrapper
=
createComponent
();
expect
(
getExpandButtonData
()).
toEqual
({
icon
:
'
information-o
'
,
expect
(
getExpandIconButton
(
wrapper
).
attributes
(
'
aria-label
'
)).
toBe
(
'
Expand child epics
'
);
iconLabel
:
'
No child epics match applied filters
'
,
tooltip
:
'
No child epics match applied filters
'
,
});
});
});
it
(
'
has "Collapse child epics" label when child epics are expanded
'
,
()
=>
{
const
epic
=
{
...
mockFormattedEpic
,
hasChildren
:
true
,
};
wrapper
=
createComponent
({
epic
,
childrenFlags
:
{
'
41
'
:
{
itemExpanded
:
true
},
},
});
});
expect
(
getExpandIconButton
(
wrapper
).
attributes
(
'
aria-label
'
)).
toBe
(
'
Collapse child epics
'
);
});
});
it
(
'
has "No child epics match applied filters" label when child epics are
expanded
'
,
()
=>
{
describe
(
'
when child epics are not
expanded
'
,
()
=>
{
const
epic
=
{
beforeEach
(()
=>
{
...
mockFormattedEpic
,
const
childrenFlags
=
{
hasChildren
:
true
,
'
41
'
:
{
itemExpanded
:
false
}
,
};
};
wrapper
=
createComponent
({
createWrapper
({
epic
,
epic
,
childrenFlags
:
{
childrenFlags
,
'
41
'
:
{
itemExpanded
:
true
},
},
hasFiltersApplied
:
true
,
isChildrenEmpty
:
true
,
});
});
expect
(
getExpandIconButton
(
wrapper
).
attributes
(
'
aria-label
'
)).
toBe
(
'
No child epics match applied filters
'
,
);
});
});
it
(
'
emits toggleIsEpicExpanded event when clicked
'
,
()
=>
{
it
(
'
shows expand button
'
,
()
=>
{
jest
.
spyOn
(
eventHub
,
'
$emit
'
).
mockImplementation
(()
=>
{});
expect
(
getExpandButtonData
()).
toEqual
({
icon
:
'
chevron-right
'
,
const
id
=
41
;
iconLabel
:
'
Expand
'
,
const
epic
=
{
tooltip
:
'
Expand
'
,
...
mockFormattedEpic
,
id
,
children
:
{
edges
:
[
mockFormattedChildEpic1
],
},
};
wrapper
=
createComponent
({
epic
});
getExpandIconButton
(
wrapper
).
vm
.
$emit
(
'
click
'
);
expect
(
eventHub
.
$emit
).
toHaveBeenCalledWith
(
'
toggleIsEpicExpanded
'
,
epic
);
});
});
it
(
'
is hidden when it is child epic
'
,
()
=>
{
const
epic
=
{
...
mockFormattedEpic
,
isChildEpic
:
true
,
};
wrapper
=
createComponent
({
epic
});
expect
(
getExpandIconButton
(
wrapper
).
classes
()).
toContain
(
'
invisible
'
);
});
});
});
});
describe
(
'
child epics count
'
,
()
=>
{
describe
(
'
child epics count
'
,
()
=>
{
it
(
'
shows the correct count of child epics
'
,
()
=>
{
it
(
'
has a tooltip with the count
'
,
()
=>
{
const
epic
=
{
createWrapper
({
epic
});
...
mockFormattedEpic
,
expect
(
getChildEpicsCountTooltip
().
text
()).
toBe
(
'
1 child epic
'
);
children
:
{
edges
:
[
mockFormattedChildEpic1
,
mockFormattedChildEpic2
],
},
descendantCounts
:
{
openedEpics
:
0
,
closedEpics
:
2
,
},
};
wrapper
=
createComponent
({
epic
});
expect
(
getChildEpicsCount
(
wrapper
).
text
()).
toBe
(
'
2
'
);
});
});
it
(
'
shows the count as 0 when there are no child epics
'
,
()
=>
{
it
(
'
has a tooltip with the count and explanation if search is being performed
'
,
()
=>
{
const
epic
=
{
createWrapper
({
epic
,
hasFiltersApplied
:
true
});
...
mockFormattedEpic
,
expect
(
getChildEpicsCountTooltip
().
text
()).
toBe
(
descendantCounts
:
{
'
1 child epic Some child epics may be hidden due to applied filters
'
,
openedEpics
:
0
,
);
closedEpics
:
0
,
},
};
wrapper
=
createComponent
({
epic
});
expect
(
getChildEpicsCount
(
wrapper
).
text
()).
toBe
(
'
0
'
);
});
});
it
(
'
has a tooltip with the count
'
,
()
=>
{
it
(
'
does not render if the user license does not support child epics
'
,
()
=>
{
const
epic
=
{
store
.
state
.
allowSubEpics
=
false
;
...
mockFormattedEpic
,
createWrapper
({
epic
});
children
:
{
expect
(
getChildEpicsCount
().
exists
()).
toBe
(
false
);
edges
:
[
mockFormattedChildEpic1
],
},
descendantCounts
:
{
openedEpics
:
0
,
closedEpics
:
1
,
},
};
wrapper
=
createComponent
({
epic
});
expect
(
wrapper
.
find
(
GlTooltip
).
text
()).
toBe
(
'
1 child epic
'
);
});
});
it
(
'
has a tooltip with the count and explanation if search is being performed
'
,
()
=>
{
it
(
'
shows the correct count of child epics
'
,
()
=>
{
const
epic
=
{
epic
=
createMockEpic
({
...
mockFormattedEpic
,
children
:
{
children
:
{
edges
:
[
mockFormattedChildEpic1
],
edges
:
[
mockFormattedChildEpic1
,
mockFormattedChildEpic2
],
},
},
descendantCounts
:
{
descendantCounts
:
{
openedEpics
:
0
,
openedEpics
:
0
,
closedEpics
:
1
,
closedEpics
:
2
,
},
},
};
wrapper
=
createComponent
({
epic
,
hasFiltersApplied
:
true
});
expect
(
wrapper
.
find
(
GlTooltip
).
text
()).
toBe
(
'
1 child epic Some child epics may be hidden due to applied filters
'
,
);
});
});
createWrapper
({
epic
});
it
(
'
does not render if the user license does not support child epics
'
,
()
=>
{
expect
(
getChildEpicsCount
().
text
()).
toBe
(
'
2
'
);
store
.
state
.
allowSubEpics
=
false
;
});
wrapper
=
createComponent
();
});
expect
(
getChildEpicsCount
(
wrapper
).
exists
()).
toBe
(
false
);
});
});
});
});
});
});
...
...
ee/spec/frontend/roadmap/components/milestone_timeline_spec.js
View file @
d1eec7ce
import
Vue
from
'
vue
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
m
ilestoneTimelineComponent
from
'
ee/roadmap/components/milestone_timeline.vue
'
;
import
M
ilestoneTimelineComponent
from
'
ee/roadmap/components/milestone_timeline.vue
'
;
import
MilestoneItem
from
'
ee/roadmap/components/milestone_item.vue
'
;
import
MilestoneItem
from
'
ee/roadmap/components/milestone_item.vue
'
;
import
{
getTimeframeForMonthsView
}
from
'
ee/roadmap/utils/roadmap_utils
'
;
import
{
getTimeframeForMonthsView
}
from
'
ee/roadmap/utils/roadmap_utils
'
;
...
@@ -11,24 +10,6 @@ import { mockTimeframeInitialDate, mockMilestone2, mockGroupId } from 'ee_jest/r
...
@@ -11,24 +10,6 @@ import { mockTimeframeInitialDate, mockMilestone2, mockGroupId } from 'ee_jest/r
const
mockTimeframeMonths
=
getTimeframeForMonthsView
(
mockTimeframeInitialDate
);
const
mockTimeframeMonths
=
getTimeframeForMonthsView
(
mockTimeframeInitialDate
);
const
createComponent
=
({
presetType
=
PRESET_TYPES
.
MONTHS
,
timeframe
=
mockTimeframeMonths
,
milestones
=
[
mockMilestone2
],
currentGroupId
=
mockGroupId
,
}
=
{})
=>
{
const
Component
=
Vue
.
extend
(
milestoneTimelineComponent
);
return
shallowMount
(
Component
,
{
propsData
:
{
presetType
,
timeframe
,
milestones
,
currentGroupId
,
},
});
};
describe
(
'
MilestoneTimelineComponent
'
,
()
=>
{
describe
(
'
MilestoneTimelineComponent
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
...
@@ -36,17 +17,39 @@ describe('MilestoneTimelineComponent', () => {
...
@@ -36,17 +17,39 @@ describe('MilestoneTimelineComponent', () => {
wrapper
.
destroy
();
wrapper
.
destroy
();
});
});
describe
(
'
template
'
,
()
=>
{
const
createWrapper
=
(
props
=
{})
=>
{
it
(
'
renders component container element with class `milestone-timeline-cell`
'
,
()
=>
{
wrapper
=
shallowMount
(
MilestoneTimelineComponent
,
{
wrapper
=
createComponent
();
propsData
:
{
presetType
:
PRESET_TYPES
.
MONTHS
,
expect
(
wrapper
.
find
(
'
.milestone-timeline-cell
'
).
exists
()).
toBe
(
true
);
timeframe
:
mockTimeframeMonths
,
milestones
:
[
mockMilestone2
],
currentGroupId
:
mockGroupId
,
milestonesExpanded
:
true
,
...
props
,
},
});
};
const
findMilestoneTimelineCell
=
()
=>
wrapper
.
find
(
'
.milestone-timeline-cell
'
);
const
findMilestoneItem
=
()
=>
wrapper
.
find
(
MilestoneItem
);
describe
.
each
`
props | hasCellEmpty | hasMilestoneItem
${{}}
|
$
{
false
}
|
${
true
}
${{
milestonesExpanded
:
false
}
} |
${
true
}
|
${
false
}
`
(
'
with $props
'
,
({
props
,
hasCellEmpty
,
hasMilestoneItem
})
=>
{
beforeEach
(()
=>
{
createWrapper
(
props
);
});
});
it
(
'
renders MilestoneItem component
'
,
()
=>
{
it
(
`renders timeline cell with empty class =
${
hasCellEmpty
}
`
,
()
=>
{
wrapper
=
createComponent
();
expect
(
findMilestoneTimelineCell
().
classes
(
'
milestone-timeline-cell-empty
'
)).
toBe
(
hasCellEmpty
,
);
});
expect
(
wrapper
.
find
(
MilestoneItem
).
exists
()).
toBe
(
true
);
it
(
`renders MilestoneItem component =
${
hasMilestoneItem
}
`
,
()
=>
{
expect
(
findMilestoneItem
().
exists
()).
toBe
(
hasMilestoneItem
);
});
});
});
});
});
});
ee/spec/frontend/roadmap/components/milestones_list_section_spec.js
View file @
d1eec7ce
import
{
GlIcon
,
GlButton
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
,
createLocalVue
}
from
'
@vue/test-utils
'
;
import
milestonesListSectionComponent
from
'
ee/roadmap/components/milestones_list_section.vue
'
;
import
milestonesListSectionComponent
from
'
ee/roadmap/components/milestones_list_section.vue
'
;
import
MilestoneTimeline
from
'
ee/roadmap/components/milestone_timeline.vue
'
;
import
MilestoneTimeline
from
'
ee/roadmap/components/milestone_timeline.vue
'
;
...
@@ -9,57 +10,72 @@ import {
...
@@ -9,57 +10,72 @@ import {
TIMELINE_CELL_MIN_WIDTH
,
TIMELINE_CELL_MIN_WIDTH
,
}
from
'
ee/roadmap/constants
'
;
}
from
'
ee/roadmap/constants
'
;
import
{
mockTimeframeInitialDate
,
mockGroupId
,
rawMilestones
}
from
'
ee_jest/roadmap/mock_data
'
;
import
{
mockTimeframeInitialDate
,
mockGroupId
,
rawMilestones
}
from
'
ee_jest/roadmap/mock_data
'
;
import
{
createMockDirective
,
getBinding
}
from
'
helpers/vue_mock_directive
'
;
const
mockTimeframeMonths
=
getTimeframeForMonthsView
(
mockTimeframeInitialDate
);
const
initializeStore
=
mockTimeframeMonths
=>
{
const
store
=
createStore
();
const
store
=
createStore
();
store
.
dispatch
(
'
setInitialData
'
,
{
store
.
dispatch
(
'
setInitialData
'
,
{
currentGroupId
:
mockGroupId
,
currentGroupId
:
mockGroupId
,
presetType
:
PRESET_TYPES
.
MONTHS
,
presetType
:
PRESET_TYPES
.
MONTHS
,
timeframe
:
mockTimeframeMonths
,
timeframe
:
mockTimeframeMonths
,
});
});
store
.
dispatch
(
'
receiveMilestonesSuccess
'
,
{
rawMilestones
});
store
.
dispatch
(
'
receiveMilestonesSuccess
'
,
{
rawMilestones
});
return
store
;
};
const
mockMilestones
=
store
.
state
.
milestones
;
const
createComponent
=
({
describe
(
'
MilestonesListSectionComponent
'
,
()
=>
{
milestones
=
mockMilestones
,
let
wrapper
;
timeframe
=
mockTimeframeMonths
,
let
store
;
currentGroupId
=
mockGroupId
,
presetType
=
PRESET_TYPES
.
MONTHS
,
const
mockTimeframeMonths
=
getTimeframeForMonthsView
(
mockTimeframeInitialDate
);
}
=
{})
=>
{
const
findMilestoneCount
=
()
=>
wrapper
.
find
(
'
[data-testid="count"]
'
);
const
findMilestoneCountTooltip
=
()
=>
getBinding
(
findMilestoneCount
().
element
,
'
gl-tooltip
'
);
const
findExpandButtonContainer
=
()
=>
wrapper
.
find
(
'
[data-testid="expandButton"]
'
);
const
findExpandButtonData
=
()
=>
{
const
container
=
findExpandButtonContainer
();
return
{
icon
:
container
.
find
(
GlIcon
).
attributes
(
'
name
'
),
iconLabel
:
container
.
find
(
GlButton
).
attributes
(
'
aria-label
'
),
tooltip
:
getBinding
(
container
.
element
,
'
gl-tooltip
'
).
value
.
title
,
};
};
const
createWrapper
=
(
props
=
{})
=>
{
const
localVue
=
createLocalVue
();
const
localVue
=
createLocalVue
();
wrapper
=
shallowMount
(
milestonesListSectionComponent
,
{
return
shallowMount
(
milestonesListSectionComponent
,
{
localVue
,
localVue
,
store
,
store
,
stubs
:
{
stubs
:
{
MilestoneTimeline
:
false
,
MilestoneTimeline
:
false
,
},
},
propsData
:
{
propsData
:
{
presetType
,
milestones
:
store
.
state
.
milestones
,
milestones
,
timeframe
:
mockTimeframeMonths
,
timeframe
,
currentGroupId
:
mockGroupId
,
currentGroupId
,
presetType
:
PRESET_TYPES
.
MONTHS
,
...
props
,
},
directives
:
{
GlTooltip
:
createMockDirective
(),
},
},
});
});
};
};
describe
(
'
MilestonesListSectionComponent
'
,
()
=>
{
let
wrapper
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
wrapper
=
createComponent
();
store
=
initializeStore
(
mockTimeframeMonths
);
createWrapper
();
});
});
afterEach
(()
=>
{
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapper
.
destroy
();
wrapper
=
null
;
});
});
describe
(
'
data
'
,
()
=>
{
describe
(
'
data
'
,
()
=>
{
it
(
'
returns default data props
'
,
()
=>
{
it
(
'
returns default data props
'
,
()
=>
{
expect
(
wrapper
.
vm
.
offsetLeft
).
toBe
(
0
);
expect
(
wrapper
.
vm
.
offsetLeft
).
toBe
(
0
);
expect
(
wrapper
.
vm
.
roadmapShellEl
).
toBeDefined
();
expect
(
wrapper
.
vm
.
roadmapShellEl
).
toBeDefined
();
expect
(
wrapper
.
vm
.
milestonesExpanded
).
toBe
(
true
);
});
});
});
});
...
@@ -134,5 +150,41 @@ describe('MilestonesListSectionComponent', () => {
...
@@ -134,5 +150,41 @@ describe('MilestonesListSectionComponent', () => {
expect
(
wrapper
.
find
(
'
.scroll-bottom-shadow
'
).
exists
()).
toBe
(
true
);
expect
(
wrapper
.
find
(
'
.scroll-bottom-shadow
'
).
exists
()).
toBe
(
true
);
});
});
it
(
'
show the correct count of milestones
'
,
()
=>
{
expect
(
findMilestoneCount
().
text
()).
toBe
(
'
2
'
);
});
it
(
'
has a tooltip with the correct count of milestones
'
,
()
=>
{
expect
(
findMilestoneCountTooltip
().
value
).
toBe
(
'
2 milestones
'
);
});
describe
(
'
milestone expand/collapse button
'
,
()
=>
{
it
(
'
is rendered
'
,
()
=>
{
expect
(
findExpandButtonData
()).
toEqual
({
icon
:
'
chevron-down
'
,
iconLabel
:
'
Collapse milestones
'
,
tooltip
:
'
Collapse
'
,
});
});
});
});
describe
(
'
when the milestone list is expanded
'
,
()
=>
{
beforeEach
(()
=>
{
findExpandButtonContainer
()
.
find
(
GlButton
)
.
vm
.
$emit
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
();
});
it
(
'
shows "chevron-right" icon when the milestone toggle button is clicked
'
,
()
=>
{
expect
(
findExpandButtonData
()).
toEqual
({
icon
:
'
chevron-right
'
,
iconLabel
:
'
Expand milestones
'
,
tooltip
:
'
Expand
'
,
});
});
});
});
});
});
locale/gitlab.pot
View file @
d1eec7ce
...
@@ -199,6 +199,11 @@ msgid_plural "%d metrics"
...
@@ -199,6 +199,11 @@ msgid_plural "%d metrics"
msgstr[0] ""
msgstr[0] ""
msgstr[1] ""
msgstr[1] ""
msgid "%d milestone"
msgid_plural "%d milestones"
msgstr[0] ""
msgstr[1] ""
msgid "%d minute"
msgid "%d minute"
msgid_plural "%d minutes"
msgid_plural "%d minutes"
msgstr[0] ""
msgstr[0] ""
...
@@ -5689,7 +5694,7 @@ msgstr ""
...
@@ -5689,7 +5694,7 @@ msgstr ""
msgid "Collapse approvers"
msgid "Collapse approvers"
msgstr ""
msgstr ""
msgid "Collapse
child epic
s"
msgid "Collapse
milestone
s"
msgstr ""
msgstr ""
msgid "Collapse replies"
msgid "Collapse replies"
...
@@ -9296,15 +9301,15 @@ msgstr ""
...
@@ -9296,15 +9301,15 @@ msgstr ""
msgid "Expand approvers"
msgid "Expand approvers"
msgstr ""
msgstr ""
msgid "Expand child epics"
msgstr ""
msgid "Expand down"
msgid "Expand down"
msgstr ""
msgstr ""
msgid "Expand dropdown"
msgid "Expand dropdown"
msgstr ""
msgstr ""
msgid "Expand milestones"
msgstr ""
msgid "Expand sidebar"
msgid "Expand sidebar"
msgstr ""
msgstr ""
...
...
spec/frontend/helpers/vue_mock_directive.js
View file @
d1eec7ce
...
@@ -2,13 +2,21 @@ export const getKey = name => `$_gl_jest_${name}`;
...
@@ -2,13 +2,21 @@ export const getKey = name => `$_gl_jest_${name}`;
export
const
getBinding
=
(
el
,
name
)
=>
el
[
getKey
(
name
)];
export
const
getBinding
=
(
el
,
name
)
=>
el
[
getKey
(
name
)];
export
const
createMockDirective
=
()
=>
({
const
writeBindingToElement
=
(
el
,
{
name
,
value
,
arg
,
modifiers
})
=>
{
bind
(
el
,
{
name
,
value
,
arg
,
modifiers
})
{
el
[
getKey
(
name
)]
=
{
el
[
getKey
(
name
)]
=
{
value
,
value
,
arg
,
arg
,
modifiers
,
modifiers
,
};
};
};
export
const
createMockDirective
=
()
=>
({
bind
(
el
,
binding
)
{
writeBindingToElement
(
el
,
binding
);
},
update
(
el
,
binding
)
{
writeBindingToElement
(
el
,
binding
);
},
},
unbind
(
el
,
{
name
})
{
unbind
(
el
,
{
name
})
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment