Skip to content

Commit

Permalink
Merge pull request #15895 from opf/implementation/55178-replace-sidem…
Browse files Browse the repository at this point in the history
…enu-of-team-planner-page-with-rails-component

[55178] Replace sidemenu of team planner and boards with Rails component
  • Loading branch information
oliverguenther authored Jun 24, 2024
2 parents db7a65e + f94a6c8 commit 8d14243
Show file tree
Hide file tree
Showing 42 changed files with 477 additions and 337 deletions.
11 changes: 8 additions & 3 deletions app/components/open_project/common/submenu_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<% top_level_sidebar_menu_items.first.children.each do |menu_item| %>
<li class="op-sidemenu--item" data-filter--filter-list-target="searchItem">
<% selected = menu_item.selected ? 'selected' : '' %>
<a class="op-sidemenu--item-action <%= selected %>" href="<%= menu_item.href %>">
<a class="op-sidemenu--item-action <%= selected %>" href="<%= menu_item.href %>" data-test-selector="op-sidemenu--item-action">
<span class="op-sidemenu--item-title"><%= menu_item.title %></span>
</a>
</li>
Expand Down Expand Up @@ -60,7 +60,7 @@
<% menu_item.children.each do |child_item| %>
<li class="op-sidemenu--item" data-filter--filter-list-target="searchItem">
<% selected = child_item.selected ? 'selected' : '' %>
<a class="op-sidemenu--item-action <%= selected %>" href="<%= child_item.href %>">
<a class="op-sidemenu--item-action <%= selected %>" href="<%= child_item.href %>" data-test-selector="op-sidemenu--item-action">
<span class="op-sidemenu--item-title"><%= child_item.title %></span>
</a>
</li>
Expand All @@ -75,9 +75,14 @@
<%= render Primer::Beta::Button.new(scheme: :primary,
tag: :a,
href: @create_btn_options[:href],
test_selector: "#{@create_btn_options[:module_key]}--create-button",
classes: "op-sidebar--footer-action") do |button|
button.with_leading_visual_icon(icon: "plus")
@create_btn_options[:text]
if @create_btn_options[:btn_text].present?
@create_btn_options[:btn_text]
else
I18n.t("label_#{@create_btn_options[:module_key]}")
end
end %>
</div>
<% end %>
Expand Down
25 changes: 25 additions & 0 deletions frontend/src/app/core/main-menu/submenu.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { TurboElement } from 'core-typings/turbo';
import { StateService } from '@uirouter/core';

