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.main
parent
d0e3ad6ee8
commit
49e0491428
|
@ -27,13 +27,18 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
<ng-container *ngFor="let row of rows">
|
||||
<tr *ngIf="!useLegacyLayout && row.itemtype === 'leader'">
|
||||
<td [attr.rowspan]="row.rowspan" class="core-grades-table-leader"></td>
|
||||
</tr>
|
||||
<tr [attr.role]="row.expandable && showSummary ? 'button row' : 'row'"
|
||||
[attr.tabindex]="row.expandable && showSummary && 0" [attr.aria-expanded]="row.expanded"
|
||||
[attr.aria-label]="rowAriaLabel(row)" [attr.aria-controls]="row.detailsid"
|
||||
(ariaButtonClick)="row.expandable && showSummary && toggleRow(row)" [class]="row.rowclass"
|
||||
[class.core-grades-grade-clickable]="row.expandable && showSummary" [id]="'grade-'+row.id">
|
||||
[class.core-grades-grade-clickable]="row.expandable && showSummary" [id]="'grade-'+row.id"
|
||||
*ngIf="useLegacyLayout || row.itemtype !== 'leader'">
|
||||
<ng-container *ngIf="row.itemtype">
|
||||
<td *ngIf="row.itemtype == 'category'" class="core-grades-table-category" [attr.rowspan]="row.rowspan">
|
||||
<td *ngIf="!useLegacyLayout && row.itemtype == 'category'" class="core-grades-table-category"
|
||||
[attr.rowspan]="row.rowspan">
|
||||
</td>
|
||||
<th class="core-grades-table-gradeitem ion-text-start" [attr.colspan]="row.colspan">
|
||||
<ion-icon *ngIf="row.expandable && showSummary" aria-hidden="true" slot="start" name="fas-chevron-right"
|
||||
|
@ -48,23 +53,25 @@
|
|||
</core-mod-icon>
|
||||
<span [innerHTML]="row.gradeitem"></span>
|
||||
</th>
|
||||
<ng-container *ngFor="let column of columns">
|
||||
<td *ngIf="column.name !== 'gradeitem' && column.name !== 'feedback' && column.name !== 'grade' &&
|
||||
<ng-container *ngIf="row.itemtype !== 'category'">
|
||||
<ng-container *ngFor="let column of columns">
|
||||
<td *ngIf="column.name !== 'gradeitem' && column.name !== 'feedback' && column.name !== 'grade' &&
|
||||
row[column.name] != undefined" [class]="'ion-text-start core-grades-table-' + column.name"
|
||||
[class.ion-hide-md-down]="column.hiddenPhone" [innerHTML]="row[column.name]">
|
||||
</td>
|
||||
<td *ngIf="column.name === 'feedback' && row.feedback !== undefined"
|
||||
class="ion-text-start core-grades-table-feedback" [class.ion-hide-md-down]="column.hiddenPhone">
|
||||
<core-format-text collapsible-item [text]="row.feedback" contextLevel="course"
|
||||
[contextInstanceId]="courseId">
|
||||
</core-format-text>
|
||||
</td>
|
||||
<td *ngIf="column.name === 'grade'" [class.ion-hide-md-down]="column.hiddenPhone"
|
||||
class="ion-text-start core-grades-table-grade {{row.gradeClass}}">
|
||||
<ion-icon *ngIf="row.gradeIcon" [name]="row.gradeIcon" [attr.aria-label]="row.gradeIconAlt">
|
||||
</ion-icon>
|
||||
<span [innerHTML]="row[column.name]"></span>
|
||||
</td>
|
||||
[class.ion-hide-md-down]="column.hiddenPhone" [innerHTML]="row[column.name]">
|
||||
</td>
|
||||
<td *ngIf="column.name === 'feedback' && row.feedback !== undefined"
|
||||
class="ion-text-start core-grades-table-feedback" [class.ion-hide-md-down]="column.hiddenPhone">
|
||||
<core-format-text collapsible-item [text]="row.feedback" contextLevel="course"
|
||||
[contextInstanceId]="courseId">
|
||||
</core-format-text>
|
||||
</td>
|
||||
<td *ngIf="column.name === 'grade'" [class.ion-hide-md-down]="column.hiddenPhone"
|
||||
class="ion-text-start core-grades-table-grade {{row.gradeClass}}">
|
||||
<ion-icon *ngIf="row.gradeIcon" [name]="row.gradeIcon" [attr.aria-label]="row.gradeIconAlt">
|
||||
</ion-icon>
|
||||
<span [innerHTML]="row[column.name]"></span>
|
||||
</td>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
</tr>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -124,6 +124,14 @@
|
|||
}
|
||||
}
|
||||
|
||||
.core-grades-table-leader {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.ion-no-border {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.dimmed_text,
|
||||
.hidden {
|
||||
opacity: .7;
|
||||
|
|
|
@ -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<T extends CoreGradesFormattedRowCommonData>(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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue