Update Angular to v15 (#11778)

* Update angular to v15

Also bumps fullcalendar to v6-beta to be compatible

Bump fullcalendar to v6 final

* Fix accessing readOnly property that no longer exists

* Replace content projection

* Fix confusing duplicate variables scss, sass file

One can be imported by helpers, the other not
pull/11873/head
Oliver Günther 2 years ago committed by GitHub
parent cb60af9055
commit 39360ebde2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      frontend/angular.json
  2. 7303
      frontend/package-lock.json
  3. 53
      frontend/package.json
  4. 3
      frontend/src/app/features/calendar/op-work-packages-calendar.service.ts
  5. 18
      frontend/src/app/features/calendar/wp-calendar/wp-calendar.component.ts
  6. 123
      frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.html
  7. 31
      frontend/src/app/features/team-planner/team-planner/planner/team-planner.component.ts
  8. 2
      frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-highlighting.service.ts
  9. 2
      frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-order.service.ts
  10. 17
      frontend/src/app/shared/components/dynamic-forms/components/dynamic-inputs/formattable-textarea-input/components/formattable-control/formattable-control.component.ts
  11. 4
      frontend/src/app/shared/components/editor/components/ckeditor/ckeditor.types.ts
  12. 2
      frontend/src/app/shared/components/grids/openproject-grids.module.ts
  13. 2
      frontend/src/app/shared/components/modal/modal-banner/modal-banner.component.sass
  14. 6
      frontend/src/assets/sass/_helpers.sass
  15. 3
      frontend/src/global_styles/openproject/_variable_defaults.scss
  16. 4
      frontend/src/global_styles/openproject/_variables.sass
  17. 2
      frontend/src/styles.scss
  18. 7
      frontend/src/test.ts

@ -45,11 +45,6 @@
"node_modules/jquery-ui/themes/base/core.css",
"node_modules/jquery-ui/themes/base/datepicker.css",
"node_modules/jquery-ui/themes/base/dialog.css",
"node_modules/@fullcalendar/common/main.css",
"node_modules/@fullcalendar/daygrid/main.css",
"node_modules/@fullcalendar/timegrid/main.css",
"node_modules/@fullcalendar/timeline/main.css",
"node_modules/@fullcalendar/resource-timeline/main.css",
"node_modules/flatpickr/dist/flatpickr.min.css"
],
"stylePreprocessorOptions": {

File diff suppressed because it is too large Load Diff

@ -5,13 +5,13 @@
"version": "0.1.0",
"private": true,
"devDependencies": {
"@angular-devkit/build-angular": "^14.2.5",
"@angular-eslint/builder": "^13.2.1",
"@angular-devkit/build-angular": "^15.0.2",
"@angular-eslint/builder": "^15.1.0",
"@angular-eslint/eslint-plugin": "^13.2.1",
"@angular-eslint/eslint-plugin-template": "^13.2.1",
"@angular-eslint/schematics": "13.2.1",
"@angular-eslint/template-parser": "^13.2.1",
"@angular/language-service": "14.0.2",
"@angular/language-service": "15.0.2",
"@babel/core": "^7.18.5",
"@compodoc/compodoc": "^1.1.19",
"@html-eslint/eslint-plugin": "^0.13.1",
@ -47,7 +47,7 @@
"@typescript-eslint/eslint-plugin": "4.23.0",
"@typescript-eslint/parser": "4.23.0",
"babel-loader": "^8.2.5",
"browserslist": "^4.9.1",
"browserslist": "^4.8.7",
"codelyzer": "^6.0.0",
"css-loader": "^6.7.1",
"eslint": "^7.26.0",
@ -79,41 +79,42 @@
"style-loader": "^3.3.1",
"theo": "^8.1.5",
"ts-node": "~8.3.0",
"typescript": "~4.7.4",
"typescript": "4.8",
"webpack-bundle-analyzer": "^4.4.2"
},
"dependencies": {
"@angular/animations": "^14.2.5",
"@angular/cdk": "^14.2.4",
"@angular/cli": "^14.2.5",
"@angular/common": "^14.2.5",
"@angular/compiler": "^14.2.5",
"@angular/compiler-cli": "^14.2.5",
"@angular/core": "^14.2.5",
"@angular/forms": "^14.2.5",
"@angular/platform-browser": "^14.2.5",
"@angular/platform-browser-dynamic": "^14.2.5",
"@angular/router": "^14.2.5",
"@angular/animations": "^15.0.2",
"@angular/cdk": "^15.0.1",
"@angular/cli": "^15.0.2",
"@angular/common": "^15.0.2",
"@angular/compiler": "^15.0.2",
"@angular/compiler-cli": "^15.0.2",
"@angular/core": "^15.0.2",
"@angular/forms": "^15.0.2",
"@angular/platform-browser": "^15.0.2",
"@angular/platform-browser-dynamic": "^15.0.2",
"@angular/router": "^15.0.2",
"@appsignal/javascript": "^1.3.23",
"@appsignal/plugin-breadcrumbs-console": "^1.1.24",
"@appsignal/plugin-breadcrumbs-network": "^1.1.21",
"@datorama/akita": "^6.2.0",
"@fullcalendar/angular": "5.10.1",
"@fullcalendar/common": "^5.10.1",
"@fullcalendar/core": "5.10.1",
"@fullcalendar/daygrid": "5.10.1",
"@fullcalendar/interaction": "5.10.1",
"@fullcalendar/resource-common": "^5.10.1",
"@fullcalendar/resource-timeline": "5.10.1",
"@fullcalendar/timegrid": "5.10.1",
"@fullcalendar/angular": "^6.0.2",
"@fullcalendar/common": "^6.0.0-beta.1",
"@fullcalendar/core": "^6.0.2",
"@fullcalendar/daygrid": "^6.0.2",
"@fullcalendar/interaction": "^6.0.2",
"@fullcalendar/resource": "^6.0.2",
"@fullcalendar/resource-common": "^6.0.0-beta.1",
"@fullcalendar/resource-timeline": "^6.0.2",
"@fullcalendar/timegrid": "^6.0.2",
"@kolkov/ngx-gallery": "^1.0.11",
"@ng-select/ng-option-highlight": "0.0.5",
"@ng-select/ng-select": "^4.0.4",
"@ngneat/content-loader": "^6.1.0",
"@ngx-formly/core": "^5.10.19",
"@uirouter/angular": "^9.1.0",
"@uirouter/angular": "^10.0.0",
"@uirouter/core": "^6.0.8",
"@uirouter/rx": "^0.6.5",
"@uirouter/rx": "^1.0.0",
"@w11k/ngx-componentdestroyed": "^5.0.2",
"@xeokit/xeokit-bim-viewer": "2.3.10",
"autoprefixer": "^9.6.1",

@ -409,7 +409,8 @@ export class OpWorkPackagesCalendarService extends UntilDestroyedMixin {
'.',
{
cdate: this.timezoneService.formattedISODate(dates.view.currentStart),
cview: dates.view.type,
// v6.beta3 fails to have type on the ViewAPI
cview: (dates.view as unknown as { type:string }).type,
},
{
custom: { notify: false },

@ -37,10 +37,12 @@ import {
import {
CalendarOptions,
DateSelectArg,
EventClickArg,
EventDropArg,
EventInput,
ToolbarInput,
} from '@fullcalendar/core';
import { EventClickArg, FullCalendarComponent, ToolbarInput } from '@fullcalendar/angular';
import { FullCalendarComponent } from '@fullcalendar/angular';
import dayGridPlugin from '@fullcalendar/daygrid';
import * as moment from 'moment';
import { Subject } from 'rxjs';
@ -50,9 +52,7 @@ import { States } from 'core-app/core/states/states.service';
import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space';
import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource';
import { WorkPackageCollectionResource } from 'core-app/features/hal/resources/wp-collection-resource';
import {
WorkPackageViewFiltersService,
} from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service';
import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service';
import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service';
import { StateService } from '@uirouter/core';
import { I18nService } from 'core-app/core/i18n/i18n.service';
@ -62,10 +62,12 @@ import { ConfigurationService } from 'core-app/core/config/configuration.service
import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin';
import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service';
import { CurrentProjectService } from 'core-app/core/current-project/current-project.service';
import interactionPlugin, { EventDragStartArg, EventDragStopArg, EventResizeDoneArg } from '@fullcalendar/interaction';
import {
HalResourceEditingService,
} from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service';
import interactionPlugin, {
EventDragStartArg,
EventDragStopArg,
EventResizeDoneArg,
} from '@fullcalendar/interaction';
import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service';
import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service';
import { splitViewRoute } from 'core-app/features/work-packages/routing/split-view-routes.helper';
import {

@ -55,7 +55,68 @@
[options]="calendarOptions"
class="op-team-planner--calendar fc-scroller"
[ngClass]="{'op-team-planner--calendar_empty': (isEmpty$ | async)}"
></full-calendar>
>
<ng-template #resourceLabelContent let-resource="$implicit.resource">
<div
*ngIf="resource && resource.extendedProps.principal"
class="tp-assignee"
>
<op-principal
[principal]="resource.extendedProps.principal"
class="tp-assignee--principal op-principal_wrapped"
[hideName]="isMobile"
></op-principal>
<button
type="button"
class="tp-assignee--remove"
(click)="removeAssignee(resource.id)"
[attr.aria-label]="text.remove_assignee"
[attr.data-qa-remove-assignee]="resource.extendedProps.principal.id"
>
<op-icon icon-classes="icon-remove"></op-icon>
</button>
</div>
<op-principal-loading-skeleton
*ngIf="resource && !resource.extendedProps.principal && resource.id.startsWith('skeleton')"
></op-principal-loading-skeleton>
<op-tp-add-assignee
*ngIf="resource && !resource.extendedProps.principal && !resource.id.startsWith('skeleton')"
(selectAssignee)="addAssignee($event)"
[alreadySelected]="principalIds$ | async"
></op-tp-add-assignee>
</ng-template>
<ng-template #eventContent let-event="$implicit.event">
<op-wp-loading-skeleton
[viewBox]="event.extendedProps.viewBox"
class="op-team-planner--wp-loading-skeleton"
*ngIf="event.source && event.source.id === 'skeleton'"
></op-wp-loading-skeleton>
<wp-single-card
*ngIf="(!event.source || event.source.id === 'work_packages') && event.extendedProps.workPackage as wp"
[workPackage]="wp"
[selectedWhenOpen]="true"
[orientation]="'horizontal'"
[highlightingMode]="'type'"
[showInfoButton]="true"
[disabledInfo]="showDisabledText(wp)"
[isClosed]="isStatusClosed(wp)"
[showAsGhost]="shouldShowAsGhost(wp.id, (globalDraggingItem$ | async))"
[showAsInlineCard]="true"
[showStartDate]="!isWpStartDateInCurrentView(wp)"
[showEndDate]="!isWpEndDateInCurrentView(wp)"
(stateLinkClicked)="openStateLink($event)"
(cardClicked)="workPackagesCalendar.onCardClicked($event)"
(cardDblClicked)="workPackagesCalendar.onCardDblClicked($event)"
(cardContextMenu)="workPackagesCalendar.showEventContextMenu($event)"
></wp-single-card>
</ng-template>
</full-calendar>
<div
[textContent]="workPackagesCalendar.tooManyResultsText"
@ -78,66 +139,6 @@
</div>
</ng-container>
<ng-template #resourceContent let-resource="resource">
<div
*ngIf="resource && resource.extendedProps.principal"
class="tp-assignee"
>
<op-principal
[principal]="resource.extendedProps.principal"
class="tp-assignee--principal op-principal_wrapped"
[hideName]="isMobile"
></op-principal>
<button
type="button"
class="tp-assignee--remove"
(click)="removeAssignee(resource.id)"
[attr.aria-label]="text.remove_assignee"
[attr.data-qa-remove-assignee]="resource.extendedProps.principal.id"
>
<op-icon icon-classes="icon-remove"></op-icon>
</button>
</div>
<op-principal-loading-skeleton
*ngIf="resource && !resource.extendedProps.principal && resource.id.startsWith('skeleton')"
></op-principal-loading-skeleton>
<op-tp-add-assignee
*ngIf="resource && !resource.extendedProps.principal && !resource.id.startsWith('skeleton')"
(selectAssignee)="addAssignee($event)"
[alreadySelected]="principalIds$ | async"
></op-tp-add-assignee>
</ng-template>
<ng-template #eventContent let-event="event">
<op-wp-loading-skeleton
[viewBox]="event.extendedProps.viewBox"
class="op-team-planner--wp-loading-skeleton"
*ngIf="event.source && event.source.id === 'skeleton'"
></op-wp-loading-skeleton>
<wp-single-card
*ngIf="(!event.source || event.source.id === 'work_packages') && event.extendedProps.workPackage as wp"
[workPackage]="wp"
[selectedWhenOpen]="true"
[orientation]="'horizontal'"
[highlightingMode]="'type'"
[showInfoButton]="true"
[disabledInfo]="showDisabledText(wp)"
[isClosed]="isStatusClosed(wp)"
[showAsGhost]="shouldShowAsGhost(wp.id, (globalDraggingItem$ | async))"
[showAsInlineCard]="true"
[showStartDate]="!isWpStartDateInCurrentView(wp)"
[showEndDate]="!isWpEndDateInCurrentView(wp)"
(stateLinkClicked)="openStateLink($event)"
(cardClicked)="workPackagesCalendar.onCardClicked($event)"
(cardDblClicked)="workPackagesCalendar.onCardDblClicked($event)"
(cardContextMenu)="workPackagesCalendar.showEventContextMenu($event)"
></wp-single-card>
</ng-template>
<div class="op-team-planner--footer" data-qa-selector="op-team-planner-footer">
<div
class="op-team-planner--add-assignee"

@ -44,8 +44,6 @@ import {
EventContentArg,
EventDropArg,
EventInput,
RawOptionsFromRefiners,
ViewOptionRefiners,
} from '@fullcalendar/core';
import {
BehaviorSubject,
@ -118,8 +116,11 @@ import { LoadingIndicatorService } from 'core-app/core/loading-indicator/loading
import { OpWorkPackagesCalendarService } from 'core-app/features/calendar/op-work-packages-calendar.service';
import { DeviceService } from 'core-app/core/browser/device.service';
import { WeekdayService } from 'core-app/core/days/weekday.service';
import { RawOptionsFromRefiners } from '@fullcalendar/core/internal';
import { ViewOptionRefiners } from '@fullcalendar/common';
import { ResourceApi } from '@fullcalendar/resource';
export type TeamPlannerViewOptionKey = 'resourceTimelineWorkWeek' | 'resourceTimelineWeek' | 'resourceTimelineTwoWeeks';
export type TeamPlannerViewOptionKey = 'resourceTimelineWorkWeek'|'resourceTimelineWeek'|'resourceTimelineTwoWeeks';
export type TeamPlannerViewOptions = { [K in TeamPlannerViewOptionKey]:RawOptionsFromRefiners<Required<ViewOptionRefiners>> };
@Component({
@ -139,8 +140,6 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit,
this.calendar.resizeObserver(v);
}
@ViewChild('eventContent') eventContent:TemplateRef<unknown>;
@ViewChild('resourceContent') resourceContent:TemplateRef<unknown>;
@ViewChild('assigneeAutocompleter') assigneeAutocompleter:TemplateRef<unknown>;
@ -430,7 +429,7 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit,
const api = this.ucCalendar.getApi();
// This also removes the skeleton resources that are rendered initially
api.getResources().forEach((resource) => resource.remove());
api.getResources().forEach((resource:ResourceApi) => resource.remove());
principals.forEach((principal) => {
const id = principal._links.self.href;
@ -522,8 +521,6 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit,
resources: skeletonResources,
resourceAreaWidth: this.isMobile ? '60px' : '180px',
select: this.handleDateClicked.bind(this) as unknown,
resourceLabelContent: (data:ResourceLabelContentArg) => this.renderTemplate(this.resourceContent, data.resource.id, data),
resourceLabelWillUnmount: (data:ResourceLabelContentArg) => this.unrenderTemplate(data.resource.id),
// DnD configuration
editable: true,
droppable: true,
@ -582,24 +579,6 @@ export class TeamPlannerComponent extends UntilDestroyedMixin implements OnInit,
await this.updateEvent(dropInfo, true);
this.actions$.dispatch(teamPlannerEventAdded({ workPackage: wp.id as string }));
},
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
eventContent: (data:EventContentArg):{ domNodes:unknown[] }|undefined => {
// Let FC handle the background events
if (data.event.source?.id === 'background') {
return undefined;
}
return this.renderTemplate(this.eventContent, this.eventId(data), data);
},
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
eventWillUnmount: (data:EventContentArg) => {
// Nothing to do for background events
if (data.event.source?.id === 'background') {
return;
}
this.unrenderTemplate(this.eventId(data));
},
} as CalendarOptions),
);
});

@ -33,7 +33,7 @@ export class WorkPackageViewHighlightingService extends WorkPackageQueryStateSer
}
// 2. Is selected attributes === undefined or empty Array?
if (this.current.selectedAttributes === undefined || this.current.selectedAttributes === []) {
if (this.current.selectedAttributes?.length === 0) {
return true;
}

@ -169,7 +169,7 @@ export class WorkPackageViewOrderService extends WorkPackageQueryStateService<Qu
const { value } = this.positions;
// Remove empty or stale values given we can reload them
if ((value === {} || this.positions.isValueOlderThan(60000))) {
if ((_.isEmpty(value) || this.positions.isValueOlderThan(60000))) {
this.positions.clear('Clearing old positions value');
}

@ -77,7 +77,8 @@ export class FormattableControlComponent implements ControlValueAccessor, OnInit
setDisabledState(disabled:boolean):void {
this.disabled = disabled;
this.editor.ckEditorInstance.isReadOnly = disabled;
this.syncCKEditorReadonlyMode();
}
onContentChange(value:string) {
@ -87,7 +88,21 @@ export class FormattableControlComponent implements ControlValueAccessor, OnInit
this.onChange(valueToEmit);
}
syncCKEditorReadonlyMode() {
const { ckEditorInstance } = this.editor;
if (!ckEditorInstance) {
return;
}
if (this.disabled) {
ckEditorInstance.enableReadOnlyMode('formattable-control');
} else {
ckEditorInstance.disableReadOnlyMode('formattable-control');
}
}
onCkeditorSetup(_editor:ICKEditorInstance) {
this.syncCKEditorReadonlyMode();
this.editor.ckEditorInstance.ui.focusTracker.on(
'change:isFocused',
(evt:unknown, name:unknown, isFocused:unknown) => {

@ -11,6 +11,9 @@ export interface ICKEditorInstance {
destroy():void;
enableReadOnlyMode(lockId:string):void;
disableReadOnlyMode(lockId:string):void;
on(event:string, callback:() => unknown):void;
model:any;
@ -18,7 +21,6 @@ export interface ICKEditorInstance {
config:any;
ui:any;
element:HTMLElement;
isReadOnly:boolean;
}
export interface ICKEditorStatic {

@ -32,7 +32,6 @@ import { HookService } from 'core-app/features/plugins/hook-service';
import { OPSharedModule } from 'core-app/shared/shared.module';
import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module';
import { OpenprojectCalendarModule } from 'core-app/features/calendar/openproject-calendar.module';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { DragDropModule } from '@angular/cdk/drag-drop';
import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module';
@ -70,7 +69,6 @@ import { TimeEntriesCurrentUserConfigurationModalComponent } from './widgets/tim
@NgModule({
imports: [
BrowserModule,
FormsModule,
DragDropModule,

@ -1,4 +1,4 @@
@import '~app/spot/styles/sass/variables'
@import 'helpers'
.op-modal-banner
display: grid

@ -3,7 +3,7 @@
* as all CSS output will be duplicated in EVERY component
* importing these helpers!
*/
@import "~global_styles/openproject/_mixins"
@import "~global_styles/openproject/_variables"
@import "~global_styles/content/drag_and_drop"
@import "../../global_styles/openproject/_mixins"
@import "../../global_styles/openproject/_variables"
@import "../../global_styles/content/drag_and_drop"
@import "../../app/spot/styles/sass/variables"

@ -226,6 +226,3 @@
--hamburger-width: 50px;
}
// SCSS variables needed for foundation, but that are set in css variables now only
// which cannot be set to sass unfortunately
$secondary-color: #bfbfbf;

@ -1,2 +1,6 @@
// SCSS variables needed for foundation, but that are set in css variables now only
// which cannot be set to sass unfortunately
$secondary-color: #bfbfbf
// A selector that checks whether we are running in a test environment
$spec-active-selector: '.env-test'

@ -2,7 +2,7 @@
@import "~@ng-select/ng-select/themes/default.theme.css";
// Variables
@import "global_styles/openproject/_variables.scss";
@import "global_styles/openproject/_variable_defaults.scss";
// Foundation variables
@import "global_styles/openproject/_settings.scss";

@ -17,8 +17,6 @@ import { I18nShim } from './test/i18n-shim';
(window as any).global = window;
require('expose-loader?_!lodash');
declare const require:any;
declare global {
export interface Window {
I18n:GlobalI18n;
@ -36,8 +34,3 @@ getTestBed().initTestEnvironment(
teardown: { destroyAfterEach: false },
},
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

Loading…
Cancel
Save