@Injectable({ providedIn: 'root' })
export class SubmenuService {
constructor(protected $state:StateService) {}

reloadSubmenu():void {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
const menuIdentifier:string|undefined = this.$state.current.data.sideMenuOptions?.sidemenuId;

if (menuIdentifier) {
const menu = (document.getElementById(menuIdentifier) as HTMLElement&TurboElement);
const currentSrc = menu.getAttribute('src');

if (currentSrc && menu) {
const frameUrl = new URL(currentSrc);

// Override the frame src to enforce a reload
menu.setAttribute('src', frameUrl.href);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
CustomDateActionAdminComponent,
customDateActionAdminSelector,
} from 'core-app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component';
import { BoardsMenuComponent, boardsMenuSelector } from 'core-app/features/boards/boards-sidebar/boards-menu.component';
import {
GlobalSearchWorkPackagesEntryComponent,
globalSearchWorkPackagesSelectorEntry,
Expand Down Expand Up @@ -143,10 +142,6 @@ import {
opInAppNotificationBellSelector,
} from 'core-app/features/in-app-notifications/bell/in-app-notification-bell.component';
import { IanMenuComponent, ianMenuSelector } from 'core-app/features/in-app-notifications/center/menu/menu.component';
import {
opTeamPlannerSidemenuSelector,
TeamPlannerSidemenuComponent,
} from 'core-app/features/team-planner/team-planner/sidemenu/team-planner-sidemenu.component';
import {
OpModalOverlayComponent,
opModalOverlaySelector,
Expand Down Expand Up @@ -187,7 +182,6 @@ export const globalDynamicComponents:OptionalBootstrapDefinition[] = [
{ selector: globalSearchWorkPackagesSelector, cls: GlobalSearchWorkPackagesComponent },
{ selector: homescreenNewFeaturesBlockSelector, cls: HomescreenNewFeaturesBlockComponent },
{ selector: customDateActionAdminSelector, cls: CustomDateActionAdminComponent },
{ selector: boardsMenuSelector, cls: BoardsMenuComponent },
{ selector: globalSearchWorkPackagesSelectorEntry, cls: GlobalSearchWorkPackagesEntryComponent },
{ selector: toastsContainerSelector, cls: ToastsContainerComponent },
{ selector: sidemenuSelector, cls: OpSidemenuComponent },
Expand All @@ -213,7 +207,6 @@ export const globalDynamicComponents:OptionalBootstrapDefinition[] = [
{ selector: headerProjectSelectSelector, cls: OpHeaderProjectSelectComponent },
{ selector: wpOverviewGraphSelector, cls: WorkPackageOverviewGraphComponent },
{ selector: opViewSelectSelector, cls: ViewSelectComponent },
{ selector: opTeamPlannerSidemenuSelector, cls: TeamPlannerSidemenuComponent },
{ selector: triggerActionsEntryComponentSelector, cls: TriggerActionsEntryComponent, embeddable: true },
{ selector: editableQueryPropsSelector, cls: EditableQueryPropsComponent },
{ selector: backupSelector, cls: BackupComponent },
Expand Down
6 changes: 1 addition & 5 deletions frontend/src/app/core/setup/globals/onboarding/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
export const onboardingTourStorageKey = 'openProject-onboardingTour';
export type OnboardingTourNames = 'prepareBacklogs'|'backlogs'|'taskboard'|'homescreen'|'workPackages'|'main';

export enum ProjectName {
demo = 'demo',
}
export type OnboardingTourNames = 'homescreen'|'workPackages'|'gantt'|'final'|'boards'|'teamPlanner';

function matchingFilter(list:NodeListOf<HTMLElement>, filterFunction:(match:HTMLElement) => boolean):HTMLElement|null {
for (let i = 0; i < list.length; i++) {
Expand Down
79 changes: 61 additions & 18 deletions frontend/src/app/core/setup/globals/onboarding/onboarding_tour.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ import { wpOnboardingTourSteps } from 'core-app/core/setup/globals/onboarding/to
import {
OnboardingTourNames,
onboardingTourStorageKey,
ProjectName,
waitForElement,
} from 'core-app/core/setup/globals/onboarding/helpers';
import { boardTourSteps } from 'core-app/core/setup/globals/onboarding/tours/boards_tour';
import {
boardTourSteps,
navigateToBoardStep,
} from 'core-app/core/setup/globals/onboarding/tours/boards_tour';
import { menuTourSteps } from 'core-app/core/setup/globals/onboarding/tours/menu_tour';
import { homescreenOnboardingTourSteps } from 'core-app/core/setup/globals/onboarding/tours/homescreen_tour';
import { teamPlannerTourSteps } from 'core-app/core/setup/globals/onboarding/tours/team_planners_tour';
import {
navigateToTeamPlannerStep,
teamPlannerTourSteps,
} from 'core-app/core/setup/globals/onboarding/tours/team_planners_tour';
import { ganttOnboardingTourSteps } from 'core-app/core/setup/globals/onboarding/tours/gantt_tour';

require('core-vendor/enjoyhint');
Expand Down Expand Up @@ -36,9 +41,11 @@ export type OnboardingStep = {
};

function initializeTour(storageValue:string) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-assignment
window.onboardingTourInstance = new window.EnjoyHint({
onStart() {
jQuery('#content-wrapper, #menu-sidebar').addClass('-hidden-overflow');
sessionStorage.setItem(onboardingTourStorageKey, storageValue);
},
onEnd() {
sessionStorage.setItem(onboardingTourStorageKey, storageValue);
Expand All @@ -56,8 +63,8 @@ function startTour(steps:OnboardingStep[]) {
window.onboardingTourInstance.run();
}

function moduleVisible(name:string):boolean {
return document.getElementsByClassName(`${name}-menu-item`).length > 0;
export function moduleVisible(name:string):boolean {
return document.querySelector(`#menu-sidebar .${name}-menu-item`) !== null;
}

function workPackageTour() {
Expand All @@ -68,39 +75,69 @@ function workPackageTour() {
startTour(steps);
});
}
function mainTour(project:ProjectName = ProjectName.demo) {
initializeTour('mainTourFinished');

function ganttTour() {
initializeTour('ganttTourFinished');

const boardsDemoDataAvailable = jQuery('meta[name=boards_demo_data_available]').attr('content') === 'true';
const teamPlannerDemoDataAvailable = jQuery('meta[name=demo_view_of_type_team_planner_seeded]').attr('content') === 'true';
const eeTokenAvailable = !jQuery('body').hasClass('ee-banners-visible');

waitForElement('.work-package--results-tbody', '#content', () => {
let steps:OnboardingStep[] = ganttOnboardingTourSteps();

// Check for EE edition
if (eeTokenAvailable) {
// ... and available seed data of boards.
// Then add boards to the tour, otherwise skip it.
if (boardsDemoDataAvailable && moduleVisible('boards')) {
steps = steps.concat(boardTourSteps('enterprise', project));
}

// ... same for team planners
if (teamPlannerDemoDataAvailable && moduleVisible('team-planner-view')) {
steps = steps.concat(teamPlannerTourSteps());
steps = steps.concat(navigateToBoardStep('enterprise'));
} else if (teamPlannerDemoDataAvailable && moduleVisible('team-planner-view')) {
steps = steps.concat(navigateToTeamPlannerStep());
} else {
steps = steps.concat(menuTourSteps());
}
} else if (boardsDemoDataAvailable && moduleVisible('boards')) {
steps = steps.concat(boardTourSteps('basic', project));
steps = steps.concat(navigateToBoardStep('basic'));
} else {
steps = steps.concat(menuTourSteps());
}

startTour(steps);
});
}

function boardTour() {
initializeTour('boardsTourFinished');

const teamPlannerDemoDataAvailable = jQuery('meta[name=demo_view_of_type_team_planner_seeded]').attr('content') === 'true';
const eeTokenAvailable = !jQuery('body').hasClass('ee-banners-visible');

waitForElement('wp-single-card', '#content', () => {
let steps:OnboardingStep[] = eeTokenAvailable ? boardTourSteps('enterprise') : boardTourSteps('basic');

// Available seed data of team planner.
// Then add Team planner to the tour, otherwise skip it.
if (teamPlannerDemoDataAvailable && moduleVisible('team-planner-view')) {
steps = steps.concat(navigateToTeamPlannerStep());
} else {
steps = steps.concat(menuTourSteps());
}

startTour(steps);
});
}

function teamPlannerTour() {
initializeTour('teamPlannerTourFinished');
waitForElement('full-calendar', '#content', () => {
let steps:OnboardingStep[] = teamPlannerTourSteps();
steps = steps.concat(menuTourSteps());

startTour(steps);
});
}

export function start(name:OnboardingTourNames, project?:ProjectName):void {
export function start(name:OnboardingTourNames):void {
switch (name) {
case 'homescreen':
initializeTour('startProjectTour');
Expand All @@ -109,8 +146,14 @@ export function start(name:OnboardingTourNames, project?:ProjectName):void {
case 'workPackages':
workPackageTour();
break;
case 'main':
mainTour(project);
case 'gantt':
ganttTour();
break;
case 'boards':
boardTour();
break;
case 'teamPlanner':
teamPlannerTour();
break;
default:
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
import {
OnboardingTourNames,
onboardingTourStorageKey,
ProjectName,
waitForElement,
} from 'core-app/core/setup/globals/onboarding/helpers';
import { debugLog } from 'core-app/shared/helpers/debug_output';

async function triggerTour(name:OnboardingTourNames, project?:ProjectName):Promise<void> {
async function triggerTour(name:OnboardingTourNames):Promise<void> {
debugLog(`Loading and triggering onboarding tour ${name}`);
await import(/* webpackChunkName: "onboarding-tour" */ './onboarding_tour').then((tour) => {
tour.start(name, project);
tour.start(name);
});
}

Expand Down Expand Up @@ -67,12 +66,40 @@ export function detectOnboardingTour():void {

// ------------------------------- Tutorial WP page -------------------------------
if (url.searchParams.get('start_onboarding_tour')) {
void triggerTour('workPackages', ProjectName.demo);
void triggerTour('workPackages');
}

// ------------------------------- Tutorial Main part (starting from the Gantt module) -------------------------------
// ------------------------------- Tutorial Gantt module -------------------------------
if (currentTourPart === 'wpTourFinished') {
void triggerTour('main', ProjectName.demo);
void triggerTour('gantt');
return;
}

// ------------------------------- Tutorial Boards module -------------------------------
if (currentTourPart === 'ganttTourFinished') {
if (url.pathname.includes('boards')) {
void triggerTour('boards');
return;
}
if (url.pathname.includes('team_planner')) {
void triggerTour('teamPlanner');
return;
}
void triggerTour('final');
}

// ------------------------------- Tutorial TeamPlanner module -------------------------------
if (currentTourPart === 'boardsTourFinished') {
if (url.pathname.includes('team_planner')) {
void triggerTour('teamPlanner');
return;
}
void triggerTour('final');
}

// ------------------------------- Fina tutorial -------------------------------
if (currentTourPart === 'teamPlannerTourFinished') {
void triggerTour('final');
}
}
}
48 changes: 25 additions & 23 deletions frontend/src/app/core/setup/globals/onboarding/tours/boards_tour.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,12 @@
import {
ProjectName,
waitForElement,
} from 'core-app/core/setup/globals/onboarding/helpers';
import { OnboardingStep } from 'core-app/core/setup/globals/onboarding/onboarding_tour';

export function boardTourSteps(edition:'basic'|'enterprise', project:ProjectName):OnboardingStep[] {
let boardName:string;
if (edition === 'basic') {
boardName = project === ProjectName.demo ? 'Basic board' : 'Task board';
} else {
boardName = 'Kanban';
}

export function boardTourSteps(edition:'basic'|'enterprise'):OnboardingStep[] {
const listExplanation = edition === 'basic' ? 'basic' : 'kanban';

return [
{
'next #boards-wrapper>.boards-menu-item': I18n.t('js.onboarding.steps.boards.overview'),
showSkip: false,
nextButton: { text: I18n.t('js.onboarding.buttons.next') },
onNext() {
jQuery('#boards-wrapper>.boards-menu-item ~ .toggler')[0].click();
waitForElement(
'.op-sidemenu--item-action',
'#main-menu',
(match) => match.click(),
(match) => !!match.textContent?.includes(boardName),
);
},
},
{
'next [data-tour-selector="op-board-list"]': I18n.t(`js.onboarding.steps.boards.lists_${listExplanation}`),
showSkip: false,
Expand Down Expand Up @@ -57,3 +35,27 @@ export function boardTourSteps(edition:'basic'|'enterprise', project:ProjectName
},
];
}

export function navigateToBoardStep(edition:'basic'|'enterprise'):OnboardingStep {
let boardName:string;
if (edition === 'basic') {
boardName = 'Basic board';
} else {
boardName = 'Kanban';
}

return {
'next #boards-wrapper>.boards-menu-item': I18n.t('js.onboarding.steps.boards.overview'),
showSkip: false,
nextButton: { text: I18n.t('js.onboarding.buttons.next') },
onNext() {
jQuery('#boards-wrapper>.boards-menu-item ~ .toggler')[0].click();
waitForElement(
'.op-sidemenu--item-action',
'#main-menu',
(match) => match.click(),
(match) => !!match.textContent?.includes(boardName),
);
},
};
}
Loading

0 comments on commit 8d14243

Please sign in to comment.