From 49e04914284354ec4062aeafe5d8d5e856e839e2 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Mon, 14 Nov 2022 17:52:47 +0100 Subject: [PATCH] MOBILE-4176 grades: Fix 4.1 layout Fixes some breaking changes introduced in MDL-75513. The fixes included here are not exhaustive but should take care of the basic scenarios covered by e2e tests. Subsequent fixes will be provided to handle other edge-cases. --- .../features/grades/pages/course/course.html | 43 +++++----- .../grades/pages/course/course.page.ts | 17 +++- .../features/grades/pages/course/course.scss | 8 ++ .../features/grades/services/grades-helper.ts | 81 +++++++++++++++---- 4 files changed, 114 insertions(+), 35 deletions(-) diff --git a/src/core/features/grades/pages/course/course.html b/src/core/features/grades/pages/course/course.html index bf4835c89..3919f3926 100644 --- a/src/core/features/grades/pages/course/course.html +++ b/src/core/features/grades/pages/course/course.html @@ -27,13 +27,18 @@ + + + + [class.core-grades-grade-clickable]="row.expandable && showSummary" [id]="'grade-'+row.id" + *ngIf="useLegacyLayout || row.itemtype !== 'leader'"> - + diff --git a/src/core/features/grades/pages/course/course.page.ts b/src/core/features/grades/pages/course/course.page.ts index f3d57d541..2f85483de 100644 --- a/src/core/features/grades/pages/course/course.page.ts +++ b/src/core/features/grades/pages/course/course.page.ts @@ -54,6 +54,7 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { rows?: CoreGradesFormattedTableRow[]; totalColumnsSpan?: number; withinSplitView?: boolean; + useLegacyLayout?: boolean; // Whether to use the layout before 4.1. protected fetchSuccess = false; @@ -68,6 +69,7 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { this.expandLabel = Translate.instant('core.expand'); this.collapseLabel = Translate.instant('core.collapse'); + this.useLegacyLayout = !CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('4.1'); if (route.snapshot.data.swipeEnabled ?? true) { const source = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(CoreGradesCoursesSource, []); @@ -133,11 +135,22 @@ export class CoreGradesCoursePage implements AfterViewInit, OnDestroy { row.expanded = expand ?? !row.expanded; - let colspan: number = this.columns.length + (row.colspan ?? 0) - 1; + let colspan: number = this.columns.length + (row.colspan ?? 0); + + if (this.useLegacyLayout) { + colspan--; + } + for (let i = this.rows.indexOf(row) - 1; i >= 0; i--) { const previousRow = this.rows[i]; - if (previousRow.expandable || !previousRow.colspan || !previousRow.rowspan || previousRow.colspan !== colspan) { + if ( + !previousRow.rowspan || + !previousRow.colspan || + previousRow.colspan !== colspan || + (!this.useLegacyLayout && previousRow.itemtype !== 'leader') || + (this.useLegacyLayout && previousRow.expandable) + ) { continue; } diff --git a/src/core/features/grades/pages/course/course.scss b/src/core/features/grades/pages/course/course.scss index 5358730a6..096eb096d 100644 --- a/src/core/features/grades/pages/course/course.scss +++ b/src/core/features/grades/pages/course/course.scss @@ -124,6 +124,14 @@ } } + .core-grades-table-leader { + width: 0; + } + + .ion-no-border { + border: 0 !important; + } + .dimmed_text, .hidden { opacity: .7; diff --git a/src/core/features/grades/services/grades-helper.ts b/src/core/features/grades/services/grades-helper.ts index f417a20a3..720ae5c45 100644 --- a/src/core/features/grades/services/grades-helper.ts +++ b/src/core/features/grades/services/grades-helper.ts @@ -25,6 +25,7 @@ import { CoreGradesTable, CoreGradesTableColumn, CoreGradesTableItemNameColumn, + CoreGradesTableLeaderColumn, CoreGradesTableRow, } from '@features/grades/services/grades'; import { CoreTextUtils } from '@services/utils/text'; @@ -71,7 +72,8 @@ export class CoreGradesHelperProvider { let content = String(column.content); if (name == 'itemname') { - await this.setRowIcon(row, content); + this.setRowIconAndType(row, content); + row.link = this.getModuleLink(content); row.rowclass += column.class.indexOf('hidden') >= 0 ? ' hidden' : ''; row.rowclass += column.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : ''; @@ -96,10 +98,23 @@ export class CoreGradesHelperProvider { * Formats a row from the grades table to be rendered in one table. * * @param tableRow JSON object representing row of grades table data. + * @param useLegacyLayout Whether to use the layout before 4.1. * @return Formatted row object. */ - protected formatGradeRowForTable(tableRow: CoreGradesTableRow): CoreGradesFormattedTableRow { + protected formatGradeRowForTable(tableRow: CoreGradesTableRow, useLegacyLayout: boolean): CoreGradesFormattedTableRow { const row: CoreGradesFormattedTableRow = {}; + + if (!useLegacyLayout && 'leader' in tableRow) { + const row = { + itemtype: 'leader', + rowspan: tableRow.leader?.rowspan, + }; + + this.setRowEvenOddClass(row, (tableRow.leader as CoreGradesTableLeaderColumn).class); + + return row; + } + for (let name in tableRow) { const column: CoreGradesTableColumn = tableRow[name]; @@ -116,13 +131,13 @@ export class CoreGradesHelperProvider { row.colspan = itemNameColumn.colspan; row.rowspan = tableRow.leader?.rowspan || 1; - this.setRowIcon(row, content); - row.rowclass = itemNameColumn.class.indexOf('leveleven') < 0 ? 'odd' : 'even'; + this.setRowIconAndType(row, content); + this.setRowEvenOddClass(row, itemNameColumn.class); row.rowclass += itemNameColumn.class.indexOf('hidden') >= 0 ? ' hidden' : ''; row.rowclass += itemNameColumn.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : ''; content = content.replace(/<\/span>/gi, '\n'); - content = CoreTextUtils.cleanTags(content); + content = CoreTextUtils.cleanTags(content, true); name = 'gradeitem'; } else if (name === 'grade') { // Add the pass/fail class if present. @@ -202,7 +217,7 @@ export class CoreGradesHelperProvider { feedback: false, contributiontocoursetotal: false, }; - formatted.rows = table.tabledata.map(row => this.formatGradeRowForTable(row)); + formatted.rows = this.formatGradesTableRows(table.tabledata); // Get a row with some info. let normalRow = formatted.rows.find( @@ -234,6 +249,33 @@ export class CoreGradesHelperProvider { return formatted; } + /** + * Format table rows. + * + * @param rows Unformatted rows. + * @returns Formatted rows. + */ + protected formatGradesTableRows(rows: CoreGradesTableRow[]): CoreGradesFormattedTableRow[] { + const useLegacyLayout = !CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('4.1'); + const formattedRows = rows.map(row => this.formatGradeRowForTable(row, useLegacyLayout)); + + if (!useLegacyLayout) { + for (let index = 0; index < formattedRows.length - 1; index++) { + const row = formattedRows[index]; + const previousRow = formattedRows[index - 1] ?? null; + + if (row.itemtype !== 'leader') { + continue; + } + + row.colspan = previousRow.colspan; + previousRow.rowclass = `${previousRow.rowclass ?? ''} ion-no-border`.trim(); + } + } + + return formattedRows; + } + /** * Get course data for grades since they only have courseid. * @@ -474,7 +516,7 @@ export class CoreGradesHelperProvider { // Find href containing "/mod/xxx/xxx.php". const regex = /href="([^"]*\/mod\/[^"|^/]*\/[^"|^.]*\.php[^"]*)/; - return table.tabledata.filter((row) => { + return this.formatGradesTableRows(table.tabledata.filter((row) => { if (row.itemname && row.itemname.content) { const matches = row.itemname.content.match(regex); @@ -486,7 +528,7 @@ export class CoreGradesHelperProvider { } return false; - }).map((row) => this.formatGradeRowForTable(row)); + })); } /** @@ -582,14 +624,25 @@ export class CoreGradesHelperProvider { return CoreGrades.invalidateCourseGradesItemsData(courseId, userId, groupId, siteId); } + /** + * Set 'odd' or 'even' classes into a row. + * + * @param row Row. + * @param classes Existing row classes. + */ + protected setRowEvenOddClass(row: CoreGradesFormattedTableRow, classes: string): void { + const level = parseInt(classes.match(/(?:^|\s)level(\d+)(?:$|\s)/)?.[1] ?? '0'); + + row.rowclass = `${row.rowclass ?? ''} ${level % 2 === 0 ? 'even' : 'odd'}`.trim(); + } + /** * Parses the image and sets it to the row. * - * @param row Formatted grade row object. - * @param text HTML where the image will be rendered. - * @return Row object with the image. + * @param row Row. + * @param text Row content. */ - protected setRowIcon(row: T, text: string): T { + protected setRowIconAndType(row: CoreGradesFormattedRowCommonData, text: string): void { text = text.replace('%2F', '/').replace('%2f', '/'); if (text.indexOf('/agg_mean') > -1) { row.itemtype = 'agg_mean'; @@ -603,7 +656,7 @@ export class CoreGradesHelperProvider { row.itemtype = 'outcome'; row.icon = 'fas-tasks'; row.iconAlt = Translate.instant('core.grades.outcome'); - } else if (text.indexOf('i/folder') > -1 || text.indexOf('fa-folder') > -1) { + } else if (text.indexOf('i/folder') > -1 || text.indexOf('fa-folder') > -1 || text.indexOf('category-content') > -1) { row.itemtype = 'category'; row.icon = 'fas-folder'; row.iconAlt = Translate.instant('core.grades.category'); @@ -643,8 +696,6 @@ export class CoreGradesHelperProvider { row.iconAlt = Translate.instant('core.unknown'); } } - - return row; } /**