Commit 68f28f0a authored by Kushal Pandya's avatar Kushal Pandya

Roadmap TimelineHeaderItem Component

parent 26150257
<script>
import { monthInWords } from '~/lib/utils/datetime_utility';
import { EPIC_DETAILS_CELL_WIDTH, TIMELINE_CELL_MIN_WIDTH } from '../constants';
import timelineHeaderSubItem from './timeline_header_sub_item.vue';
export default {
components: {
timelineHeaderSubItem,
},
props: {
timeframeIndex: {
type: Number,
required: true,
},
timeframeItem: {
type: Date,
required: true,
},
timeframe: {
type: Array,
required: true,
},
shellWidth: {
type: Number,
required: true,
},
},
data() {
const currentDate = new Date();
return {
currentDate,
currentYear: currentDate.getFullYear(),
currentMonth: currentDate.getMonth(),
};
},
computed: {
thStyles() {
const timeframeLength = this.timeframe.length;
// Calculate minimum width for single cell
// based on total number of months in current timeframe
// and available shellWidth
const minWidth =
Math.ceil((this.shellWidth - EPIC_DETAILS_CELL_WIDTH) / timeframeLength);
// When shellWidth is too low, we need to obey global
// minimum cell width.
if (minWidth < TIMELINE_CELL_MIN_WIDTH) {
return `min-width: ${TIMELINE_CELL_MIN_WIDTH}px;`;
}
return `min-width: ${minWidth}px;`;
},
timelineHeaderLabel() {
const year = this.timeframeItem.getFullYear();
const month = monthInWords(this.timeframeItem, true);
// Show Year only if current timeframe has months between
// two years and current timeframe item is first month
// from one of the two years.
//
// End result of doing this is;
// 2017 Nov, Dec, 2018 Jan, Feb, Mar
if (this.timeframeIndex !== 0 &&
this.timeframe[this.timeframeIndex - 1].getFullYear() === year) {
return month;
}
return `${year} ${month}`;
},
timelineHeaderClass() {
let itemLabelClass = '';
const timeframeYear = this.timeframeItem.getFullYear();
const timeframeMonth = this.timeframeItem.getMonth();
// Show dark color text only if timeframe item year & month
// are greater than current year.
if (timeframeYear >= this.currentYear &&
timeframeMonth >= this.currentMonth) {
itemLabelClass += 'label-dark';
}
// Show bold text only if timeframe item year & month
// is current year & month
if (timeframeYear === this.currentYear &&
timeframeMonth === this.currentMonth) {
itemLabelClass += ' label-bold';
}
return itemLabelClass;
},
},
};
</script>
<template>
<th
class="timeline-header-item"
:style="thStyles"
>
<div
class="item-label"
:class="timelineHeaderClass"
>
{{ timelineHeaderLabel }}
</div>
<timeline-header-sub-item
:timeframe-item="timeframeItem"
:current-date="currentDate"
/>
</th>
</template>
import Vue from 'vue';
import timelineHeaderItemComponent from 'ee/roadmap/components/timeline_header_item.vue';
import { TIMELINE_CELL_MIN_WIDTH } from 'ee/roadmap/constants';
import { mockTimeframe, mockShellWidth } from '../mock_data';
import mountComponent from '../../helpers/vue_mount_component_helper';
const mockTimeframeIndex = 0;
const createComponent = ({
timeframeIndex = mockTimeframeIndex,
timeframeItem = mockTimeframe[mockTimeframeIndex],
timeframe = mockTimeframe,
shellWidth = mockShellWidth,
}) => {
const Component = Vue.extend(timelineHeaderItemComponent);
return mountComponent(Component, {
timeframeIndex,
timeframeItem,
timeframe,
shellWidth,
});
};
describe('TimelineHeaderItemComponent', () => {
let vm;
afterEach(() => {
vm.$destroy();
});
describe('data', () => {
it('returns default data props', () => {
vm = createComponent({});
const currentDate = new Date();
expect(vm.currentDate.getDate()).toBe(currentDate.getDate());
expect(vm.currentYear).toBe(currentDate.getFullYear());
expect(vm.currentMonth).toBe(currentDate.getMonth());
});
});
describe('computed', () => {
describe('thStyles', () => {
it('returns style string for th element based on shellWidth, timeframe length and Epic details cell width', () => {
vm = createComponent({});
expect(vm.thStyles).toBe('min-width: 280px;');
});
it('returns style string for th element with minimum permissible width when calculated width is lower defined minimum width', () => {
vm = createComponent({ shellWidth: 1000 });
expect(vm.thStyles).toBe(`min-width: ${TIMELINE_CELL_MIN_WIDTH}px;`);
});
});
describe('timelineHeaderLabel', () => {
it('returns string containing Year and Month for current timeline header item', () => {
vm = createComponent({});
expect(vm.timelineHeaderLabel).toBe('2017 Nov');
});
it('returns string containing only Month for current timeline header item when previous header contained Year', () => {
vm = createComponent({
timeframeIndex: mockTimeframeIndex + 1,
timeframeItem: mockTimeframe[mockTimeframeIndex + 1],
});
expect(vm.timelineHeaderLabel).toBe('Dec');
});
});
describe('timelineHeaderClass', () => {
it('returns empty string when timeframeItem year or month is less than current year or month', () => {
vm = createComponent({});
expect(vm.timelineHeaderClass).toBe('');
});
it('returns string containing `label-dark label-bold` when current year and month is same as timeframeItem year and month', () => {
vm = createComponent({
timeframeItem: new Date(),
});
expect(vm.timelineHeaderClass).toBe('label-dark label-bold');
});
it('returns string containing `label-dark` when current year and month is less than timeframeItem year and month', () => {
const timeframeIndex = 2;
const timeframeItem = new Date(
mockTimeframe[timeframeIndex].getFullYear(),
mockTimeframe[timeframeIndex].getMonth() + 2,
1,
);
vm = createComponent({
timeframeIndex,
timeframeItem,
});
expect(vm.timelineHeaderClass).toBe('label-dark');
});
});
});
describe('template', () => {
beforeEach(() => {
vm = createComponent({});
});
it('renders component container element with class `timeline-header-item`', () => {
expect(vm.$el.classList.contains('timeline-header-item')).toBeTruthy();
});
it('renders item label element class `item-label` and value as `timelineHeaderLabel`', () => {
const itemLabelEl = vm.$el.querySelector('.item-label');
expect(itemLabelEl).not.toBeNull();
expect(itemLabelEl.innerText.trim()).toBe('2017 Nov');
});
});
});
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