Merge branch '3559-ce-port-group-roadmap' into 'master'
CE Port for Group Roadmap changes See merge request gitlab-org/gitlab-ce!16908
This commit is contained in:
commit
bb6895ec6c
|
@ -1,5 +1,6 @@
|
||||||
import axios from './axios_utils';
|
import axios from './axios_utils';
|
||||||
import { getLocationHash } from './url_utility';
|
import { getLocationHash } from './url_utility';
|
||||||
|
import { convertToCamelCase } from './text_utility';
|
||||||
|
|
||||||
export const getPagePath = (index = 0) => $('body').attr('data-page').split(':')[index];
|
export const getPagePath = (index = 0) => $('body').attr('data-page').split(':')[index];
|
||||||
|
|
||||||
|
@ -395,6 +396,26 @@ export const spriteIcon = (icon, className = '') => {
|
||||||
return `<svg ${classAttribute}><use xlink:href="${gon.sprite_icons}#${icon}" /></svg>`;
|
return `<svg ${classAttribute}><use xlink:href="${gon.sprite_icons}#${icon}" /></svg>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method takes in object with snake_case property names
|
||||||
|
* and returns new object with camelCase property names
|
||||||
|
*
|
||||||
|
* Reasoning for this method is to ensure consistent property
|
||||||
|
* naming conventions across JS code.
|
||||||
|
*/
|
||||||
|
export const convertObjectPropsToCamelCase = (obj = {}) => {
|
||||||
|
if (obj === null) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(obj).reduce((acc, prop) => {
|
||||||
|
const result = acc;
|
||||||
|
|
||||||
|
result[convertToCamelCase(prop)] = obj[prop];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
export const imagePath = imgUrl => `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`;
|
export const imagePath = imgUrl => `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`;
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
window.gl = window.gl || {};
|
||||||
|
|
|
@ -9,6 +9,20 @@ import {
|
||||||
window.timeago = timeago;
|
window.timeago = timeago;
|
||||||
window.dateFormat = dateFormat;
|
window.dateFormat = dateFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns i18n month names array.
|
||||||
|
* If `abbreviated` is provided, returns abbreviated
|
||||||
|
* name.
|
||||||
|
*
|
||||||
|
* @param {Boolean} abbreviated
|
||||||
|
*/
|
||||||
|
const getMonthNames = (abbreviated) => {
|
||||||
|
if (abbreviated) {
|
||||||
|
return [s__('Jan'), s__('Feb'), s__('Mar'), s__('Apr'), s__('May'), s__('Jun'), s__('Jul'), s__('Aug'), s__('Sep'), s__('Oct'), s__('Nov'), s__('Dec')];
|
||||||
|
}
|
||||||
|
return [s__('January'), s__('February'), s__('March'), s__('April'), s__('May'), s__('June'), s__('July'), s__('August'), s__('September'), s__('October'), s__('November'), s__('December')];
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a date object returns the day of the week in English
|
* Given a date object returns the day of the week in English
|
||||||
* @param {date} date
|
* @param {date} date
|
||||||
|
@ -143,7 +157,6 @@ export const getDayDifference = (a, b) => {
|
||||||
* @param {Number} seconds
|
* @param {Number} seconds
|
||||||
* @return {String}
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line import/prefer-default-export
|
|
||||||
export function timeIntervalInWords(intervalInSeconds) {
|
export function timeIntervalInWords(intervalInSeconds) {
|
||||||
const secondsInteger = parseInt(intervalInSeconds, 10);
|
const secondsInteger = parseInt(intervalInSeconds, 10);
|
||||||
const minutes = Math.floor(secondsInteger / 60);
|
const minutes = Math.floor(secondsInteger / 60);
|
||||||
|
@ -158,7 +171,7 @@ export function timeIntervalInWords(intervalInSeconds) {
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function dateInWords(date, abbreviated = false) {
|
export function dateInWords(date, abbreviated = false, hideYear = false) {
|
||||||
if (!date) return date;
|
if (!date) return date;
|
||||||
|
|
||||||
const month = date.getMonth();
|
const month = date.getMonth();
|
||||||
|
@ -169,9 +182,115 @@ export function dateInWords(date, abbreviated = false) {
|
||||||
|
|
||||||
const monthName = abbreviated ? monthNamesAbbr[month] : monthNames[month];
|
const monthName = abbreviated ? monthNamesAbbr[month] : monthNames[month];
|
||||||
|
|
||||||
|
if (hideYear) {
|
||||||
|
return `${monthName} ${date.getDate()}`;
|
||||||
|
}
|
||||||
|
|
||||||
return `${monthName} ${date.getDate()}, ${year}`;
|
return `${monthName} ${date.getDate()}, ${year}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns month name based on provided date.
|
||||||
|
*
|
||||||
|
* @param {Date} date
|
||||||
|
* @param {Boolean} abbreviated
|
||||||
|
*/
|
||||||
|
export const monthInWords = (date, abbreviated = false) => {
|
||||||
|
if (!date) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return getMonthNames(abbreviated)[date.getMonth()];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns number of days in a month for provided date.
|
||||||
|
* courtesy: https://stacko(verflow.com/a/1185804/414749
|
||||||
|
*
|
||||||
|
* @param {Date} date
|
||||||
|
*/
|
||||||
|
export const totalDaysInMonth = (date) => {
|
||||||
|
if (!date) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of Dates referring to Sundays of the month
|
||||||
|
* based on provided date
|
||||||
|
*
|
||||||
|
* @param {Date} date
|
||||||
|
*/
|
||||||
|
export const getSundays = (date) => {
|
||||||
|
if (!date) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const daysToSunday = ['Saturday', 'Friday', 'Thursday', 'Wednesday', 'Tuesday', 'Monday', 'Sunday'];
|
||||||
|
|
||||||
|
const month = date.getMonth();
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const sundays = [];
|
||||||
|
const dateOfMonth = new Date(year, month, 1);
|
||||||
|
|
||||||
|
while (dateOfMonth.getMonth() === month) {
|
||||||
|
const dayName = getDayName(dateOfMonth);
|
||||||
|
if (dayName === 'Sunday') {
|
||||||
|
sundays.push(new Date(dateOfMonth.getTime()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const daysUntilNextSunday = daysToSunday.indexOf(dayName) + 1;
|
||||||
|
dateOfMonth.setDate(dateOfMonth.getDate() + daysUntilNextSunday);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sundays;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns list of Dates representing a timeframe of Months from month of provided date (inclusive)
|
||||||
|
* up to provided length
|
||||||
|
*
|
||||||
|
* For eg;
|
||||||
|
* If current month is January 2018 and `length` provided is `6`
|
||||||
|
* Then this method will return list of Date objects as follows;
|
||||||
|
*
|
||||||
|
* [ October 2017, November 2017, December 2017, January 2018, February 2018, March 2018 ]
|
||||||
|
*
|
||||||
|
* If current month is March 2018 and `length` provided is `3`
|
||||||
|
* Then this method will return list of Date objects as follows;
|
||||||
|
*
|
||||||
|
* [ February 2018, March 2018, April 2018 ]
|
||||||
|
*
|
||||||
|
* @param {Number} length
|
||||||
|
* @param {Date} date
|
||||||
|
*/
|
||||||
|
export const getTimeframeWindow = (length, date) => {
|
||||||
|
if (!length) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentDate = date instanceof Date ? date : new Date();
|
||||||
|
const currentMonthIndex = Math.floor(length / 2);
|
||||||
|
const timeframe = [];
|
||||||
|
|
||||||
|
// Move date object backward to the first month of timeframe
|
||||||
|
currentDate.setDate(1);
|
||||||
|
currentDate.setMonth(currentDate.getMonth() - currentMonthIndex);
|
||||||
|
|
||||||
|
// Iterate and update date for the size of length
|
||||||
|
// and push date reference to timeframe list
|
||||||
|
for (let i = 0; i < length; i += 1) {
|
||||||
|
timeframe.push(new Date(currentDate.getTime()));
|
||||||
|
currentDate.setMonth(currentDate.getMonth() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change date of last timeframe item to last date of the month
|
||||||
|
timeframe[length - 1].setDate(totalDaysInMonth(timeframe[length - 1]));
|
||||||
|
|
||||||
|
return timeframe;
|
||||||
|
};
|
||||||
|
|
||||||
window.gl = window.gl || {};
|
window.gl = window.gl || {};
|
||||||
window.gl.utils = {
|
window.gl.utils = {
|
||||||
...(window.gl.utils || {}),
|
...(window.gl.utils || {}),
|
||||||
|
|
|
@ -73,3 +73,10 @@ export function capitalizeFirstCharacter(text) {
|
||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
export const stripHtml = (string, replace = '') => string.replace(/<[^>]*>/g, replace);
|
export const stripHtml = (string, replace = '') => string.replace(/<[^>]*>/g, replace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts snake_case string to camelCase
|
||||||
|
*
|
||||||
|
* @param {*} string
|
||||||
|
*/
|
||||||
|
export const convertToCamelCase = string => string.replace(/(_\w)/g, s => s[1].toUpperCase());
|
||||||
|
|
|
@ -214,6 +214,7 @@ $tooltip-font-size: 12px;
|
||||||
* Padding
|
* Padding
|
||||||
*/
|
*/
|
||||||
$gl-padding: 16px;
|
$gl-padding: 16px;
|
||||||
|
$gl-padding-8: 8px;
|
||||||
$gl-col-padding: 15px;
|
$gl-col-padding: 15px;
|
||||||
$gl-btn-padding: 10px;
|
$gl-btn-padding: 10px;
|
||||||
$gl-input-padding: 10px;
|
$gl-input-padding: 10px;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
.layout-page{ class: page_with_sidebar_class }
|
.layout-page{ class: page_with_sidebar_class }
|
||||||
- if defined?(nav) && nav
|
- if defined?(nav) && nav
|
||||||
= render "layouts/nav/sidebar/#{nav}"
|
= render "layouts/nav/sidebar/#{nav}"
|
||||||
.content-wrapper
|
.content-wrapper{ class: "#{@content_wrapper_class}" }
|
||||||
= render 'shared/outdated_browser'
|
= render 'shared/outdated_browser'
|
||||||
.mobile-overlay
|
.mobile-overlay
|
||||||
.alert-wrapper
|
.alert-wrapper
|
||||||
|
|
|
@ -105,4 +105,68 @@ describe('dateInWords', () => {
|
||||||
it('should return abbreviated month name', () => {
|
it('should return abbreviated month name', () => {
|
||||||
expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016');
|
expect(datetimeUtility.dateInWords(date, true)).toEqual('Jul 1, 2016');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should return date in words without year', () => {
|
||||||
|
expect(datetimeUtility.dateInWords(date, true, true)).toEqual('Jul 1');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('monthInWords', () => {
|
||||||
|
const date = new Date('2017-01-20');
|
||||||
|
|
||||||
|
it('returns month name from provided date', () => {
|
||||||
|
expect(datetimeUtility.monthInWords(date)).toBe('January');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns abbreviated month name from provided date', () => {
|
||||||
|
expect(datetimeUtility.monthInWords(date, true)).toBe('Jan');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('totalDaysInMonth', () => {
|
||||||
|
it('returns number of days in a month for given date', () => {
|
||||||
|
// 1st Feb, 2016 (leap year)
|
||||||
|
expect(datetimeUtility.totalDaysInMonth(new Date(2016, 1, 1))).toBe(29);
|
||||||
|
|
||||||
|
// 1st Feb, 2017
|
||||||
|
expect(datetimeUtility.totalDaysInMonth(new Date(2017, 1, 1))).toBe(28);
|
||||||
|
|
||||||
|
// 1st Jan, 2017
|
||||||
|
expect(datetimeUtility.totalDaysInMonth(new Date(2017, 0, 1))).toBe(31);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSundays', () => {
|
||||||
|
it('returns array of dates representing all Sundays of the month', () => {
|
||||||
|
// December, 2017 (it has 5 Sundays)
|
||||||
|
const dateOfSundays = [3, 10, 17, 24, 31];
|
||||||
|
const sundays = datetimeUtility.getSundays(new Date(2017, 11, 1));
|
||||||
|
|
||||||
|
expect(sundays.length).toBe(5);
|
||||||
|
sundays.forEach((sunday, index) => {
|
||||||
|
expect(sunday.getDate()).toBe(dateOfSundays[index]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getTimeframeWindow', () => {
|
||||||
|
it('returns array of dates representing a timeframe based on provided length and date', () => {
|
||||||
|
const date = new Date(2018, 0, 1);
|
||||||
|
const mockTimeframe = [
|
||||||
|
new Date(2017, 9, 1),
|
||||||
|
new Date(2017, 10, 1),
|
||||||
|
new Date(2017, 11, 1),
|
||||||
|
new Date(2018, 0, 1),
|
||||||
|
new Date(2018, 1, 1),
|
||||||
|
new Date(2018, 2, 31),
|
||||||
|
];
|
||||||
|
const timeframe = datetimeUtility.getTimeframeWindow(6, date);
|
||||||
|
|
||||||
|
expect(timeframe.length).toBe(6);
|
||||||
|
timeframe.forEach((timeframeItem, index) => {
|
||||||
|
expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBeTruthy();
|
||||||
|
expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBeTruthy();
|
||||||
|
expect(timeframeItem.getDate() === mockTimeframe[index].getDate()).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -480,4 +480,33 @@ describe('common_utils', () => {
|
||||||
expect(commonUtils.spriteIcon('test', 'fa fa-test')).toEqual('<svg class="fa fa-test"><use xlink:href="icons.svg#test" /></svg>');
|
expect(commonUtils.spriteIcon('test', 'fa fa-test')).toEqual('<svg class="fa fa-test"><use xlink:href="icons.svg#test" /></svg>');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('convertObjectPropsToCamelCase', () => {
|
||||||
|
it('returns new object with camelCase property names by converting object with snake_case names', () => {
|
||||||
|
const snakeRegEx = /(_\w)/g;
|
||||||
|
const mockObj = {
|
||||||
|
id: 1,
|
||||||
|
group_name: 'GitLab.org',
|
||||||
|
absolute_web_url: 'https://gitlab.com/gitlab-org/',
|
||||||
|
};
|
||||||
|
const mappings = {
|
||||||
|
id: 'id',
|
||||||
|
groupName: 'group_name',
|
||||||
|
absoluteWebUrl: 'absolute_web_url',
|
||||||
|
};
|
||||||
|
|
||||||
|
const convertedObj = commonUtils.convertObjectPropsToCamelCase(mockObj);
|
||||||
|
|
||||||
|
Object.keys(convertedObj).forEach((prop) => {
|
||||||
|
expect(snakeRegEx.test(prop)).toBeFalsy();
|
||||||
|
expect(convertedObj[prop]).toBe(mockObj[mappings[prop]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('return empty object if method is called with null or undefined', () => {
|
||||||
|
expect(Object.keys(commonUtils.convertObjectPropsToCamelCase(null)).length).toBe(0);
|
||||||
|
expect(Object.keys(commonUtils.convertObjectPropsToCamelCase()).length).toBe(0);
|
||||||
|
expect(Object.keys(commonUtils.convertObjectPropsToCamelCase({})).length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -72,4 +72,10 @@ describe('text_utility', () => {
|
||||||
expect(textUtils.stripHtml('This is a text with <p>html</p>.', ' ')).toEqual('This is a text with html .');
|
expect(textUtils.stripHtml('This is a text with <p>html</p>.', ' ')).toEqual('This is a text with html .');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('convertToCamelCase', () => {
|
||||||
|
it('converts snake_case string to camelCase string', () => {
|
||||||
|
expect(textUtils.convertToCamelCase('snake_case')).toBe('snakeCase');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue