From 214d741ba5caf1e533873bba1b8f030e80774d72 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Tue, 8 Jan 2019 18:37:21 +0530 Subject: [PATCH 1/2] Add support for `offset` values in `isInViewport` --- .../javascripts/lib/utils/common_utils.js | 7 +++--- .../lib/utils/common_utils_spec.js | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 9e22cdc04e9..fc34d243dd7 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -118,12 +118,13 @@ export const handleLocationHash = () => { // Check if element scrolled into viewport from above or below // Courtesy http://stackoverflow.com/a/7557433/414749 -export const isInViewport = el => { +export const isInViewport = (el, offset = {}) => { const rect = el.getBoundingClientRect(); + const { top, left } = offset; return ( - rect.top >= 0 && - rect.left >= 0 && + rect.top >= (top || 0) && + rect.left >= (left || 0) && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth ); diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 1ec1e8a8dd9..f320f232687 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -716,4 +716,29 @@ describe('common_utils', () => { expect(commonUtils.roundOffFloat(34567.14159, -5)).toBe(0); }); }); + + describe('isInViewport', () => { + let el; + + beforeEach(() => { + el = document.createElement('div'); + }); + + afterEach(() => { + document.body.removeChild(el); + }); + + it('returns true when provided `el` is in viewport', () => { + document.body.appendChild(el); + + expect(commonUtils.isInViewport(el)).toBe(true); + }); + + it('returns false when provided `el` is not in viewport', () => { + el.setAttribute('style', 'position: absolute; top: -1000px; left: -1000px;'); + document.body.appendChild(el); + + expect(commonUtils.isInViewport(el)).toBe(false); + }); + }); }); From 2bb73dab72c4a9c1518d04814b697b8f2dff3194 Mon Sep 17 00:00:00 2001 From: Kushal Pandya Date: Tue, 8 Jan 2019 18:40:41 +0530 Subject: [PATCH 2/2] Add `newDate` helper, update `getTimeframeWindowFrom` Adds `newDate` helper to copy date object without keeping reference. Adds negative length support in `getTimeframeWindowFrom` to go back in time. --- .../javascripts/lib/utils/datetime_utility.js | 34 ++++++++++++---- .../lib/utils/datetime_utility_spec.js | 40 ++++++++++++++++++- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 59007d5950e..01dbbb9dd16 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -7,6 +7,14 @@ import { languageCode, s__ } from '../../locale'; window.timeago = timeago; +/** + * This method allows you to create new Date instance from existing + * date instance without keeping the reference. + * + * @param {Date} date + */ +export const newDate = date => (date instanceof Date ? new Date(date.getTime()) : new Date()); + /** * Returns i18n month names array. * If `abbreviated` is provided, returns abbreviated @@ -321,23 +329,35 @@ export const getSundays = date => { /** * Returns list of Dates representing a timeframe of months from startDate and length + * This method also supports going back in time when `length` is negative number * - * @param {Date} startDate + * @param {Date} initialStartDate * @param {Number} length */ -export const getTimeframeWindowFrom = (startDate, length) => { - if (!(startDate instanceof Date) || !length) { +export const getTimeframeWindowFrom = (initialStartDate, length) => { + if (!(initialStartDate instanceof Date) || !length) { return []; } + const startDate = newDate(initialStartDate); + const moveMonthBy = length > 0 ? 1 : -1; + + startDate.setDate(1); + startDate.setHours(0, 0, 0, 0); + // Iterate and set date for the size of length // and push date reference to timeframe list - const timeframe = new Array(length) - .fill() - .map((val, i) => new Date(startDate.getFullYear(), startDate.getMonth() + i, 1)); + const timeframe = new Array(Math.abs(length)).fill().map(() => { + const currentMonth = startDate.getTime(); + startDate.setMonth(startDate.getMonth() + moveMonthBy); + return new Date(currentMonth); + }); // Change date of last timeframe item to last date of the month - timeframe[length - 1].setDate(totalDaysInMonth(timeframe[length - 1])); + // when length is positive + if (length > 0) { + timeframe[timeframe.length - 1].setDate(totalDaysInMonth(timeframe[timeframe.length - 1])); + } return timeframe; }; diff --git a/spec/javascripts/lib/utils/datetime_utility_spec.js b/spec/javascripts/lib/utils/datetime_utility_spec.js index bebe76f76c5..5327ec9d2a0 100644 --- a/spec/javascripts/lib/utils/datetime_utility_spec.js +++ b/spec/javascripts/lib/utils/datetime_utility_spec.js @@ -156,7 +156,7 @@ describe('getSundays', () => { }); describe('getTimeframeWindowFrom', () => { - it('returns array of date objects upto provided length start with provided startDate', () => { + it('returns array of date objects upto provided length (positive number) into the future starting from provided startDate', () => { const startDate = new Date(2018, 0, 1); const mockTimeframe = [ new Date(2018, 0, 1), @@ -174,6 +174,25 @@ describe('getTimeframeWindowFrom', () => { expect(timeframeItem.getDate()).toBe(mockTimeframe[index].getDate()); }); }); + + it('returns array of date objects upto provided length (negative number) into the past starting from provided startDate', () => { + const startDate = new Date(2018, 0, 1); + const mockTimeframe = [ + new Date(2018, 0, 1), + new Date(2017, 11, 1), + new Date(2017, 10, 1), + new Date(2017, 9, 1), + new Date(2017, 8, 1), + ]; + const timeframe = datetimeUtility.getTimeframeWindowFrom(startDate, -5); + + expect(timeframe.length).toBe(5); + timeframe.forEach((timeframeItem, index) => { + expect(timeframeItem.getFullYear()).toBe(mockTimeframe[index].getFullYear()); + expect(timeframeItem.getMonth()).toBe(mockTimeframe[index].getMonth()); + expect(timeframeItem.getDate()).toBe(mockTimeframe[index].getDate()); + }); + }); }); describe('formatTime', () => { @@ -376,3 +395,22 @@ describe('calculateRemainingMilliseconds', () => { expect(milliseconds).toBe(0); }); }); + +describe('newDate', () => { + it('returns new date instance from existing date instance', () => { + const initialDate = new Date(2019, 0, 1); + const copiedDate = datetimeUtility.newDate(initialDate); + + expect(copiedDate.getTime()).toBe(initialDate.getTime()); + + initialDate.setMonth(initialDate.getMonth() + 1); + + expect(copiedDate.getTime()).not.toBe(initialDate.getTime()); + }); + + it('returns date instance when provided date param is not of type date or is undefined', () => { + const initialDate = datetimeUtility.newDate(); + + expect(initialDate instanceof Date).toBe(true); + }); +});