Commit 7975af2f authored by Kushal Pandya's avatar Kushal Pandya

Update template, add mixin, cleanup methods for new template

parent 7339cf59
<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>
...@@ -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('tbodyStyles', () => { describe('emptyRowCellStyles', () => {
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('emptyRowCellStyles', () => { describe('shadowCellStyles', () => {
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();
}); });
}); });
......
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