Commit eb571eb2 authored by David O'Regan's avatar David O'Regan

Feat: Update schedule grid for current day in week

Update the schedule grid to allow
the grid to begin at the first day
of the current week but still track
the current day via the current
day indecator
parent a138ca53
...@@ -3,6 +3,7 @@ import { GlCard, GlButtonGroup, GlButton, GlModalDirective, GlTooltipDirective } ...@@ -3,6 +3,7 @@ import { GlCard, GlButtonGroup, GlButton, GlModalDirective, GlTooltipDirective }
import * as Sentry from '@sentry/browser'; import * as Sentry from '@sentry/browser';
import { capitalize } from 'lodash'; import { capitalize } from 'lodash';
import { import {
getStartOfWeek,
formatDate, formatDate,
nWeeksBefore, nWeeksBefore,
nWeeksAfter, nWeeksAfter,
...@@ -87,7 +88,7 @@ export default { ...@@ -87,7 +88,7 @@ export default {
data() { data() {
return { return {
presetType: this.$options.PRESET_TYPES.WEEKS, presetType: this.$options.PRESET_TYPES.WEEKS,
timeframeStartDate: new Date(), timeframeStartDate: getStartOfWeek(new Date()),
rotations: this.schedule.rotations.nodes, rotations: this.schedule.rotations.nodes,
rotationToUpdate: {}, rotationToUpdate: {},
}; };
...@@ -127,7 +128,8 @@ export default { ...@@ -127,7 +128,8 @@ export default {
methods: { methods: {
switchPresetType(type) { switchPresetType(type) {
this.presetType = type; this.presetType = type;
this.timeframeStartDate = new Date(); this.timeframeStartDate =
type === PRESET_TYPES.WEEKS ? getStartOfWeek(new Date()) : new Date();
}, },
formatPresetType(type) { formatPresetType(type) {
return capitalize(type); return capitalize(type);
......
...@@ -32,7 +32,7 @@ export default { ...@@ -32,7 +32,7 @@ export default {
<template> <template>
<span <span
v-if="isVisible" v-if="isVisible"
:style="getIndicatorStyles(presetType)" :style="getIndicatorStyles(presetType, timeframeItem)"
data-testid="current-day-indicator" data-testid="current-day-indicator"
class="current-day-indicator" class="current-day-indicator"
></span> ></span>
......
...@@ -48,7 +48,7 @@ export default { ...@@ -48,7 +48,7 @@ export default {
currentDateTime >= this.timeframeItem.getTime() && currentDateTime >= this.timeframeItem.getTime() &&
currentDateTime <= lastDayOfCurrentWeekTime currentDateTime <= lastDayOfCurrentWeekTime
) { ) {
return 'label-dark label-bold'; return 'label-bold';
} }
return ''; return '';
...@@ -66,7 +66,7 @@ export default { ...@@ -66,7 +66,7 @@ export default {
<span class="timeline-header-item" :style="timelineHeaderStyles"> <span class="timeline-header-item" :style="timelineHeaderStyles">
<div <div
:class="timelineHeaderClass" :class="timelineHeaderClass"
class="item-label gl-pl-6 gl-py-4" class="item-label label-dark gl-pl-5 gl-py-4"
data-testid="timeline-header-label" data-testid="timeline-header-label"
> >
{{ timelineHeaderLabel }} {{ timelineHeaderLabel }}
......
...@@ -78,7 +78,7 @@ export default { ...@@ -78,7 +78,7 @@ export default {
> >
<span <span
v-if="hasToday" v-if="hasToday"
:style="getIndicatorStyles($options.PRESET_TYPES.WEEKS)" :style="getIndicatorStyles($options.PRESET_TYPES.WEEKS, timeframeItem)"
class="current-day-indicator-header preset-weeks" class="current-day-indicator-header preset-weeks"
></span> ></span>
</div> </div>
......
...@@ -44,3 +44,7 @@ export const ASSIGNEE_SPACER_SMALL = 1; ...@@ -44,3 +44,7 @@ export const ASSIGNEE_SPACER_SMALL = 1;
export const TIMELINE_CELL_WIDTH = 180; export const TIMELINE_CELL_WIDTH = 180;
export const SHIFT_WIDTH_CALCULATION_DELAY = 250; export const SHIFT_WIDTH_CALCULATION_DELAY = 250;
export const CURRENT_DAY_INDICATOR_OFFSET = 2.25; export const CURRENT_DAY_INDICATOR_OFFSET = 2.25;
export const oneHourOffsetDayView = 100 / HOURS_IN_DAY;
export const oneDayOffsetWeekView = 100 / DAYS_IN_WEEK;
export const oneHourOffsetWeekView = oneDayOffsetWeekView / HOURS_IN_DAY;
import { isToday } from '~/lib/utils/datetime_utility'; import { getDayDifference, isToday } from '~/lib/utils/datetime_utility';
import { import {
DAYS_IN_WEEK,
HOURS_IN_DAY,
PRESET_TYPES, PRESET_TYPES,
CURRENT_DAY_INDICATOR_OFFSET, oneHourOffsetDayView,
oneDayOffsetWeekView,
oneHourOffsetWeekView,
} from '../constants'; } from '../constants';
export default { export default {
...@@ -38,23 +38,30 @@ export default { ...@@ -38,23 +38,30 @@ export default {
this.$options.currentDate = currentDate; this.$options.currentDate = currentDate;
}, },
methods: { methods: {
getIndicatorStyles(presetType = PRESET_TYPES.WEEKS) { getIndicatorStyles(presetType = PRESET_TYPES.WEEKS, timeframeStartDate = new Date()) {
const currentDate = new Date();
const base = 100 / HOURS_IN_DAY;
const hours = base * currentDate.getHours();
if (presetType === PRESET_TYPES.DAYS) { if (presetType === PRESET_TYPES.DAYS) {
const minutes = base * (currentDate.getMinutes() / 60) - CURRENT_DAY_INDICATOR_OFFSET; return this.getDayViewIndicatorStyles();
return {
left: `${hours + minutes}%`,
};
} }
const weeklyDayOffset = 100 / DAYS_IN_WEEK / 2; return this.getWeekViewIndicatorStyles(timeframeStartDate);
const weeklyHourOffset = (weeklyDayOffset / HOURS_IN_DAY) * currentDate.getHours(); },
getDayViewIndicatorStyles() {
const currentDate = new Date();
const hours = oneHourOffsetDayView * currentDate.getHours();
const minutes = oneHourOffsetDayView * (currentDate.getMinutes() / 60);
return {
left: `${hours + minutes}%`,
};
},
getWeekViewIndicatorStyles(timeframeStartDate) {
const currentDate = new Date();
const hourOffset = oneHourOffsetWeekView * currentDate.getHours();
const daysSinceShiftStart = getDayDifference(timeframeStartDate, currentDate);
const leftOffset = oneDayOffsetWeekView * daysSinceShiftStart + hourOffset;
return { return {
left: `${weeklyDayOffset + weeklyHourOffset}%`, left: `${Math.round(leftOffset)}%`,
}; };
}, },
}, },
......
---
title: Update schedule grid to start at beginning of current week
merge_request: 57004
author:
type: added
...@@ -74,7 +74,7 @@ exports[`RotationsListSectionComponent when the timeframe includes today renders ...@@ -74,7 +74,7 @@ exports[`RotationsListSectionComponent when the timeframe includes today renders
<span <span
class="current-day-indicator" class="current-day-indicator"
data-testid="current-day-indicator" data-testid="current-day-indicator"
style="left: 7.142857142857143%;" style="left: 29%;"
/> />
<div> <div>
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import CurrentDayIndicator from 'ee/oncall_schedules/components/schedule/components/current_day_indicator.vue'; import CurrentDayIndicator from 'ee/oncall_schedules/components/schedule/components/current_day_indicator.vue';
import { PRESET_TYPES, DAYS_IN_WEEK, HOURS_IN_DAY } from 'ee/oncall_schedules/constants'; import { PRESET_TYPES, HOURS_IN_DAY } from 'ee/oncall_schedules/constants';
import { useFakeDate } from 'helpers/fake_date'; import { useFakeDate } from 'helpers/fake_date';
describe('CurrentDayIndicator', () => { describe('CurrentDayIndicator', () => {
...@@ -10,7 +10,8 @@ describe('CurrentDayIndicator', () => { ...@@ -10,7 +10,8 @@ describe('CurrentDayIndicator', () => {
// January 1st, 2018 is the first day of the week-long timeframe // January 1st, 2018 is the first day of the week-long timeframe
// so as long as current date (faked January 3rd, 2018) is within week timeframe // so as long as current date (faked January 3rd, 2018) is within week timeframe
// current indicator will be rendered // current indicator will be rendered
const mockTimeframeInitialDate = new Date(2018, 0, 1); const mockTimeframeInitialDate = new Date(2018, 0, 1); // Monday
const mockCurrentDate = new Date(2018, 0, 3); // Wednesday
function createComponent({ function createComponent({
props = { presetType: PRESET_TYPES.WEEKS, timeframeItem: mockTimeframeInitialDate }, props = { presetType: PRESET_TYPES.WEEKS, timeframeItem: mockTimeframeInitialDate },
...@@ -36,19 +37,26 @@ describe('CurrentDayIndicator', () => { ...@@ -36,19 +37,26 @@ describe('CurrentDayIndicator', () => {
expect(wrapper.classes('current-day-indicator')).toBe(true); expect(wrapper.classes('current-day-indicator')).toBe(true);
}); });
it('sets correct styles for a week', async () => { it('sets correct styles for a week that on a different day than the timeframe start date', () => {
const left = 100 / DAYS_IN_WEEK / 2; /**
expect(wrapper.attributes('style')).toBe(`left: ${left}%;`); * Our start date for the timeframe in this spec is a Monday,
* and the current day is the following Wednesday.
* This creates a gap of two days so our generated offset should represent:
* DayDiffOffset + weeklyOffset + weeklyHourOffset
* 29 + 0
*/
const leftOffset = '29';
expect(wrapper.attributes('style')).toBe(`left: ${leftOffset}%;`);
}); });
it('sets correct styles for a day', async () => { it('sets correct styles for a day', () => {
createComponent({ createComponent({
props: { presetType: PRESET_TYPES.DAYS, timeframeItem: new Date(2018, 0, 3) }, props: { presetType: PRESET_TYPES.DAYS, timeframeItem: mockCurrentDate },
}); });
const currentDate = new Date(); const currentDate = new Date();
const base = 100 / HOURS_IN_DAY; const base = 100 / HOURS_IN_DAY;
const hours = base * currentDate.getHours(); const hours = base * currentDate.getHours();
const minutes = base * (currentDate.getMinutes() / 60) - 2.25; const minutes = base * (currentDate.getMinutes() / 60);
const left = hours + minutes; const left = hours + minutes;
expect(wrapper.attributes('style')).toBe(`left: ${left}%;`); expect(wrapper.attributes('style')).toBe(`left: ${left}%;`);
}); });
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { DAYS_IN_WEEK, HOURS_IN_DAY, PRESET_TYPES } from 'ee/oncall_schedules/constants'; import {
PRESET_TYPES,
oneHourOffsetDayView,
oneDayOffsetWeekView,
oneHourOffsetWeekView,
} from 'ee/oncall_schedules/constants';
import CommonMixin from 'ee/oncall_schedules/mixins/common_mixin'; import CommonMixin from 'ee/oncall_schedules/mixins/common_mixin';
import { useFakeDate } from 'helpers/fake_date'; import { useFakeDate } from 'helpers/fake_date';
import * as dateTimeUtility from '~/lib/utils/datetime_utility'; import * as dateTimeUtility from '~/lib/utils/datetime_utility';
...@@ -81,20 +86,26 @@ describe('Schedule Common Mixins', () => { ...@@ -81,20 +86,26 @@ describe('Schedule Common Mixins', () => {
}); });
describe('getIndicatorStyles', () => { describe('getIndicatorStyles', () => {
it('returns object containing `left` offset', () => { it('returns object containing `left` offset for the weekly grid', () => {
const left = 100 / DAYS_IN_WEEK / 2; const mockTimeframeInitialDate = new Date(2018, 0, 1);
expect(wrapper.vm.getIndicatorStyles()).toEqual( const mockCurrentDate = new Date(2018, 0, 3);
const hourOffset = oneHourOffsetWeekView * mockCurrentDate.getHours();
const daysSinceShiftStart = dateTimeUtility.getDayDifference(
mockTimeframeInitialDate,
mockCurrentDate,
);
const leftOffset = oneDayOffsetWeekView * daysSinceShiftStart + hourOffset;
expect(wrapper.vm.getIndicatorStyles(PRESET_TYPES.WEEKS, mockTimeframeInitialDate)).toEqual(
expect.objectContaining({ expect.objectContaining({
left: `${left}%`, left: `${Math.round(leftOffset)}%`,
}), }),
); );
}); });
it('returns object containing `left` offset for a single day grid', () => { it('returns object containing `left` offset for a single day grid', () => {
const currentDate = new Date(2018, 0, 8); const currentDate = new Date(2018, 0, 8);
const base = 100 / HOURS_IN_DAY; const hours = oneHourOffsetDayView * currentDate.getHours();
const hours = base * currentDate.getHours(); const minutes = oneHourOffsetDayView * (currentDate.getMinutes() / 60);
const minutes = base * (currentDate.getMinutes() / 60) - 2.25;
expect(wrapper.vm.getIndicatorStyles(PRESET_TYPES.DAYS)).toEqual( expect(wrapper.vm.getIndicatorStyles(PRESET_TYPES.DAYS)).toEqual(
expect.objectContaining({ expect.objectContaining({
......
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