commit
fba9fd8b2d
|
@ -40,14 +40,14 @@
|
|||
"addon.block_learningplans.pluginname": "block_lp",
|
||||
"addon.block_myoverview.all": "block_myoverview",
|
||||
"addon.block_myoverview.allincludinghidden": "block_myoverview",
|
||||
"addon.block_myoverview.aria:card": "block_myoverview",
|
||||
"addon.block_myoverview.aria:list": "block_myoverview",
|
||||
"addon.block_myoverview.browseallcourses": "local_moodlemobileapp",
|
||||
"addon.block_myoverview.card": "block_myoverview",
|
||||
"addon.block_myoverview.favourites": "block_myoverview",
|
||||
"addon.block_myoverview.future": "block_myoverview",
|
||||
"addon.block_myoverview.hiddencourses": "block_myoverview",
|
||||
"addon.block_myoverview.inprogress": "block_myoverview",
|
||||
"addon.block_myoverview.lastaccessed": "block_myoverview",
|
||||
"addon.block_myoverview.list": "block_myoverview",
|
||||
"addon.block_myoverview.nocoursesenrolled": "local_moodlemobileapp",
|
||||
"addon.block_myoverview.nocoursesenrolleddescription": "local_moodlemobileapp",
|
||||
"addon.block_myoverview.noresult": "local_moodlemobileapp",
|
||||
|
|
|
@ -89,11 +89,11 @@
|
|||
</ion-col>
|
||||
<ion-col size="auto" *ngIf="isLayoutSwitcherAvailable">
|
||||
<ion-button *ngIf="layout == 'card'" fill="outline" (click)="toggleLayout('list')"
|
||||
[attr.aria-label]="'addon.block_myoverview.list' | translate">
|
||||
[attr.aria-label]="'addon.block_myoverview.aria:list' | translate">
|
||||
<ion-icon slot="icon-only" name="fas-list" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button *ngIf="layout == 'list'" fill="outline" (click)="toggleLayout('card')"
|
||||
[attr.aria-label]="'addon.block_myoverview.card' | translate">
|
||||
[attr.aria-label]="'addon.block_myoverview.aria:card' | translate">
|
||||
<ion-icon slot="icon-only" name="fas-th" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-col>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"all": "All",
|
||||
"allincludinghidden": "All (including removed from view)",
|
||||
"aria:card": "Switch to card view",
|
||||
"aria:list": "Switch to list view",
|
||||
"browseallcourses": "Browse all courses",
|
||||
"card": "Card",
|
||||
"favourites": "Starred",
|
||||
"future": "Future",
|
||||
"hiddencourses": "Removed from view",
|
||||
"inprogress": "In progress",
|
||||
"lastaccessed": "Last accessed",
|
||||
"list": "List",
|
||||
"nocoursesenrolled": "You're not enrolled in any courses yet.",
|
||||
"nocoursesenrolleddescription": "Browse all available courses below and start learning.",
|
||||
"noresult": "Your search didn't match any courses.",
|
||||
|
|
|
@ -29,13 +29,13 @@
|
|||
<ng-container *ngIf="calculatingSize">{{ 'core.calculating' | translate }}</ng-container>
|
||||
</ion-badge>
|
||||
</ion-item>
|
||||
<ion-button *ngIf="downloadCourseEnabled" (click)="prefetchCourse()" expand="block" fill="outline" class="ion-no-margin"
|
||||
[disabled]="prefetchCourseData.loading">
|
||||
<ion-button *ngIf="downloadCourseEnabled" (click)="prefetchCourse($event)" expand="block" fill="outline"
|
||||
class="ion-no-margin" [disabled]="prefetchCourseData.loading">
|
||||
<ion-icon *ngIf="!prefetchCourseData.loading" [name]="prefetchCourseData.icon" slot="start"></ion-icon>
|
||||
<ion-spinner *ngIf="prefetchCourseData.loading" slot="start"></ion-spinner>
|
||||
{{ prefetchCourseData.statusTranslatable | translate }}
|
||||
</ion-button>
|
||||
<ion-button [disabled]="calculatingSize || totalSize <= 0" (click)="deleteForCourse()" expand="block" color="danger"
|
||||
<ion-button [disabled]="calculatingSize || totalSize <= 0" (click)="deleteForCourse($event)" expand="block" color="danger"
|
||||
class="ion-no-margin ion-margin-top">
|
||||
<ion-icon name="fas-trash" slot="start" [attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate:
|
||||
{ name: title }">
|
||||
|
@ -90,8 +90,8 @@
|
|||
{{section.count}} / {{section.total}}
|
||||
</ion-badge>
|
||||
</div>
|
||||
<ion-button (click)="deleteForSection(section)" *ngIf="!section.calculatingSize && section.totalSize > 0"
|
||||
color="danger" fill="clear">
|
||||
<ion-button (click)="deleteForSection($event, section)"
|
||||
*ngIf="!section.calculatingSize && section.totalSize > 0" color="danger" fill="clear">
|
||||
<ion-icon name="fas-trash" slot="icon-only"
|
||||
[attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: section.name }">
|
||||
</ion-icon>
|
||||
|
@ -128,9 +128,9 @@
|
|||
<core-download-refresh *ngIf="downloadEnabled && module.handlerData?.showDownloadButton &&
|
||||
module.downloadStatus != statusDownloaded" [status]="module.downloadStatus" [enabled]="true"
|
||||
[canTrustDownload]="true" [loading]="module.spinner || module.handlerData.spinner"
|
||||
(action)="prefetchModule(module, $event)">
|
||||
(action)="prefetchModule(module)">
|
||||
</core-download-refresh>
|
||||
<ion-button fill="clear" (click)="deleteForModule(module, section)"
|
||||
<ion-button fill="clear" (click)="deleteForModule($event, module, section)"
|
||||
*ngIf="!module.calculatingSize && module.totalSize > 0" color="danger">
|
||||
<ion-icon name="fas-trash" slot="icon-only"
|
||||
[attr.aria-label]="'addon.storagemanager.deletedatafrom' | translate: { name: module.name }">
|
||||
|
|
|
@ -352,8 +352,13 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
|||
* The user has requested a delete for the whole course data.
|
||||
*
|
||||
* (This works by deleting data for each module on the course that has data.)
|
||||
*
|
||||
* @param event Event object.
|
||||
*/
|
||||
async deleteForCourse(): Promise<void> {
|
||||
async deleteForCourse(event: Event): Promise<void> {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
await CoreDomUtils.showDeleteConfirm(
|
||||
'addon.storagemanager.confirmdeletedatafrom',
|
||||
|
@ -384,9 +389,13 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
|||
*
|
||||
* (This works by deleting data for each module in the section that has data.)
|
||||
*
|
||||
* @param event Event object.
|
||||
* @param section Section object with information about section and modules
|
||||
*/
|
||||
async deleteForSection(section: AddonStorageManagerCourseSection): Promise<void> {
|
||||
async deleteForSection(event: Event, section: AddonStorageManagerCourseSection): Promise<void> {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
try {
|
||||
await CoreDomUtils.showDeleteConfirm(
|
||||
'addon.storagemanager.confirmdeletedatafrom',
|
||||
|
@ -413,10 +422,18 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
|||
/**
|
||||
* The user has requested a delete for a module's data
|
||||
*
|
||||
* @param event Event object.
|
||||
* @param module Module details
|
||||
* @param section Section the module belongs to.
|
||||
*/
|
||||
async deleteForModule(module: AddonStorageManagerModule, section: AddonStorageManagerCourseSection): Promise<void> {
|
||||
async deleteForModule(
|
||||
event: Event,
|
||||
module: AddonStorageManagerModule,
|
||||
section: AddonStorageManagerCourseSection,
|
||||
): Promise<void> {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if (module.totalSize === 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -635,8 +652,13 @@ export class AddonStorageManagerCourseStoragePage implements OnInit, OnDestroy {
|
|||
|
||||
/**
|
||||
* Prefetch the whole course.
|
||||
*
|
||||
* @param event Event object.
|
||||
*/
|
||||
async prefetchCourse(): Promise<void> {
|
||||
async prefetchCourse(event: Event): Promise<void> {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const courses = await CoreCourses.getUserCourses(true);
|
||||
let course = courses.find((course) => course.id == this.courseId);
|
||||
if (!course) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<ng-content></ng-content>
|
||||
<ion-button fill="clear" [attr.aria-label]="label | translate" core-suppress-events (onClick)="toggle($event)"
|
||||
<ion-button fill="clear" [attr.aria-label]="(shown ? 'core.hide' : 'core.show') | translate" core-suppress-events (onClick)="toggle($event)"
|
||||
(mousedown)="doNotBlur($event)" (keydown)="doNotBlur($event)" (keyup)="toggle($event)">
|
||||
<ion-icon [name]="shown ? 'fas-eye-slash' : 'fas-eye'" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
|
|
|
@ -45,7 +45,6 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
|
|||
@ContentChild(IonInput) ionInput?: IonInput;
|
||||
|
||||
shown = false; // Whether the password is shown.
|
||||
label = ''; // Label for the button to show/hide.
|
||||
|
||||
protected input?: HTMLInputElement; // Input affected.
|
||||
protected element: HTMLElement; // Current element.
|
||||
|
@ -97,7 +96,6 @@ export class CoreShowPasswordComponent implements OnInit, AfterViewInit {
|
|||
* @param input The input element.
|
||||
*/
|
||||
protected setData(input: HTMLInputElement): void {
|
||||
this.label = this.shown ? 'core.hide' : 'core.show';
|
||||
input.type = this.shown ? 'text' : 'password';
|
||||
}
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@
|
|||
[class.item-current]="selectedId === section.id" [class.item-dimmed]="!section.visible"
|
||||
[class.item-hightlighted]="section.highlighted" detail="false" sticky="true">
|
||||
<ion-icon *ngIf="section.hasVisibleModules" name="fas-chevron-right" flip-rtl slot="start"
|
||||
class="expandable-status-icon" (click)="toggleExpand($event, section)"
|
||||
class="expandable-status-icon" (ariaButtonClick)="toggleExpand($event, section)"
|
||||
[attr.aria-label]="(section.expanded ? 'core.collapse' : 'core.expand') | translate"
|
||||
[attr.aria-expanded]="section.expanded" [attr.aria-controls]="'core-course-index-section-' + section.id"
|
||||
[class.expandable-status-icon-expanded]="section.expanded">
|
||||
[class.expandable-status-icon-expanded]="section.expanded" tabindex="0">
|
||||
</ion-icon>
|
||||
<ion-icon *ngIf="!section.hasVisibleModules" name="" slot="start" aria-hidden="true" class="expandable-status-icon">
|
||||
</ion-icon>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<core-mod-icon slot="start" [modicon]="modicon" [modname]="module.modname" [componentId]="module.instance">
|
||||
</core-mod-icon>
|
||||
<ion-label>
|
||||
<p class="core-modulename" *ngIf="moduleNameTranslated">{{moduleNameTranslated}}</p>
|
||||
<div class="core-modulename" *ngIf="moduleNameTranslated">{{moduleNameTranslated}}</div>
|
||||
<h1>
|
||||
<core-format-text [text]="module.name" contextLevel="module" [component]="component" [componentId]="componentId"
|
||||
[contextInstanceId]="module.id" [courseId]="courseId">
|
||||
|
|
|
@ -71,9 +71,9 @@
|
|||
|
||||
<ion-item class="ion-text-wrap" *ngIf="course.summary" detail="false">
|
||||
<ion-label>
|
||||
<p class="item-heading">
|
||||
<h2 class="item-heading">
|
||||
{{'core.course.coursesummary' | translate}}
|
||||
</p>
|
||||
</h2>
|
||||
<core-format-text [text]="course.summary" collapsible-item contextLevel="course" [contextInstanceId]="course.id">
|
||||
</core-format-text>
|
||||
</ion-label>
|
||||
|
@ -86,9 +86,9 @@
|
|||
class="expandable-status-icon" [class.expandable-status-icon-expanded]="contactsExpanded">
|
||||
</ion-icon>
|
||||
<ion-label>
|
||||
<p class="item-heading">
|
||||
<h2 class="item-heading">
|
||||
{{ 'core.teachers' | translate }}
|
||||
</p>
|
||||
</h2>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ng-container *ngIf="contactsExpanded || course.contacts.length < 5">
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { AbstractType, Component, CUSTOM_ELEMENTS_SCHEMA, Type, ViewChild } from '@angular/core';
|
||||
import { AbstractType, Component, CUSTOM_ELEMENTS_SCHEMA, EventEmitter, Type, ViewChild } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { Observable, of, Subject } from 'rxjs';
|
||||
import { sep } from 'path';
|
||||
|
||||
import { CORE_SITE_SCHEMAS } from '@services/sites';
|
||||
|
@ -33,7 +33,7 @@ import { TranslateService, TranslateStore } from '@ngx-translate/core';
|
|||
import { CoreIonLoadingElement } from '@classes/ion-loading';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { DefaultUrlSerializer, UrlSerializer } from '@angular/router';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreUtils, CoreUtilsProvider } from '@services/utils/utils';
|
||||
|
||||
abstract class WrapperComponent<U> {
|
||||
|
||||
|
@ -45,8 +45,14 @@ type ServiceInjectionToken = AbstractType<unknown> | Type<unknown> | string;
|
|||
|
||||
let testBedInitialized = false;
|
||||
const textUtils = new CoreTextUtilsProvider();
|
||||
const DEFAULT_SERVICE_SINGLETON_MOCKS: [CoreSingletonProxy, Record<string, unknown>][] = [
|
||||
[Translate, mock({ instant: key => key })],
|
||||
const DEFAULT_SERVICE_SINGLETON_MOCKS: [CoreSingletonProxy, unknown][] = [
|
||||
[Translate, mock({
|
||||
instant: key => key,
|
||||
get: key => of(key),
|
||||
onTranslationChange: new EventEmitter(),
|
||||
onLangChange: new EventEmitter(),
|
||||
onDefaultLangChange: new EventEmitter(),
|
||||
})],
|
||||
[CoreDB, mock({ getDB: () => mock() })],
|
||||
[CoreNavigator, mock({ navigateToSitePath: () => Promise.resolve(true) })],
|
||||
[ApplicationInit, mock({
|
||||
|
@ -66,7 +72,7 @@ const DEFAULT_SERVICE_SINGLETON_MOCKS: [CoreSingletonProxy, Record<string, unkno
|
|||
[CoreDomUtils, mock({
|
||||
showModalLoading: () => Promise.resolve(mock<CoreIonLoadingElement>({ dismiss: jest.fn() })),
|
||||
})],
|
||||
[CoreUtils, mock({
|
||||
[CoreUtils, mock(new CoreUtilsProvider(), {
|
||||
nextTick: () => Promise.resolve(),
|
||||
})],
|
||||
];
|
||||
|
@ -273,8 +279,17 @@ export function mockSingleton<T>(
|
|||
const methods = Array.isArray(methodsOrProperties) ? methodsOrProperties : [];
|
||||
const instance = getServiceInstance(singleton.injectionToken) as T;
|
||||
const mockInstance = mock(instance, methods);
|
||||
const mockInstancePrototype = Object.getPrototypeOf(mockInstance);
|
||||
|
||||
Object.assign(mockInstance as Record<string, unknown>, properties);
|
||||
for (const [name, value] of Object.entries(properties)) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(mockInstancePrototype, name);
|
||||
|
||||
if (descriptor && !descriptor.writable) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mockInstance[name] = value;
|
||||
}
|
||||
|
||||
singleton.setInstance(mockInstance);
|
||||
|
||||
|
|
|
@ -1568,6 +1568,11 @@ ion-modal:focus-visible {
|
|||
box-shadow: none;
|
||||
}
|
||||
|
||||
input {
|
||||
--placeholder-color: var(--ion-placeholder-color);
|
||||
--placeholder-opacity: .85;
|
||||
}
|
||||
|
||||
ion-input .native-input {
|
||||
&:focus, &:focus-visible {
|
||||
box-shadow: none;
|
||||
|
|
Loading…
Reference in New Issue