From 339baf8f5d7716baa1a6924c94f1896cf8f6805f Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 18 Jul 2017 17:05:41 -0500 Subject: [PATCH 01/14] refactor async calendar data --- app/assets/javascripts/users/index.js | 2 -- app/assets/javascripts/users/user_tabs.js | 27 +++++++++++++++++++++-- app/controllers/users_controller.rb | 5 +---- app/views/users/calendar.html.haml | 9 -------- app/views/users/show.html.haml | 2 +- 5 files changed, 27 insertions(+), 18 deletions(-) delete mode 100644 app/views/users/calendar.html.haml diff --git a/app/assets/javascripts/users/index.js b/app/assets/javascripts/users/index.js index ecd8e09161ec..c9d5da5c5f94 100644 --- a/app/assets/javascripts/users/index.js +++ b/app/assets/javascripts/users/index.js @@ -1,7 +1,5 @@ -import ActivityCalendar from './activity_calendar'; import User from './user'; // use legacy exports until embedded javascript is refactored -window.Calendar = ActivityCalendar; window.gl = window.gl || {}; window.gl.User = User; diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js index f8e23c8624dc..c499c403e281 100644 --- a/app/assets/javascripts/users/user_tabs.js +++ b/app/assets/javascripts/users/user_tabs.js @@ -1,5 +1,7 @@ /* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, no-param-reassign, class-methods-use-this */ +import ActivityCalendar from './activity_calendar'; + /* UserTabs @@ -60,6 +62,15 @@ content on the Users#show page. */ +const CALENDAR_TEMPLATE = ` +
+
+
+ Summary of issues, merge requests, push events, and comments +
+
+`; + export default class UserTabs { constructor ({ defaultAction, action, parentEl }) { this.loaded = {}; @@ -147,9 +158,21 @@ export default class UserTabs { return; } const $calendarWrap = this.$parentEl.find('.user-calendar'); - $calendarWrap.load($calendarWrap.data('href')); + const calendarPath = $calendarWrap.data('calendarPath'); + const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath'); + + $.ajax({ + dataType: 'json', + type: 'GET', + url: calendarPath, + success: (activityData) => { + $calendarWrap.html(CALENDAR_TEMPLATE); + new ActivityCalendar(activityData, calendarActivitiesPath); + } + }); + new gl.Activities(); - return this.loaded['activity'] = true; + this.loaded['activity'] = true; } toggleLoading(status) { diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 8131eba6a2fd..4ee855806ab9 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -73,10 +73,7 @@ def snippets end def calendar - calendar = contributions_calendar - @activity_dates = calendar.activity_dates - - render 'calendar', layout: false + render json: contributions_calendar.activity_dates end def calendar_activities diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml deleted file mode 100644 index 57b8845c55dc..000000000000 --- a/app/views/users/calendar.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -.clearfix.calendar - .js-contrib-calendar - .calendar-hint - Summary of issues, merge requests, push events, and comments -:javascript - new Calendar( - #{@activity_dates.to_json}, - '#{user_calendar_activities_path}' - ); diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index f246bd7a586e..863fec842f53 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -105,7 +105,7 @@ .tab-content #activity.tab-pane .row-content-block.calender-block.white.second-block.hidden-xs - .user-calendar{ data: { href: user_calendar_path } } + .user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path } } %h4.center.light %i.fa.fa-spinner.fa-spin .user-calendar-activities -- GitLab From f2c0ab398eb71ba9549ebd6d70b7fa7c8707b380 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Tue, 18 Jul 2017 17:27:01 -0500 Subject: [PATCH 02/14] refactor users bundle, remove inline javascript, and transform into an async chunk --- app/assets/javascripts/dispatcher.js | 7 ++++++ app/assets/javascripts/users/index.js | 22 +++++++++++++---- app/assets/javascripts/users/user.js | 34 --------------------------- app/views/users/show.html.haml | 10 -------- config/webpack.config.js | 2 -- 5 files changed, 25 insertions(+), 50 deletions(-) delete mode 100644 app/assets/javascripts/users/user.js diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index ae19592ecbe5..f114dec4e60f 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -516,6 +516,13 @@ import PerformanceBar from './performance_bar'; case 'protected_branches': shortcut_handler = new ShortcutsNavigation(); } + break; + case 'users': + const action = path[1]; + import(/* webpackChunkName: 'user_profile' */ './users') + .then(user => user.default(action)) + .catch(() => {}); + break; } // If we haven't installed a custom shortcut handler, install the default one if (!shortcut_handler) { diff --git a/app/assets/javascripts/users/index.js b/app/assets/javascripts/users/index.js index c9d5da5c5f94..33a83f8dae52 100644 --- a/app/assets/javascripts/users/index.js +++ b/app/assets/javascripts/users/index.js @@ -1,5 +1,19 @@ -import User from './user'; +import Cookies from 'js-cookie'; +import UserTabs from './user_tabs'; -// use legacy exports until embedded javascript is refactored -window.gl = window.gl || {}; -window.gl.User = User; +export default function initUserProfile(action) { + // place profile avatars to top + $('.profile-groups-avatars').tooltip({ + placement: 'top', + }); + + // eslint-disable-next-line no-new + new UserTabs({ parentEl: '.user-profile', action }); + + // hide project limit message + $('.hide-project-limit-message').on('click', (e) => { + e.preventDefault(); + Cookies.set('hide_project_limit_message', 'false'); + $(this).parents('.project-limit-message').remove(); + }); +} diff --git a/app/assets/javascripts/users/user.js b/app/assets/javascripts/users/user.js deleted file mode 100644 index 0b0a3e1afb42..000000000000 --- a/app/assets/javascripts/users/user.js +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable class-methods-use-this */ - -import Cookies from 'js-cookie'; -import UserTabs from './user_tabs'; - -export default class User { - constructor({ action }) { - this.action = action; - this.placeProfileAvatarsToTop(); - this.initTabs(); - this.hideProjectLimitMessage(); - } - - placeProfileAvatarsToTop() { - $('.profile-groups-avatars').tooltip({ - placement: 'top', - }); - } - - initTabs() { - return new UserTabs({ - parentEl: '.user-profile', - action: this.action, - }); - } - - hideProjectLimitMessage() { - $('.hide-project-limit-message').on('click', (e) => { - e.preventDefault(); - Cookies.set('hide_project_limit_message', 'false'); - $(this).parents('.project-limit-message').remove(); - }); - } -} diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 863fec842f53..7107c1b2d81f 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -1,8 +1,5 @@ - page_title @user.name - page_description @user.bio -- content_for :page_specific_javascripts do - = page_specific_javascript_bundle_tag('common_d3') - = page_specific_javascript_bundle_tag('users') - header_title @user.name, user_path(@user) - @no_container = true @@ -129,10 +126,3 @@ .loading-status = spinner - -:javascript - var userProfile; - - userProfile = new gl.User({ - action: "#{controller.action_name}" - }); diff --git a/config/webpack.config.js b/config/webpack.config.js index 1113241e402c..98626d9fe73a 100644 --- a/config/webpack.config.js +++ b/config/webpack.config.js @@ -66,7 +66,6 @@ var config = { stl_viewer: './blob/stl_viewer.js', terminal: './terminal/terminal_bundle.js', u2f: ['vendor/u2f'], - users: './users/index.js', raven: './raven/index.js', vue_merge_request_widget: './vue_merge_request_widget/index.js', test: './test.js', @@ -184,7 +183,6 @@ var config = { name: 'common_d3', chunks: [ 'graphs', - 'users', 'monitoring', ], }), -- GitLab From b74b2d8e867c0b3d58c636df205060f9434cb0c7 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Wed, 19 Jul 2017 01:44:38 -0500 Subject: [PATCH 03/14] fix broken tests --- spec/controllers/users_controller_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 842d82cdbe9e..7aeb6efd86dd 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -80,9 +80,9 @@ it 'renders calendar' do sign_in(user) - get :calendar, username: user.username + get :calendar, username: user.username, format: :json - expect(response).to render_template('calendar') + expect(response).to have_http_status(200) end context 'forked project' do -- GitLab From 8cfc89bc9b946b6d492d29814f78b311f85f4c8c Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 14:41:26 -0500 Subject: [PATCH 04/14] remove needless brackets object accessor syntax --- app/assets/javascripts/users/user_tabs.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js index c499c403e281..5b9f3a681385 100644 --- a/app/assets/javascripts/users/user_tabs.js +++ b/app/assets/javascripts/users/user_tabs.js @@ -154,7 +154,7 @@ export default class UserTabs { } loadActivities() { - if (this.loaded['activity']) { + if (this.loaded.activity) { return; } const $calendarWrap = this.$parentEl.find('.user-calendar'); @@ -172,7 +172,7 @@ export default class UserTabs { }); new gl.Activities(); - this.loaded['activity'] = true; + this.loaded.activity = true; } toggleLoading(status) { -- GitLab From a6732f59e41fdcbccc71938dc88d22763f0f1d54 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 14:43:18 -0500 Subject: [PATCH 05/14] remove implied "GET" http method option --- app/assets/javascripts/users/user_tabs.js | 1 - 1 file changed, 1 deletion(-) diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js index 5b9f3a681385..ab7306c4f15b 100644 --- a/app/assets/javascripts/users/user_tabs.js +++ b/app/assets/javascripts/users/user_tabs.js @@ -163,7 +163,6 @@ export default class UserTabs { $.ajax({ dataType: 'json', - type: 'GET', url: calendarPath, success: (activityData) => { $calendarWrap.html(CALENDAR_TEMPLATE); -- GitLab From ffa4cedab6e6286490c30b8176519609378c8ca7 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 14:58:21 -0500 Subject: [PATCH 06/14] fix all eslint violations in user_tabs.js --- app/assets/javascripts/users/user_tabs.js | 153 +++++++++++----------- 1 file changed, 73 insertions(+), 80 deletions(-) diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js index ab7306c4f15b..fd9fe5973035 100644 --- a/app/assets/javascripts/users/user_tabs.js +++ b/app/assets/javascripts/users/user_tabs.js @@ -1,66 +1,59 @@ -/* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, no-param-reassign, class-methods-use-this */ - import ActivityCalendar from './activity_calendar'; -/* -UserTabs - -Handles persisting and restoring the current tab selection and lazily-loading -content on the Users#show page. - -### Example Markup - - - -
-
- Activity Content -
-
- Groups Content -
-
- Contributed projects content -
-
- Projects content -
-
- Snippets content -
-
- -
-
- Loading Animation -
-
-*/ +/** + * UserTabs + * + * Handles persisting and restoring the current tab selection and lazily-loading + * content on the Users#show page. + * + * ### Example Markup + * + * + * + *
+ *
+ * Activity Content + *
+ *
+ * Groups Content + *
+ *
+ * Contributed projects content + *
+ *
+ * Projects content + *
+ *
+ * Snippets content + *
+ *
+ * + *
+ *
+ * Loading Animation + *
+ *
+ */ const CALENDAR_TEMPLATE = `
@@ -72,12 +65,12 @@ const CALENDAR_TEMPLATE = ` `; export default class UserTabs { - constructor ({ defaultAction, action, parentEl }) { + constructor({ defaultAction, action, parentEl }) { this.loaded = {}; this.defaultAction = defaultAction || 'activity'; this.action = action || this.defaultAction; this.$parentEl = $(parentEl) || $(document); - this._location = window.location; + this.windowLocation = window.location; this.$parentEl.find('.nav-links a') .each((i, navLink) => { this.loaded[$(navLink).attr('data-action')] = false; @@ -93,12 +86,10 @@ export default class UserTabs { } bindEvents() { - this.changeProjectsPageWrapper = this.changeProjectsPage.bind(this); - - this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') - .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event)); - - this.$parentEl.on('click', '.gl-pagination a', this.changeProjectsPageWrapper); + this.$parentEl + .off('shown.bs.tab', '.nav-links a[data-toggle="tab"]') + .on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event)) + .on('click', '.gl-pagination a', event => this.changeProjectsPage(event)); } changeProjectsPage(e) { @@ -133,7 +124,7 @@ export default class UserTabs { const loadableActions = ['groups', 'contributed', 'projects', 'snippets']; if (loadableActions.indexOf(action) > -1) { - return this.loadTab(action, endpoint); + this.loadTab(action, endpoint); } } @@ -142,14 +133,13 @@ export default class UserTabs { beforeSend: () => this.toggleLoading(true), complete: () => this.toggleLoading(false), dataType: 'json', - type: 'GET', url: endpoint, success: (data) => { const tabSelector = `div#${action}`; this.$parentEl.find(tabSelector).html(data.html); this.loaded[action] = true; - return gl.utils.localTimeAgo($('.js-timeago', tabSelector)); - } + gl.utils.localTimeAgo($('.js-timeago', tabSelector)); + }, }); } @@ -166,10 +156,13 @@ export default class UserTabs { url: calendarPath, success: (activityData) => { $calendarWrap.html(CALENDAR_TEMPLATE); + + // eslint-disable-next-line no-new new ActivityCalendar(activityData, calendarActivitiesPath); - } + }, }); + // eslint-disable-next-line no-new new gl.Activities(); this.loaded.activity = true; } @@ -180,13 +173,13 @@ export default class UserTabs { } setCurrentAction(source) { - let new_state = source; - new_state = new_state.replace(/\/+$/, ''); - new_state += this._location.search + this._location.hash; + let newState = source; + newState = newState.replace(/\/+$/, ''); + newState += this.windowLocation.search + this.windowLocation.hash; history.replaceState({ - url: new_state - }, document.title, new_state); - return new_state; + url: newState, + }, document.title, newState); + return newState; } getCurrentAction() { -- GitLab From c4fae77e899d181346792566f381f9049f542910 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 15:17:11 -0500 Subject: [PATCH 07/14] remove janky function binding in activity calendar class --- .../javascripts/users/activity_calendar.js | 100 ++++++++++-------- 1 file changed, 55 insertions(+), 45 deletions(-) diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index b7f50cfd0832..9f1ac3ebf319 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,7 +1,13 @@ -/* eslint-disable func-names, space-before-function-paren, no-var, wrap-iife, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ +/* eslint-disable no-var, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ import d3 from 'd3'; +const LOADING_HTML = ` +
+ +
+`; + export default class ActivityCalendar { constructor(timestamps, calendar_activities_path) { this.calendar_activities_path = calendar_activities_path; @@ -79,40 +85,41 @@ export default class ActivityCalendar { } renderDays() { - return this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g').attr('transform', (function(_this) { - return function(group, i) { - _.each(group, function(stamp, a) { + this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g') + .attr('transform', (group, i) => { + _.each(group, (stamp, a) => { var lastMonth, lastMonthX, month, x; if (a === 0 && stamp.day === 0) { month = stamp.date.getMonth(); - x = (_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace; - lastMonth = _.last(_this.months); + x = (this.daySizeWithSpace * i + 1) + this.daySizeWithSpace; + lastMonth = _.last(this.months); if (lastMonth != null) { lastMonthX = lastMonth.x; } if (lastMonth == null) { - return _this.months.push({ + return this.months.push({ month: month, x: x }); - } else if (month !== lastMonth.month && x - _this.daySizeWithSpace !== lastMonthX) { - return _this.months.push({ + } else if (month !== lastMonth.month && x - this.daySizeWithSpace !== lastMonthX) { + return this.months.push({ month: month, x: x }); } } }); - return "translate(" + ((_this.daySizeWithSpace * i + 1) + _this.daySizeWithSpace) + ", 18)"; - }; - })(this)).selectAll('rect').data(function(stamp) { - return stamp; - }).enter().append('rect').attr('x', '0').attr('y', (function(_this) { - return function(stamp, i) { - return _this.daySizeWithSpace * stamp.day; - }; - })(this)).attr('width', this.daySize).attr('height', this.daySize).attr('title', (function(_this) { - return function(stamp) { + return "translate(" + ((this.daySizeWithSpace * i + 1) + this.daySizeWithSpace) + ", 18)"; + }) + .selectAll('rect') + .data(stamp => stamp) + .enter() + .append('rect') + .attr('x', '0') + .attr('y', stamp => this.daySizeWithSpace * stamp.day) + .attr('width', this.daySize) + .attr('height', this.daySize) + .attr('title', (stamp) => { var contribText, date, dateText; date = new Date(stamp.date); contribText = 'No contributions'; @@ -121,21 +128,20 @@ export default class ActivityCalendar { } dateText = date.format('mmm d, yyyy'); return contribText + "
" + (gl.utils.getDayName(date)) + " " + dateText; - }; - })(this)).attr('class', 'user-contrib-cell js-tooltip').attr('fill', (function(_this) { - return function(stamp) { + }) + .attr('class', 'user-contrib-cell js-tooltip').attr('fill', (stamp) => { if (stamp.count !== 0) { - return _this.color(Math.min(stamp.count, 40)); + return this.color(Math.min(stamp.count, 40)); } else { return '#ededed'; } - }; - })(this)).attr('data-container', 'body').on('click', this.clickDay); + }) + .attr('data-container', 'body') + .on('click', this.clickDay); } renderDayTitles() { - var days; - days = [ + const days = [ { text: 'M', y: 29 + (this.daySizeWithSpace * 1) @@ -147,21 +153,29 @@ export default class ActivityCalendar { y: 29 + (this.daySizeWithSpace * 5) } ]; - return this.svg.append('g').selectAll('text').data(days).enter().append('text').attr('text-anchor', 'middle').attr('x', 8).attr('y', function(day) { - return day.y; - }).text(function(day) { - return day.text; - }).attr('class', 'user-contrib-text'); + this.svg.append('g') + .selectAll('text') + .data(days) + .enter() + .append('text') + .attr('text-anchor', 'middle') + .attr('x', 8) + .attr('y', day => day.y) + .text(day => day.text) + .attr('class', 'user-contrib-text'); } renderMonths() { - return this.svg.append('g').attr('direction', 'ltr').selectAll('text').data(this.months).enter().append('text').attr('x', function(date) { - return date.x; - }).attr('y', 10).attr('class', 'user-contrib-text').text((function(_this) { - return function(date) { - return _this.monthNames[date.month]; - }; - })(this)); + this.svg.append('g') + .attr('direction', 'ltr') + .selectAll('text') + .data(this.months) + .enter() + .append('text') + .attr('x', date => date.x) + .attr('y', 10) + .attr('class', 'user-contrib-text') + .text(date => this.monthNames[date.month]); } renderKey() { @@ -206,12 +220,8 @@ export default class ActivityCalendar { }, cache: false, dataType: 'html', - beforeSend: function() { - return $('.user-calendar-activities').html('
'); - }, - success: function(data) { - return $('.user-calendar-activities').html(data); - } + beforeSend: () => $('.user-calendar-activities').html(LOADING_HTML), + success: data => $('.user-calendar-activities').html(data), }); } else { this.currentSelectedDate = ''; -- GitLab From 6902145716d80a13db08d4d72cb4dc5b195fe1ef Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 15:19:59 -0500 Subject: [PATCH 08/14] resolve camelcase violations --- app/assets/javascripts/users/activity_calendar.js | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index 9f1ac3ebf319..56f3014c9831 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,4 +1,4 @@ -/* eslint-disable no-var, camelcase, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ +/* eslint-disable no-var, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ import d3 from 'd3'; @@ -9,8 +9,8 @@ const LOADING_HTML = ` `; export default class ActivityCalendar { - constructor(timestamps, calendar_activities_path) { - this.calendar_activities_path = calendar_activities_path; + constructor(timestamps, calendarActivitiesPath) { + this.calendarActivitiesPath = calendarActivitiesPath; this.clickDay = this.clickDay.bind(this); this.currentSelectedDate = ''; this.daySpace = 1; @@ -209,15 +209,12 @@ export default class ActivityCalendar { } clickDay(stamp) { - var formatted_date; if (this.currentSelectedDate !== stamp.date) { this.currentSelectedDate = stamp.date; - formatted_date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate(); + const date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate(); return $.ajax({ - url: this.calendar_activities_path, - data: { - date: formatted_date - }, + url: this.calendarActivitiesPath, + data: { date }, cache: false, dataType: 'html', beforeSend: () => $('.user-calendar-activities').html(LOADING_HTML), -- GitLab From 45a446ea9475a658631b83e7c7b42838fe2de3b4 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 15:25:13 -0500 Subject: [PATCH 09/14] resolve comma-dangle and object-shorthand eslint violations --- .../javascripts/users/activity_calendar.js | 35 +++++++------------ 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index 56f3014c9831..b2c3b3d70ca6 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,4 +1,4 @@ -/* eslint-disable no-var, vars-on-top, object-shorthand, comma-dangle, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ +/* eslint-disable no-var, vars-on-top, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ import d3 from 'd3'; @@ -18,6 +18,7 @@ export default class ActivityCalendar { this.daySizeWithSpace = this.daySize + (this.daySpace * 2); this.monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; this.months = []; + // Loop through the timestamps to create a group of objects // The group of objects will be grouped based on the day of the week they are this.timestampsTmp = []; @@ -36,7 +37,7 @@ export default class ActivityCalendar { date.setDate(date.getDate() + i); var day = date.getDay(); - var count = timestamps[date.format('yyyy-mm-dd')]; + var count = timestamps[date.format('yyyy-mm-dd')] || 0; // Create a new group array if this is the first day of the week // or if is first object @@ -45,13 +46,9 @@ export default class ActivityCalendar { group += 1; } - var innerArray = this.timestampsTmp[group - 1]; // Push to the inner array the values that will be used to render map - innerArray.push({ - count: count || 0, - date: date, - day: day - }); + var innerArray = this.timestampsTmp[group - 1]; + innerArray.push({ count, date, day }); } // Init color functions @@ -97,15 +94,9 @@ export default class ActivityCalendar { lastMonthX = lastMonth.x; } if (lastMonth == null) { - return this.months.push({ - month: month, - x: x - }); + return this.months.push({ month, x }); } else if (month !== lastMonth.month && x - this.daySizeWithSpace !== lastMonthX) { - return this.months.push({ - month: month, - x: x - }); + return this.months.push({ month, x }); } } }); @@ -144,14 +135,14 @@ export default class ActivityCalendar { const days = [ { text: 'M', - y: 29 + (this.daySizeWithSpace * 1) + y: 29 + (this.daySizeWithSpace * 1), }, { text: 'W', - y: 29 + (this.daySizeWithSpace * 3) + y: 29 + (this.daySizeWithSpace * 3), }, { text: 'F', - y: 29 + (this.daySizeWithSpace * 5) - } + y: 29 + (this.daySizeWithSpace * 5), + }, ]; this.svg.append('g') .selectAll('text') @@ -227,8 +218,6 @@ export default class ActivityCalendar { } initTooltips() { - return $('.js-contrib-calendar .js-tooltip').tooltip({ - html: true - }); + $('.js-contrib-calendar .js-tooltip').tooltip({ html: true }); } } -- GitLab From ce4e891065368901da214b689f929d4cc7ce5b94 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 15:27:42 -0500 Subject: [PATCH 10/14] resolve no-mixed-operators and no-return-assign eslint violations --- app/assets/javascripts/users/activity_calendar.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index b2c3b3d70ca6..c345f2ae1ce4 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,4 +1,4 @@ -/* eslint-disable no-var, vars-on-top, eqeqeq, no-mixed-operators, no-return-assign, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ +/* eslint-disable no-var, vars-on-top, eqeqeq, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ import d3 from 'd3'; @@ -77,8 +77,8 @@ export default class ActivityCalendar { } renderSvg(group) { - var width = (group + 1) * this.daySizeWithSpace + this.getExtraWidthPadding(group); - return this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar'); + var width = ((group + 1) * this.daySizeWithSpace) + this.getExtraWidthPadding(group); + this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar'); } renderDays() { @@ -88,7 +88,7 @@ export default class ActivityCalendar { var lastMonth, lastMonthX, month, x; if (a === 0 && stamp.day === 0) { month = stamp.date.getMonth(); - x = (this.daySizeWithSpace * i + 1) + this.daySizeWithSpace; + x = (this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace; lastMonth = _.last(this.months); if (lastMonth != null) { lastMonthX = lastMonth.x; @@ -100,7 +100,7 @@ export default class ActivityCalendar { } } }); - return "translate(" + ((this.daySizeWithSpace * i + 1) + this.daySizeWithSpace) + ", 18)"; + return "translate(" + ((this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace) + ", 18)"; }) .selectAll('rect') .data(stamp => stamp) @@ -174,7 +174,7 @@ export default class ActivityCalendar { const keyColors = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; this.svg.append('g') - .attr('transform', `translate(18, ${this.daySizeWithSpace * 8 + 16})`) + .attr('transform', `translate(18, ${(this.daySizeWithSpace * 8) + 16})`) .selectAll('rect') .data(keyColors) .enter() -- GitLab From 9d5bdb5d313f42197cfa8c57da314ea423e8d8a1 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 15:35:25 -0500 Subject: [PATCH 11/14] resolve prefer-template and quotes eslint violations --- .../javascripts/users/activity_calendar.js | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index c345f2ae1ce4..79b31227b77f 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,4 +1,4 @@ -/* eslint-disable no-var, vars-on-top, eqeqeq, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, prefer-template, quotes, no-unused-vars, no-else-return, max-len, class-methods-use-this */ +/* eslint-disable no-var, vars-on-top, eqeqeq, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, no-unused-vars, no-else-return, max-len, class-methods-use-this */ import d3 from 'd3'; @@ -54,6 +54,7 @@ export default class ActivityCalendar { // Init color functions this.colorKey = this.initColorKey(); this.color = this.initColor(); + // Init the svg element this.renderSvg(group); this.renderDays(); @@ -100,7 +101,7 @@ export default class ActivityCalendar { } } }); - return "translate(" + ((this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace) + ", 18)"; + return `translate(${(this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace}, 18)`; }) .selectAll('rect') .data(stamp => stamp) @@ -115,10 +116,10 @@ export default class ActivityCalendar { date = new Date(stamp.date); contribText = 'No contributions'; if (stamp.count > 0) { - contribText = stamp.count + " contribution" + (stamp.count > 1 ? 's' : ''); + contribText = `${stamp.count} contribution${stamp.count > 1 ? 's' : ''}`; } dateText = date.format('mmm d, yyyy'); - return contribText + "
" + (gl.utils.getDayName(date)) + " " + dateText; + return `${contribText}
${gl.utils.getDayName(date)} ${dateText}`; }) .attr('class', 'user-contrib-cell js-tooltip').attr('fill', (stamp) => { if (stamp.count !== 0) { @@ -202,8 +203,14 @@ export default class ActivityCalendar { clickDay(stamp) { if (this.currentSelectedDate !== stamp.date) { this.currentSelectedDate = stamp.date; - const date = this.currentSelectedDate.getFullYear() + "-" + (this.currentSelectedDate.getMonth() + 1) + "-" + this.currentSelectedDate.getDate(); - return $.ajax({ + + const date = [ + this.currentSelectedDate.getFullYear(), + this.currentSelectedDate.getMonth() + 1, + this.currentSelectedDate.getDate(), + ].join('-'); + + $.ajax({ url: this.calendarActivitiesPath, data: { date }, cache: false, @@ -213,7 +220,7 @@ export default class ActivityCalendar { }); } else { this.currentSelectedDate = ''; - return $('.user-calendar-activities').html(''); + $('.user-calendar-activities').html(''); } } -- GitLab From c4718a5f2f443c78a78ad144098ee049d9f70128 Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 15:37:50 -0500 Subject: [PATCH 12/14] resolve consistent-return eslint violation [ci-skip] --- app/assets/javascripts/users/activity_calendar.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index 79b31227b77f..2efb10860d5c 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,4 +1,4 @@ -/* eslint-disable no-var, vars-on-top, eqeqeq, newline-per-chained-call, prefer-arrow-callback, consistent-return, one-var, one-var-declaration-per-line, no-unused-vars, no-else-return, max-len, class-methods-use-this */ +/* eslint-disable no-var, vars-on-top, eqeqeq, newline-per-chained-call, one-var, one-var-declaration-per-line, no-unused-vars, no-else-return, max-len, class-methods-use-this */ import d3 from 'd3'; @@ -94,10 +94,8 @@ export default class ActivityCalendar { if (lastMonth != null) { lastMonthX = lastMonth.x; } - if (lastMonth == null) { - return this.months.push({ month, x }); - } else if (month !== lastMonth.month && x - this.daySizeWithSpace !== lastMonthX) { - return this.months.push({ month, x }); + if (lastMonth == null || (month !== lastMonth.month && x - this.daySizeWithSpace !== lastMonthX)) { + this.months.push({ month, x }); } } }); -- GitLab From 17b43cd4a1df2bbe8e4d59b015cbae741849317f Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 15:43:12 -0500 Subject: [PATCH 13/14] resolve eqeqeq, newline-per-chained-call, no-unused-vars, and no-else-return eslint violations --- .../javascripts/users/activity_calendar.js | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index 2efb10860d5c..b109eb2b2232 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,4 +1,4 @@ -/* eslint-disable no-var, vars-on-top, eqeqeq, newline-per-chained-call, one-var, one-var-declaration-per-line, no-unused-vars, no-else-return, max-len, class-methods-use-this */ +/* eslint-disable no-var, vars-on-top, one-var, one-var-declaration-per-line, max-len, class-methods-use-this */ import d3 from 'd3'; @@ -70,7 +70,7 @@ export default class ActivityCalendar { var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); - if (lastColMonth != secondLastColMonth) { + if (lastColMonth !== secondLastColMonth) { extraWidthPadding = 3; } @@ -79,7 +79,11 @@ export default class ActivityCalendar { renderSvg(group) { var width = ((group + 1) * this.daySizeWithSpace) + this.getExtraWidthPadding(group); - this.svg = d3.select('.js-contrib-calendar').append('svg').attr('width', width).attr('height', 167).attr('class', 'contrib-calendar'); + this.svg = d3.select('.js-contrib-calendar') + .append('svg') + .attr('width', width) + .attr('height', 167) + .attr('class', 'contrib-calendar'); } renderDays() { @@ -119,13 +123,10 @@ export default class ActivityCalendar { dateText = date.format('mmm d, yyyy'); return `${contribText}
${gl.utils.getDayName(date)} ${dateText}`; }) - .attr('class', 'user-contrib-cell js-tooltip').attr('fill', (stamp) => { - if (stamp.count !== 0) { - return this.color(Math.min(stamp.count, 40)); - } else { - return '#ededed'; - } - }) + .attr('class', 'user-contrib-cell js-tooltip') + .attr('fill', stamp => ( + stamp.count !== 0 ? this.color(Math.min(stamp.count, 40)) : '#ededed' + )) .attr('data-container', 'body') .on('click', this.clickDay); } -- GitLab From 288e8ea1e77d46197f6c93ae1f5d0f5cc810625f Mon Sep 17 00:00:00 2001 From: Mike Greiling Date: Thu, 27 Jul 2017 16:36:48 -0500 Subject: [PATCH 14/14] resolve remaining eslint violations --- .../javascripts/users/activity_calendar.js | 119 +++++++++--------- app/assets/javascripts/users/user_tabs.js | 2 +- 2 files changed, 58 insertions(+), 63 deletions(-) diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/users/activity_calendar.js index b109eb2b2232..f091e319f44b 100644 --- a/app/assets/javascripts/users/activity_calendar.js +++ b/app/assets/javascripts/users/activity_calendar.js @@ -1,5 +1,3 @@ -/* eslint-disable no-var, vars-on-top, one-var, one-var-declaration-per-line, max-len, class-methods-use-this */ - import d3 from 'd3'; const LOADING_HTML = ` @@ -8,8 +6,22 @@ const LOADING_HTML = `
`; +function formatTooltipText({ date, count }) { + const dateObject = new Date(date); + const dateDayName = gl.utils.getDayName(dateObject); + const dateText = dateObject.format('mmm d, yyyy'); + + let contribText = 'No contributions'; + if (count > 0) { + contribText = `${count} contribution${count > 1 ? 's' : ''}`; + } + return `${contribText}
${dateDayName} ${dateText}`; +} + +const initColorKey = () => d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); + export default class ActivityCalendar { - constructor(timestamps, calendarActivitiesPath) { + constructor(container, timestamps, calendarActivitiesPath) { this.calendarActivitiesPath = calendarActivitiesPath; this.clickDay = this.clickDay.bind(this); this.currentSelectedDate = ''; @@ -22,22 +34,22 @@ export default class ActivityCalendar { // Loop through the timestamps to create a group of objects // The group of objects will be grouped based on the day of the week they are this.timestampsTmp = []; - var group = 0; + let group = 0; - var today = new Date(); + const today = new Date(); today.setHours(0, 0, 0, 0, 0); - var oneYearAgo = new Date(today); + const oneYearAgo = new Date(today); oneYearAgo.setFullYear(today.getFullYear() - 1); - var days = gl.utils.getDayDifference(oneYearAgo, today); + const days = gl.utils.getDayDifference(oneYearAgo, today); - for (var i = 0; i <= days; i += 1) { - var date = new Date(oneYearAgo); + for (let i = 0; i <= days; i += 1) { + const date = new Date(oneYearAgo); date.setDate(date.getDate() + i); - var day = date.getDay(); - var count = timestamps[date.format('yyyy-mm-dd')] || 0; + const day = date.getDay(); + const count = timestamps[date.format('yyyy-mm-dd')] || 0; // Create a new group array if this is the first day of the week // or if is first object @@ -47,28 +59,30 @@ export default class ActivityCalendar { } // Push to the inner array the values that will be used to render map - var innerArray = this.timestampsTmp[group - 1]; + const innerArray = this.timestampsTmp[group - 1]; innerArray.push({ count, date, day }); } // Init color functions - this.colorKey = this.initColorKey(); + this.colorKey = initColorKey(); this.color = this.initColor(); // Init the svg element - this.renderSvg(group); + this.svg = this.renderSvg(container, group); this.renderDays(); this.renderMonths(); this.renderDayTitles(); this.renderKey(); - this.initTooltips(); + + // Init tooltips + $(`${container} .js-tooltip`).tooltip({ html: true }); } // Add extra padding for the last month label if it is also the last column getExtraWidthPadding(group) { - var extraWidthPadding = 0; - var lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); - var secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); + let extraWidthPadding = 0; + const lastColMonth = this.timestampsTmp[group - 1][0].date.getMonth(); + const secondLastColMonth = this.timestampsTmp[group - 2][0].date.getMonth(); if (lastColMonth !== secondLastColMonth) { extraWidthPadding = 3; @@ -77,9 +91,9 @@ export default class ActivityCalendar { return extraWidthPadding; } - renderSvg(group) { - var width = ((group + 1) * this.daySizeWithSpace) + this.getExtraWidthPadding(group); - this.svg = d3.select('.js-contrib-calendar') + renderSvg(container, group) { + const width = ((group + 1) * this.daySizeWithSpace) + this.getExtraWidthPadding(group); + return d3.select(container) .append('svg') .attr('width', width) .attr('height', 167) @@ -90,15 +104,14 @@ export default class ActivityCalendar { this.svg.selectAll('g').data(this.timestampsTmp).enter().append('g') .attr('transform', (group, i) => { _.each(group, (stamp, a) => { - var lastMonth, lastMonthX, month, x; if (a === 0 && stamp.day === 0) { - month = stamp.date.getMonth(); - x = (this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace; - lastMonth = _.last(this.months); - if (lastMonth != null) { - lastMonthX = lastMonth.x; - } - if (lastMonth == null || (month !== lastMonth.month && x - this.daySizeWithSpace !== lastMonthX)) { + const month = stamp.date.getMonth(); + const x = (this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace; + const lastMonth = _.last(this.months); + if ( + lastMonth == null || + (month !== lastMonth.month && x - this.daySizeWithSpace !== lastMonth.x) + ) { this.months.push({ month, x }); } } @@ -106,29 +119,20 @@ export default class ActivityCalendar { return `translate(${(this.daySizeWithSpace * i) + 1 + this.daySizeWithSpace}, 18)`; }) .selectAll('rect') - .data(stamp => stamp) - .enter() - .append('rect') - .attr('x', '0') - .attr('y', stamp => this.daySizeWithSpace * stamp.day) - .attr('width', this.daySize) - .attr('height', this.daySize) - .attr('title', (stamp) => { - var contribText, date, dateText; - date = new Date(stamp.date); - contribText = 'No contributions'; - if (stamp.count > 0) { - contribText = `${stamp.count} contribution${stamp.count > 1 ? 's' : ''}`; - } - dateText = date.format('mmm d, yyyy'); - return `${contribText}
${gl.utils.getDayName(date)} ${dateText}`; - }) - .attr('class', 'user-contrib-cell js-tooltip') - .attr('fill', stamp => ( - stamp.count !== 0 ? this.color(Math.min(stamp.count, 40)) : '#ededed' - )) - .attr('data-container', 'body') - .on('click', this.clickDay); + .data(stamp => stamp) + .enter() + .append('rect') + .attr('x', '0') + .attr('y', stamp => this.daySizeWithSpace * stamp.day) + .attr('width', this.daySize) + .attr('height', this.daySize) + .attr('fill', stamp => ( + stamp.count !== 0 ? this.color(Math.min(stamp.count, 40)) : '#ededed' + )) + .attr('title', stamp => formatTooltipText(stamp)) + .attr('class', 'user-contrib-cell js-tooltip') + .attr('data-container', 'body') + .on('click', this.clickDay); } renderDayTitles() { @@ -190,15 +194,10 @@ export default class ActivityCalendar { } initColor() { - var colorRange; - colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; + const colorRange = ['#ededed', this.colorKey(0), this.colorKey(1), this.colorKey(2), this.colorKey(3)]; return d3.scale.threshold().domain([0, 10, 20, 30]).range(colorRange); } - initColorKey() { - return d3.scale.linear().range(['#acd5f2', '#254e77']).domain([0, 3]); - } - clickDay(stamp) { if (this.currentSelectedDate !== stamp.date) { this.currentSelectedDate = stamp.date; @@ -222,8 +221,4 @@ export default class ActivityCalendar { $('.user-calendar-activities').html(''); } } - - initTooltips() { - $('.js-contrib-calendar .js-tooltip').tooltip({ html: true }); - } } diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/users/user_tabs.js index fd9fe5973035..5fe6603ce7bf 100644 --- a/app/assets/javascripts/users/user_tabs.js +++ b/app/assets/javascripts/users/user_tabs.js @@ -158,7 +158,7 @@ export default class UserTabs { $calendarWrap.html(CALENDAR_TEMPLATE); // eslint-disable-next-line no-new - new ActivityCalendar(activityData, calendarActivitiesPath); + new ActivityCalendar('.js-contrib-calendar', activityData, calendarActivitiesPath); }, }); -- GitLab