From f304fc6320a1d5a64f4522931c0ba3eb67534c71 Mon Sep 17 00:00:00 2001 From: Henriette Dinger Date: Mon, 15 Oct 2018 10:27:50 +0200 Subject: [PATCH] Move tour steps to separate files, disable tour on mobile && fix marker url for correct arrow head in safari --- app/assets/javascripts/application.js.erb | 7 +- .../javascripts/onboarding/backlogs_tour.js | 62 ++++ .../javascripts/onboarding/homescreen_tour.js | 26 ++ .../javascripts/onboarding/onboarding_tour.js | 103 +++++++ .../javascripts/onboarding/overview_tour.js | 67 +++++ .../onboarding/work_package_tour.js | 56 ++++ app/assets/javascripts/onboarding_tour.js | 282 ------------------ app/assets/javascripts/vendor/enjoyhint.js | 2 +- app/assets/stylesheets/vendor/_enjoyhint.sass | 8 - config/initializers/assets.rb | 1 - 10 files changed, 321 insertions(+), 293 deletions(-) create mode 100644 app/assets/javascripts/onboarding/backlogs_tour.js create mode 100644 app/assets/javascripts/onboarding/homescreen_tour.js create mode 100644 app/assets/javascripts/onboarding/onboarding_tour.js create mode 100644 app/assets/javascripts/onboarding/overview_tour.js create mode 100644 app/assets/javascripts/onboarding/work_package_tour.js delete mode 100644 app/assets/javascripts/onboarding_tour.js diff --git a/app/assets/javascripts/application.js.erb b/app/assets/javascripts/application.js.erb index 1d48051c23..8ed1879148 100644 --- a/app/assets/javascripts/application.js.erb +++ b/app/assets/javascripts/application.js.erb @@ -49,7 +49,12 @@ //= require danger_zone_validation //= require flash_messages -//= require onboarding_tour +//= require onboarding/homescreen_tour +//= require onboarding/overview_tour +//= require onboarding/backlogs_tour +//= require onboarding/work_package_tour +//= require onboarding/onboarding_tour + function checkAll(selector, checked) { jQuery('#' + selector + ' input:checkbox').not(':disabled').each(function() { diff --git a/app/assets/javascripts/onboarding/backlogs_tour.js b/app/assets/javascripts/onboarding/backlogs_tour.js new file mode 100644 index 0000000000..3a83d03e55 --- /dev/null +++ b/app/assets/javascripts/onboarding/backlogs_tour.js @@ -0,0 +1,62 @@ +(function ($) { + $(function() { + window.scrumBacklogsTourSteps = [ + { + 'next #content-wrapper': I18n.t('js.onboarding.steps.backlogs_overview'), + 'showSkip': false, + 'containerClass': '-dark' + }, + { + 'event': 'click', + 'selector': '.backlog .menu-trigger', + 'description': I18n.t('js.onboarding.steps.backlogs_task_board_arrow'), + 'showSkip': false, + 'clickable': true, + }, + { + 'event': 'custom', + 'selector': '.backlog .menu .items', + 'description': I18n.t('js.onboarding.steps.backlogs_task_board_select'), + 'showSkip': false, + 'clickable': true, + 'containerClass': '-dark', + onBeforeStart: function () { + // Handle next step + jQuery('.backlog .show_task_board').click(function () { + tutorialInstance.trigger('next'); + }); + + // Disable clicks on the wp context menu links + $(".backlog .menu a:not('.show_task_board')").addClass('-disabled').bind('click', preventClickHandler); + } + } + ]; + + window.scrumTaskBoardTourSteps = [ + { + 'next #content-wrapper': I18n.t('js.onboarding.steps.backlogs_task_board'), + 'showSkip': false, + 'containerClass': '-dark' + }, + { + 'custom .toggler': I18n.t('js.onboarding.steps.wp_toggler'), + 'showSkip': false, + 'shape': 'circle', + 'radius': 20, + 'clickable': true, + onBeforeStart: function () { + waitForElement('.wp-query-menu--item', '.wp-query-menu--results-container', function() { + tutorialInstance.trigger('next'); + }); + } + }, + { + "click .wp-query-menu--item[data-category='default']": I18n.t('js.onboarding.steps.wp_query'), + 'showSkip': false, + 'timeout': 200, + 'margin': 0, + 'clickable': true + } + ]; + }); +}(jQuery)) diff --git a/app/assets/javascripts/onboarding/homescreen_tour.js b/app/assets/javascripts/onboarding/homescreen_tour.js new file mode 100644 index 0000000000..adc6dc5253 --- /dev/null +++ b/app/assets/javascripts/onboarding/homescreen_tour.js @@ -0,0 +1,26 @@ +(function ($) { + $(function() { + window.homescreenOnboardingTourSteps = [ + { + 'next #top-menu': I18n.t('js.onboarding.steps.welcome') + }, + { + 'description': I18n.t('js.onboarding.steps.project_selection'), + 'selector': '.widget-box.projects', + 'event': 'custom', + 'showSkip': false, + 'containerClass': '-dark', + 'clickable': true, + onBeforeStart: function () { + // Handle next step + $('.widget-box.projects a').click(function () { + tutorialInstance.trigger('next'); + }); + + // Disable clicks on the wp context menu links + $('.widget-box--blocks--buttons .button').addClass('-disabled').bind('click', preventClickHandler); + } + } + ]; + }); +}(jQuery)) diff --git a/app/assets/javascripts/onboarding/onboarding_tour.js b/app/assets/javascripts/onboarding/onboarding_tour.js new file mode 100644 index 0000000000..c30be77480 --- /dev/null +++ b/app/assets/javascripts/onboarding/onboarding_tour.js @@ -0,0 +1,103 @@ +(function ($) { + $(function() { + // ------------------------------- Global ------------------------------- + window.tutorialInstance; + window.preventClickHandler = function (e) { + e.preventDefault(); + e.stopPropagation(); + }; + window.waitForElement = function(element, container, execFunction) { + // Wait for the element to be ready + var observer = new MutationObserver(function (mutations, observerInstance) { + if ($(element).length) { + observerInstance.disconnect(); // stop observing + execFunction(); + return; + } + }); + observer.observe($(container)[0], { + childList: true, + subtree: true + }); + } + + + var storageKey = 'openProject-onboardingTour'; + var currentTourPart = sessionStorage.getItem(storageKey); + var url = new URL(window.location.href); + + // ------------------------------- Initial start ------------------------------- + // Do not show the tutorial on mobile + if(! /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) { + + // Start after the intro modal (language selection) + // This has to be changed once the project selection is implemented + if (url.searchParams.get("first_time_user")) { + currentTourPart = ''; + sessionStorage.setItem(storageKey, 'readyToStart'); + + // Start automatically when the language selection is closed + $('.op-modal--modal-close-button').click(function () { + initializeTour('startOverviewTour', '.widget-box--blocks--buttons .button'); + startTour(homescreenOnboardingTourSteps); + }); + } + + // ------------------------------- Tutorial Homescreen page ------------------------------- + if (currentTourPart === "readyToStart") { + initializeTour('startOverviewTour', '.widget-box--blocks--buttons .button'); + startTour(homescreenOnboardingTourSteps); + } + + // ------------------------------- Tutorial Overview page ------------------------------- + if (currentTourPart === "startOverviewTour") { + if ($('.backlogs-menu-item').length > 0) { + initializeTour('startBacklogsTour'); + startTour(scrumOverviewOnboardingTourSteps); + } else { + initializeTour('startWpTour'); + startTour(overviewOnboardingTourSteps); + } + } + + // ------------------------------- Tutorial Backlogs page ------------------------------- + if (currentTourPart === "startBacklogsTour") { + initializeTour('startTaskBoardTour', ".backlog .menu a:not('.show_task_board')"); + startTour(scrumBacklogsTourSteps); + } + + // ------------------------------- Tutorial Task Board page ------------------------------- + if (currentTourPart === "startTaskBoardTour") { + initializeTour('startWpTour'); + startTour(scrumTaskBoardTourSteps); + } + + // ------------------------------- Tutorial WP page ------------------------------- + if (currentTourPart === "startWpTour") { + initializeTour('wpFinished', '.wp-table--details-link, .wp-table-context-menu-link, .wp-table--cell-span'); + + waitForElement('.work-package--results-tbody', '.work-packages-split-view--tabletimeline-side', function() { + startTour(wpOnboardingTourSteps); + }); + } + } + + function initializeTour(storageValue, disabledElements) { + tutorialInstance = new EnjoyHint({ + onEnd: function () { + sessionStorage.setItem(storageKey, storageValue); + }, + onSkip: function () { + sessionStorage.setItem(storageKey, 'skipped'); + if (disabledElements) jQuery(disabledElements).removeClass('-disabled').unbind('click', preventClickHandler); + } + }); + } + + function startTour(steps) { + tutorialInstance.set(steps); + tutorialInstance.run(); + } + + }); +}(jQuery)); diff --git a/app/assets/javascripts/onboarding/overview_tour.js b/app/assets/javascripts/onboarding/overview_tour.js new file mode 100644 index 0000000000..53276b91b8 --- /dev/null +++ b/app/assets/javascripts/onboarding/overview_tour.js @@ -0,0 +1,67 @@ +(function ($) { + $(function() { + window.overviewOnboardingTourSteps = [ + { + 'next #content-wrapper': I18n.t('js.onboarding.steps.project_overview'), + 'showSkip': false, + 'containerClass': '-dark' + }, + { + 'next #menu-sidebar': I18n.t('js.onboarding.steps.sidebar'), + 'showSkip': false + }, + { + 'next .settings-menu-item': I18n.t('js.onboarding.steps.settings'), + 'showSkip': false + }, + { + 'next .members-menu-item': I18n.t('js.onboarding.steps.members'), + 'showSkip': false + }, + { + 'custom .toggler': I18n.t('js.onboarding.steps.wp_toggler'), + 'showSkip': false, + 'shape': 'circle', + 'radius': 20, + 'clickable': true, + onBeforeStart: function () { + waitForElement('.wp-query-menu--item', '.wp-query-menu--results-container', function() { + tutorialInstance.trigger('next'); + }); + } + }, + { + "click .wp-query-menu--item[data-category='default']": I18n.t('js.onboarding.steps.wp_query'), + 'showSkip': false, + 'margin': 0, + 'clickable': true + } + ]; + + window.scrumOverviewOnboardingTourSteps = [ + { + 'next #content-wrapper': I18n.t('js.onboarding.steps.project_overview'), + 'showSkip': false, + 'containerClass': '-dark' + }, + { + 'next #menu-sidebar': I18n.t('js.onboarding.steps.sidebar'), + 'showSkip': false + }, + { + 'next .settings-menu-item': I18n.t('js.onboarding.steps.settings'), + 'showSkip': false + }, + { + 'next .members-menu-item': I18n.t('js.onboarding.steps.members'), + 'showSkip': false + }, + { + 'click .backlogs-menu-item': I18n.t('js.onboarding.steps.backlogs'), + 'showSkip': false, + 'margin': 0, + 'clickable': true + } + ]; + }); +}(jQuery)) diff --git a/app/assets/javascripts/onboarding/work_package_tour.js b/app/assets/javascripts/onboarding/work_package_tour.js new file mode 100644 index 0000000000..29c911f989 --- /dev/null +++ b/app/assets/javascripts/onboarding/work_package_tour.js @@ -0,0 +1,56 @@ +(function ($) { + $(function() { + window.wpOnboardingTourSteps = [ + { + 'custom .wp-table--row': I18n.t('js.onboarding.steps.wp_list'), + 'showSkip': false, + 'margin': 5, + 'clickable': true, + onBeforeStart: function () { + // Handle next step + $('.wp-table--row ').dblclick(function (e) { + if (!$(e.target).hasClass('wp-edit-field--display-field')) tutorialInstance.trigger('next'); + }); + $('.wp-table--cell-td.id a').click(function () { + tutorialInstance.trigger('next'); + }); + + // Disable clicks on the wp context menu links + $('.wp-table--details-link, .wp-table-context-menu-link, .wp-table--cell-span').addClass('-disabled').bind('click', preventClickHandler); + } + }, + { + 'next .work-packages-full-view--split-left': I18n.t('js.onboarding.steps.wp_full_view'), + 'showSkip': false, + 'containerClass': '-dark' + }, + { + 'click .work-packages-list-view-button': I18n.t('js.onboarding.steps.wp_back_button'), + 'showSkip': false, + 'clickable': true + }, + { + 'next .add-work-package': I18n.t('js.onboarding.steps.wp_create_button'), + 'showSkip': false, + 'shape': 'circle' + }, + { + 'click .timeline-toolbar--button': I18n.t('js.onboarding.steps.wp_timeline_button'), + 'showSkip': false, + 'shape': 'circle', + 'clickable': true + }, + { + 'next .work-packages-tabletimeline--timeline-side': I18n.t('js.onboarding.steps.wp_timeline'), + 'showSkip': false, + 'containerClass': '-dark' + }, + { + 'next .menu-item--help': I18n.t('js.onboarding.steps.help_menu'), + 'shape': 'circle', + "nextButton": {text: I18n.t('js.onboarding.steps.got_it')}, + 'showSkip': false + } + ]; + }); +}(jQuery)) diff --git a/app/assets/javascripts/onboarding_tour.js b/app/assets/javascripts/onboarding_tour.js deleted file mode 100644 index 099f862fbe..0000000000 --- a/app/assets/javascripts/onboarding_tour.js +++ /dev/null @@ -1,282 +0,0 @@ -(function ($) { - $(function() { - var preventClickHandler = function(e) { - e.preventDefault(); - e.stopPropagation(); - }; - var storageKey = 'openProject-onboardingTour'; - var currentTourPart = sessionStorage.getItem(storageKey); - var url = new URL(window.location.href); - var tutorialInstance; - - var homescreenOnboardingTourSteps = [ - { - 'next #top-menu' : I18n.t('js.onboarding.steps.welcome') - }, - { - 'description' : I18n.t('js.onboarding.steps.project_selection'), - 'selector' : '.widget-box.projects', - 'event' : 'custom', - 'showSkip' : false, - 'containerClass' : '-dark', - 'clickable' : true, - onBeforeStart: function(){ - // Handle next step - $('.widget-box.projects a').click(function() { - tutorialInstance.trigger('next'); - }); - - // Disable clicks on the wp context menu links - $('.widget-box--blocks--buttons .button').addClass('-disabled').bind('click', preventClickHandler); - } - } - ]; - - var overviewOnboardingTourSteps = [ - { - 'next #content' : I18n.t('js.onboarding.steps.project_overview'), - 'showSkip' : false, - 'containerClass' : '-dark' - }, - { - 'next #menu-sidebar' : I18n.t('js.onboarding.steps.sidebar'), - 'showSkip' : false - }, - { - 'next .settings-menu-item' : I18n.t('js.onboarding.steps.settings'), - 'showSkip' : false - }, - { - 'next .members-menu-item' : I18n.t('js.onboarding.steps.members'), - 'showSkip' : false - }, - { - 'click .toggler' : I18n.t('js.onboarding.steps.wp_toggler'), - 'showSkip' : false, - 'shape' : 'circle', - 'radius' : 20, - 'clickable' : true - }, - { - "click .wp-query-menu--item[data-category='default']": I18n.t('js.onboarding.steps.wp_query'), - 'showSkip' : false, - 'timeout' : 200, - 'margin' : 0, - 'clickable' : true - } - ]; - - var scrumOverviewOnboardingTourSteps = [ - { - 'next #content' : I18n.t('js.onboarding.steps.project_overview'), - 'showSkip' : false, - 'containerClass' : '-dark' - }, - { - 'next #menu-sidebar' : I18n.t('js.onboarding.steps.sidebar'), - 'showSkip' : false - }, - { - 'next .settings-menu-item' : I18n.t('js.onboarding.steps.settings'), - 'showSkip' : false - }, - { - 'next .members-menu-item' : I18n.t('js.onboarding.steps.members'), - 'showSkip' : false - }, - { - 'click .backlogs-menu-item' : I18n.t('js.onboarding.steps.backlogs'), - 'showSkip' : false, - 'margin' : 0, - 'clickable' : true - } - ]; - - var scrumBacklogsTourSteps = [ - { - 'next #content' : I18n.t('js.onboarding.steps.backlogs_overview'), - 'showSkip' : false, - 'containerClass' : '-dark' - }, - { - 'event' : 'click', - 'selector' : '.backlog .menu-trigger', - 'description' : I18n.t('js.onboarding.steps.backlogs_task_board_arrow'), - 'showSkip' : false, - 'clickable' : true, - }, - { - 'event' : 'custom', - 'selector' : '.backlog .menu .items', - 'description' : I18n.t('js.onboarding.steps.backlogs_task_board_select'), - 'showSkip' : false, - 'clickable' : true, - 'containerClass' : '-dark', - onBeforeStart: function(){ - // Handle next step - jQuery('.backlog .show_task_board').click(function() { - tutorialInstance.trigger('next'); - }); - - // Disable clicks on the wp context menu links - $(".backlog .menu a:not('.show_task_board')").addClass('-disabled').bind('click', preventClickHandler); - } - } - ]; - - var scrumTaskBoardTourSteps = [ - { - 'next #content' : I18n.t('js.onboarding.steps.backlogs_task_board'), - 'showSkip' : false, - 'containerClass' : '-dark' - }, - { - 'click .toggler' : I18n.t('js.onboarding.steps.wp_toggler'), - 'showSkip' : false, - 'shape' : 'circle', - 'radius' : 20, - 'clickable' : true - }, - { - "click .wp-query-menu--item[data-category='default']": I18n.t('js.onboarding.steps.wp_query'), - 'showSkip' : false, - 'timeout' : 200, - 'margin' : 0, - 'clickable' : true - } - ]; - - var wpOnboardingTourSteps = [ - { - 'custom .wp-table--row' : I18n.t('js.onboarding.steps.wp_list'), - 'showSkip' : false, - 'margin' : 5, - 'clickable' : true, - onBeforeStart: function(){ - // Handle next step - $('.wp-table--row ').dblclick(function(e) { - if (!$(e.target).hasClass('wp-edit-field--display-field')) tutorialInstance.trigger('next'); - }); - $('.wp-table--cell-td.id a').click(function() { - tutorialInstance.trigger('next'); - }); - - // Disable clicks on the wp context menu links - $('.wp-table--details-link, .wp-table-context-menu-link, .wp-table--cell-span').addClass('-disabled').bind('click', preventClickHandler); - } - }, - { - 'next .work-packages-full-view--split-left' : I18n.t('js.onboarding.steps.wp_full_view'), - 'showSkip' : false, - 'containerClass' : '-dark' - }, - { - 'click .work-packages-list-view-button' : I18n.t('js.onboarding.steps.wp_back_button'), - 'showSkip' : false, - 'clickable' : true - }, - { - 'next .add-work-package' : I18n.t('js.onboarding.steps.wp_create_button'), - 'showSkip' : false, - 'shape' : 'circle' - }, - { - 'click .timeline-toolbar--button' : I18n.t('js.onboarding.steps.wp_timeline_button'), - 'showSkip' : false, - 'shape' : 'circle', - 'clickable' : true - }, - { - 'next .work-packages-tabletimeline--timeline-side' : I18n.t('js.onboarding.steps.wp_timeline'), - 'showSkip' : false, - 'containerClass' : '-dark' - }, - { - 'next .menu-item--help' : I18n.t('js.onboarding.steps.help_menu'), - 'shape' : 'circle', - "nextButton" : {text: I18n.t('js.onboarding.steps.got_it')}, - 'showSkip' : false - } - ]; - - // Start after the intro modal (language selection) - // This has to be changed once the project selection is implemented - if(url.searchParams.get("first_time_user")) { - currentTourPart = ''; - sessionStorage.setItem(storageKey, 'readyToStart'); - - // Start automatically when the language selection is closed - $('.op-modal--modal-close-button').click(function() { - initializeTour('startOverviewTour', '.widget-box--blocks--buttons .button'); - startTour(homescreenOnboardingTourSteps); - }); - } - - // ------------------------------- Tutorial Homescreen page ------------------------------- - if (currentTourPart === "readyToStart") { - initializeTour('startOverviewTour', '.widget-box--blocks--buttons .button'); - startTour(homescreenOnboardingTourSteps); - }; - - // ------------------------------- Tutorial Overview page ------------------------------- - if (currentTourPart === "startOverviewTour") { - if($('.backlogs-menu-item').length > 0) { - initializeTour('startBacklogsTour'); - startTour(scrumOverviewOnboardingTourSteps); - } else { - initializeTour('startWpTour'); - startTour(overviewOnboardingTourSteps); - } - }; - - // ------------------------------- Tutorial Backlogs page ------------------------------- - if (currentTourPart === "startBacklogsTour") { - initializeTour('startTaskBoardTour', ".backlog .menu a:not('.show_task_board')"); - startTour(scrumBacklogsTourSteps); - }; - - // ------------------------------- Tutorial Backlogs page ------------------------------- - if (currentTourPart === "startTaskBoardTour") { - initializeTour('startWpTour'); - startTour(scrumTaskBoardTourSteps); - }; - - // ------------------------------- Tutorial WP page ------------------------------- - if (currentTourPart === "startWpTour") { - initializeTour('wpFinished', '.wp-table--details-link, .wp-table-context-menu-link, .wp-table--cell-span'); - - // Wait for the WP table to be ready - var observer = new MutationObserver(function (mutations, observerInstance) { - if ($('.work-package--results-tbody')) { - observerInstance.disconnect(); // stop observing - - startTour(wpOnboardingTourSteps); - return; - } - }); - observer.observe($('.work-packages-split-view--tabletimeline-side')[0], { - childList: true, - subtree: true - }); - - } - - function initializeTour(storageValue, disabledElements) { - tutorialInstance = new EnjoyHint({ - onEnd: function () { - sessionStorage.setItem(storageKey, storageValue); - }, - onSkip: function () { - sessionStorage.setItem(storageKey, 'skipped'); - if (disabledElements) jQuery(disabledElements).removeClass('-disabled').unbind('click', preventClickHandler); - } - }); - - } - - function startTour(steps) { - tutorialInstance.set(steps); - tutorialInstance.run(); - } - }); -}(jQuery)); diff --git a/app/assets/javascripts/vendor/enjoyhint.js b/app/assets/javascripts/vendor/enjoyhint.js index 96b926a115..0d25a54280 100755 --- a/app/assets/javascripts/vendor/enjoyhint.js +++ b/app/assets/javascripts/vendor/enjoyhint.js @@ -1065,7 +1065,7 @@ var EnjoyHint; $('#enjoyhint_arrpw_line').remove(); var d = 'M' + x_from + ',' + y_from + ' Q' + control_point_x + ',' + control_point_y + ' ' + x_to + ',' + y_to; - that.$svg.append(makeSVG('path', {style: "fill:none; stroke:rgb(255,255,255); stroke-width:3", 'marker-end': "url(#arrowMarker)", d: d, id: 'enjoyhint_arrpw_line'})); + that.$svg.append(makeSVG('path', {style: "fill:none; stroke:rgb(255,255,255); stroke-width:3", 'marker-end': "url(" + location.href + "#arrowMarker)", d: d, id: 'enjoyhint_arrpw_line'})); that.enjoyhint.removeClass(that.cl.svg_transparent); }, that.options.animation_time / 2); diff --git a/app/assets/stylesheets/vendor/_enjoyhint.sass b/app/assets/stylesheets/vendor/_enjoyhint.sass index 781c050af0..519c21bf29 100755 --- a/app/assets/stylesheets/vendor/_enjoyhint.sass +++ b/app/assets/stylesheets/vendor/_enjoyhint.sass @@ -1,11 +1,3 @@ -@font-face - font-family: casino_handregular - src: url(Casino_Hand/casino_hand-webfont.eot) - src: url(Casino_Hand/casino_hand-webfont.eot?#iefix) format("embedded-opentype"), url(Casino_Hand/casino_hand-webfont.woff) format("woff"), url(Casino_Hand/casino_hand-webfont.ttf) format("truetype"), url(Casino_Hand/casino_hand-webfont.svg#casino_handregular) format("svg") - font-weight: 400 - font-style: normal - - @mixin onboarding-button-styles color: white border: 2px solid $alternative-color diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index aff4be4d47..dcb9b4f9bf 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -17,7 +17,6 @@ OpenProject::Application.configure do members_select_boxes.js my_page.js new_user.js - onboarding_tour.js project/responsible_attribute.js project/description_handling.js project/filters.js