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
7975af2f
Commit
7975af2f
authored
Apr 02, 2018
by
Kushal Pandya
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update template, add mixin, cleanup methods for new template
parent
7339cf59
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
69 additions
and
84 deletions
+69
-84
ee/app/assets/javascripts/roadmap/components/epics_list_section.vue
...ets/javascripts/roadmap/components/epics_list_section.vue
+45
-61
spec/javascripts/roadmap/components/epics_list_section_spec.js
...javascripts/roadmap/components/epics_list_section_spec.js
+24
-23
No files found.
ee/app/assets/javascripts/roadmap/components/epics_list_section.vue
View file @
7975af2f
<
script
>
<
script
>
import
$
from
'
jquery
'
;
import
eventHub
from
'
../event_hub
'
;
import
eventHub
from
'
../event_hub
'
;
import
{
SCROLL_BAR_SIZE
}
from
'
../constants
'
;
import
SectionMixin
from
'
../mixins/section_mixin
'
;
import
epicItem
from
'
./epic_item.vue
'
;
import
epicItem
from
'
./epic_item.vue
'
;
...
@@ -10,6 +9,9 @@
...
@@ -10,6 +9,9 @@
components
:
{
components
:
{
epicItem
,
epicItem
,
},
},
mixins
:
[
SectionMixin
,
],
props
:
{
props
:
{
epics
:
{
epics
:
{
type
:
Array
,
type
:
Array
,
...
@@ -27,6 +29,10 @@
...
@@ -27,6 +29,10 @@
type
:
Number
,
type
:
Number
,
required
:
true
,
required
:
true
,
},
},
listScrollable
:
{
type
:
Boolean
,
required
:
true
,
},
},
},
data
()
{
data
()
{
return
{
return
{
...
@@ -38,26 +44,20 @@
...
@@ -38,26 +44,20 @@
};
};
},
},
computed
:
{
computed
:
{
/**
emptyRowContainerStyles
()
{
* Return width after reducing scrollbar size
return
{
* such that Epic item cells do not consider
height
:
`
${
this
.
emptyRowHeight
}
px`
,
* scrollbar
};
*/
calcShellWidth
()
{
return
this
.
shellWidth
-
SCROLL_BAR_SIZE
;
},
/**
* Adjust tbody styles while pushing scrollbar further away
* from the view
*/
tbodyStyles
()
{
return
`width:
${
this
.
shellWidth
+
SCROLL_BAR_SIZE
}
px; height:
${
this
.
shellHeight
}
px;`
;
},
},
emptyRowCellStyles
()
{
emptyRowCellStyles
()
{
return
`height:
${
this
.
emptyRowHeight
}
px;`
;
return
{
width
:
`
${
this
.
sectionItemWidth
}
px`
,
};
},
},
shadowCellStyles
()
{
shadowCellStyles
()
{
return
`left:
${
this
.
offsetLeft
}
px;`
;
return
{
left
:
`
${
this
.
offsetLeft
}
px`
,
};
},
},
},
},
watch
:
{
watch
:
{
...
@@ -69,14 +69,18 @@
...
@@ -69,14 +69,18 @@
},
},
},
},
mounted
()
{
mounted
()
{
eventHub
.
$on
(
'
epicsListScrolled
'
,
this
.
handleEpicsListScroll
);
this
.
$nextTick
(()
=>
{
this
.
$nextTick
(()
=>
{
this
.
initMounted
();
this
.
initMounted
();
});
});
},
},
beforeDestroy
()
{
eventHub
.
$off
(
'
epicsListScrolled
'
,
this
.
handleEpicsListScroll
);
},
methods
:
{
methods
:
{
initMounted
()
{
initMounted
()
{
// Get available shell height based on viewport height
// Get available shell height based on viewport height
this
.
shellHeight
=
window
.
innerHeight
-
(
this
.
$el
.
offsetTop
+
this
.
$root
.
$el
.
offsetTop
)
;
this
.
shellHeight
=
window
.
innerHeight
-
this
.
$el
.
offsetTop
;
// In case there are epics present, initialize empty row
// In case there are epics present, initialize empty row
if
(
this
.
epics
.
length
)
{
if
(
this
.
epics
.
length
)
{
...
@@ -113,32 +117,12 @@
...
@@ -113,32 +117,12 @@
});
});
// set height and show empty row reducing horizontal scrollbar size
// set height and show empty row reducing horizontal scrollbar size
this
.
emptyRowHeight
=
(
this
.
shellHeight
-
approxChildrenHeight
)
-
1
;
this
.
emptyRowHeight
=
(
this
.
shellHeight
-
approxChildrenHeight
);
this
.
showEmptyRow
=
true
;
this
.
showEmptyRow
=
true
;
}
else
{
}
else
{
this
.
showBottomShadow
=
true
;
this
.
showBottomShadow
=
true
;
}
}
},
},
/**
* We can easily use `eventHub` and dispatch this event
* to all sibling and child components but it adds an overhead/delay
* resulting to janky element positioning. Hence, we directly
* update raw element properties upon event via jQuery.
*/
handleScroll
()
{
const
{
scrollTop
,
scrollLeft
,
scrollHeight
,
clientHeight
}
=
this
.
$el
;
const
tableEl
=
this
.
$el
.
parentElement
;
if
(
tableEl
)
{
const
$theadEl
=
$
(
tableEl
).
find
(
'
thead
'
);
const
$tbodyEl
=
$
(
tableEl
).
find
(
'
tbody
'
);
$theadEl
.
css
(
'
left
'
,
-
scrollLeft
);
$theadEl
.
find
(
'
th:nth-child(1)
'
).
css
(
'
left
'
,
scrollLeft
);
$tbodyEl
.
find
(
'
td:nth-child(1)
'
).
css
(
'
left
'
,
scrollLeft
);
}
this
.
showBottomShadow
=
(
Math
.
ceil
(
scrollTop
)
+
clientHeight
)
<
scrollHeight
;
eventHub
.
$emit
(
'
epicsListScrolled
'
,
scrollTop
,
scrollLeft
);
},
/**
/**
* `clientWidth` is full width of list section, and we need to
* `clientWidth` is full width of list section, and we need to
* scroll up to 60% of the view where today indicator is present.
* scroll up to 60% of the view where today indicator is present.
...
@@ -149,45 +133,45 @@
...
@@ -149,45 +133,45 @@
const
uptoTodayIndicator
=
Math
.
ceil
((
this
.
$el
.
clientWidth
*
60
)
/
100
);
const
uptoTodayIndicator
=
Math
.
ceil
((
this
.
$el
.
clientWidth
*
60
)
/
100
);
this
.
$el
.
scrollTo
(
uptoTodayIndicator
,
0
);
this
.
$el
.
scrollTo
(
uptoTodayIndicator
,
0
);
},
},
handleEpicsListScroll
({
scrollTop
,
clientHeight
,
scrollHeight
})
{
this
.
showBottomShadow
=
(
Math
.
ceil
(
scrollTop
)
+
clientHeight
)
<
scrollHeight
;
},
},
},
};
};
</
script
>
</
script
>
<
template
>
<
template
>
<
tbody
<
div
class=
"epics-list-section"
class=
"epics-list-section"
:style=
"tbodyStyles"
:style=
"sectionContainerStyles"
@
scroll=
"handleScroll"
>
>
<tr
v-if=
"showBottomShadow"
class=
"bottom-shadow-cell"
:style=
"shadowCellStyles"
></tr>
<epic-item
<epic-item
v-for=
"(epic, index) in epics"
v-for=
"(epic, index) in epics"
:key=
"index"
:key=
"index"
:epic=
"epic"
:epic=
"epic"
:timeframe=
"timeframe"
:timeframe=
"timeframe"
:current-group-id=
"currentGroupId"
:current-group-id=
"currentGroupId"
:shell-width=
"calcShellWidth"
:shell-width=
"sectionShellWidth"
:item-width=
"sectionItemWidth"
/>
/>
<
tr
<
div
v-if=
"showEmptyRow"
v-if=
"showEmptyRow"
class=
"epics-list-item epics-list-item-empty"
class=
"epics-list-item epics-list-item-empty clearfix"
:style=
"emptyRowContainerStyles"
>
>
<td
<span
class=
"epic-details-cell"
></span>
class=
"epic-details-cell"
<span
:style=
"emptyRowCellStyles"
>
</td>
<td
class=
"epic-timeline-cell"
v-for=
"(timeframeItem, index) in timeframe"
v-for=
"(timeframeItem, index) in timeframe"
:key=
"index"
:key=
"index"
class=
"epic-timeline-cell"
:style=
"emptyRowCellStyles"
:style=
"emptyRowCellStyles"
>
>
</td>
</span>
</tr>
</div>
</tbody>
<div
v-if=
"showBottomShadow"
class=
"scroll-bottom-shadow"
:style=
"shadowCellStyles"
></div>
</div>
</
template
>
</
template
>
spec/javascripts/roadmap/components/epics_list_section_spec.js
View file @
7975af2f
...
@@ -15,6 +15,7 @@ const createComponent = ({
...
@@ -15,6 +15,7 @@ const createComponent = ({
timeframe
=
mockTimeframe
,
timeframe
=
mockTimeframe
,
currentGroupId
=
mockGroupId
,
currentGroupId
=
mockGroupId
,
shellWidth
=
mockShellWidth
,
shellWidth
=
mockShellWidth
,
listScrollable
=
false
,
})
=>
{
})
=>
{
const
Component
=
Vue
.
extend
(
epicsListSectionComponent
);
const
Component
=
Vue
.
extend
(
epicsListSectionComponent
);
...
@@ -23,6 +24,7 @@ const createComponent = ({
...
@@ -23,6 +24,7 @@ const createComponent = ({
timeframe
,
timeframe
,
currentGroupId
,
currentGroupId
,
shellWidth
,
shellWidth
,
listScrollable
,
});
});
};
};
...
@@ -39,6 +41,8 @@ describe('EpicsListSectionComponent', () => {
...
@@ -39,6 +41,8 @@ describe('EpicsListSectionComponent', () => {
expect
(
vm
.
shellHeight
).
toBe
(
0
);
expect
(
vm
.
shellHeight
).
toBe
(
0
);
expect
(
vm
.
emptyRowHeight
).
toBe
(
0
);
expect
(
vm
.
emptyRowHeight
).
toBe
(
0
);
expect
(
vm
.
showEmptyRow
).
toBe
(
false
);
expect
(
vm
.
showEmptyRow
).
toBe
(
false
);
expect
(
vm
.
offsetLeft
).
toBe
(
0
);
expect
(
vm
.
showBottomShadow
).
toBe
(
false
);
});
});
});
});
...
@@ -47,24 +51,21 @@ describe('EpicsListSectionComponent', () => {
...
@@ -47,24 +51,21 @@ describe('EpicsListSectionComponent', () => {
vm
=
createComponent
({});
vm
=
createComponent
({});
});
});
describe
(
'
calcShellWidth
'
,
()
=>
{
describe
(
'
emptyRowContainerStyles
'
,
()
=>
{
it
(
'
returns shellWidth after deducting predefined scrollbar size
'
,
()
=>
{
it
(
'
returns computed style object based on emptyRowHeight prop value
'
,
()
=>
{
// shellWidth is 2000 (as defined above in mockShellWidth)
expect
(
vm
.
emptyRowContainerStyles
.
height
).
toBe
(
'
0px
'
);
// SCROLLBAR_SIZE is 15 (as defined in app's constants.js)
// Hence, calcShellWidth = shellWidth - SCROLLBAR_SIZE
expect
(
vm
.
calcShellWidth
).
toBe
(
1985
);
});
});
});
});
describe
(
'
tbody
Styles
'
,
()
=>
{
describe
(
'
emptyRowCell
Styles
'
,
()
=>
{
it
(
'
returns computed style
string based on shellWidth and shellHeight
'
,
()
=>
{
it
(
'
returns computed style
object based on sectionItemWidth prop value
'
,
()
=>
{
expect
(
vm
.
tbodyStyles
).
toBe
(
'
width: 2015px; height: 0px;
'
);
expect
(
vm
.
emptyRowCellStyles
.
width
).
toBe
(
'
280px
'
);
});
});
});
});
describe
(
'
emptyR
owCellStyles
'
,
()
=>
{
describe
(
'
shad
owCellStyles
'
,
()
=>
{
it
(
'
returns computed style
string based on emptyRowHeight
'
,
()
=>
{
it
(
'
returns computed style
object based on `offsetLeft` prop value
'
,
()
=>
{
expect
(
vm
.
emptyRowCellStyles
).
toBe
(
'
height: 0px;
'
);
expect
(
vm
.
shadowCellStyles
.
left
).
toBe
(
'
0px
'
);
});
});
});
});
});
});
...
@@ -104,7 +105,7 @@ describe('EpicsListSectionComponent', () => {
...
@@ -104,7 +105,7 @@ describe('EpicsListSectionComponent', () => {
describe
(
'
initEmptyRow
'
,
()
=>
{
describe
(
'
initEmptyRow
'
,
()
=>
{
it
(
'
sets `emptyRowHeight` and `showEmptyRow` props when shellHeight is greater than approximate height of epics list
'
,
(
done
)
=>
{
it
(
'
sets `emptyRowHeight` and `showEmptyRow` props when shellHeight is greater than approximate height of epics list
'
,
(
done
)
=>
{
vm
.
$nextTick
(()
=>
{
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
emptyRowHeight
).
toBe
(
599
);
// total size -1px
expect
(
vm
.
emptyRowHeight
).
toBe
(
600
);
expect
(
vm
.
showEmptyRow
).
toBe
(
true
);
expect
(
vm
.
showEmptyRow
).
toBe
(
true
);
done
();
done
();
});
});
...
@@ -125,14 +126,6 @@ describe('EpicsListSectionComponent', () => {
...
@@ -125,14 +126,6 @@ describe('EpicsListSectionComponent', () => {
});
});
});
});
describe
(
'
handleScroll
'
,
()
=>
{
it
(
'
emits `epicsListScrolled` event via eventHub
'
,
()
=>
{
spyOn
(
eventHub
,
'
$emit
'
);
vm
.
handleScroll
();
expect
(
eventHub
.
$emit
).
toHaveBeenCalledWith
(
'
epicsListScrolled
'
,
jasmine
.
any
(
Number
),
jasmine
.
any
(
Number
));
});
});
describe
(
'
scrollToTodayIndicator
'
,
()
=>
{
describe
(
'
scrollToTodayIndicator
'
,
()
=>
{
it
(
'
scrolls table body to put timeline today indicator in focus
'
,
()
=>
{
it
(
'
scrolls table body to put timeline today indicator in focus
'
,
()
=>
{
spyOn
(
vm
.
$el
,
'
scrollTo
'
);
spyOn
(
vm
.
$el
,
'
scrollTo
'
);
...
@@ -154,9 +147,17 @@ describe('EpicsListSectionComponent', () => {
...
@@ -154,9 +147,17 @@ describe('EpicsListSectionComponent', () => {
});
});
});
});
it
(
'
renders component container element with `width` and `left` properties applied via style attribute
'
,
(
done
)
=>
{
it
(
'
renders component container element with `width` property applied via style attribute
'
,
(
done
)
=>
{
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$el
.
getAttribute
(
'
style
'
)).
toBe
(
`width:
${
mockShellWidth
}
px;`
);
done
();
});
});
it
(
'
renders bottom shadow element when `showBottomShadow` prop is true
'
,
(
done
)
=>
{
vm
.
showBottomShadow
=
true
;
vm
.
$nextTick
(()
=>
{
vm
.
$nextTick
(()
=>
{
expect
(
vm
.
$el
.
getAttribute
(
'
style
'
)).
toBe
(
'
width: 2015px; height: 0px;
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.scroll-bottom-shadow
'
)).
not
.
toBe
(
null
);
done
();
done
();
});
});
});
});
...
...
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