From f7796700a75885e81f819e5700ee008454e46902 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Fri, 11 Nov 2022 12:27:03 +0100
Subject: [PATCH 1/8] MOBILE-4065 a11y: Fix placeholder color
---
src/theme/theme.base.scss | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/theme/theme.base.scss b/src/theme/theme.base.scss
index 1d3f41565..eaf80569e 100644
--- a/src/theme/theme.base.scss
+++ b/src/theme/theme.base.scss
@@ -1564,6 +1564,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;
From 8b8719559375ad784ee16be23bbb9fb579e565c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Fri, 11 Nov 2022 13:22:22 +0100
Subject: [PATCH 2/8] MOBILE-4065 a11y: Change approach to add aria-label to
eye button
---
src/core/components/show-password/core-show-password.html | 2 +-
src/core/components/show-password/show-password.ts | 2 --
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/core/components/show-password/core-show-password.html b/src/core/components/show-password/core-show-password.html
index bd1589bb6..5a260c94a 100644
--- a/src/core/components/show-password/core-show-password.html
+++ b/src/core/components/show-password/core-show-password.html
@@ -1,5 +1,5 @@
-
diff --git a/src/core/components/show-password/show-password.ts b/src/core/components/show-password/show-password.ts
index 22faec88f..2ab247d28 100644
--- a/src/core/components/show-password/show-password.ts
+++ b/src/core/components/show-password/show-password.ts
@@ -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';
}
From 824a804887ef498178a741694fcc487802e67692 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Fri, 11 Nov 2022 13:33:30 +0100
Subject: [PATCH 3/8] MOBILE-4065 a11y: Improve my courses view aria labels
---
scripts/langindex.json | 4 ++--
.../components/myoverview/addon-block-myoverview.html | 4 ++--
src/addons/block/myoverview/lang.json | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/scripts/langindex.json b/scripts/langindex.json
index d99604aae..9964a93ae 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -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",
diff --git a/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html b/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html
index 233d107de..cce9ec632 100644
--- a/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html
+++ b/src/addons/block/myoverview/components/myoverview/addon-block-myoverview.html
@@ -89,11 +89,11 @@
+ [attr.aria-label]="'addon.block_myoverview.aria:list' | translate">
+ [attr.aria-label]="'addon.block_myoverview.aria:card' | translate">
diff --git a/src/addons/block/myoverview/lang.json b/src/addons/block/myoverview/lang.json
index 5c381252e..3738950f0 100644
--- a/src/addons/block/myoverview/lang.json
+++ b/src/addons/block/myoverview/lang.json
@@ -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.",
From 622f6ee2651ffb0762be219cccf656a9e7c0c5f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Fri, 11 Nov 2022 14:27:14 +0100
Subject: [PATCH 4/8] MOBILE-4065 a11y: Get keyboard access to course index
accordions
---
.../features/course/components/course-index/course-index.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/core/features/course/components/course-index/course-index.html b/src/core/features/course/components/course-index/course-index.html
index 7fca7f10e..dd97d4628 100644
--- a/src/core/features/course/components/course-index/course-index.html
+++ b/src/core/features/course/components/course-index/course-index.html
@@ -29,10 +29,10 @@
[class.item-current]="selectedId === section.id" [class.item-dimmed]="!section.visible"
[class.item-hightlighted]="section.highlighted" detail="false" sticky="true">
+ [class.expandable-status-icon-expanded]="section.expanded" tabindex="0">
From 6500f3546298173f6abda1ee1427adbe298015c7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Fri, 11 Nov 2022 14:36:47 +0100
Subject: [PATCH 5/8] MOBILE-4065 a11y: Change module name tag to div
---
.../course/components/module-info/core-course-module-info.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/core/features/course/components/module-info/core-course-module-info.html b/src/core/features/course/components/module-info/core-course-module-info.html
index 1e00b23b0..af4f10c7c 100644
--- a/src/core/features/course/components/module-info/core-course-module-info.html
+++ b/src/core/features/course/components/module-info/core-course-module-info.html
@@ -2,7 +2,7 @@
- {{moduleNameTranslated}}
+ {{moduleNameTranslated}}
From c97d92fd041f046d7559da8ac9f3cc3dcb4abe61 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Fri, 11 Nov 2022 14:44:54 +0100
Subject: [PATCH 6/8] MOBILE-4065 a11y: Add h2 to sections on course summary
---
.../course/pages/course-summary/course-summary.html | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/core/features/course/pages/course-summary/course-summary.html b/src/core/features/course/pages/course-summary/course-summary.html
index 56e89e0fa..994a79a98 100644
--- a/src/core/features/course/pages/course-summary/course-summary.html
+++ b/src/core/features/course/pages/course-summary/course-summary.html
@@ -71,9 +71,9 @@
-
+
{{'core.course.coursesummary' | translate}}
-
+
@@ -86,9 +86,9 @@
class="expandable-status-icon" [class.expandable-status-icon-expanded]="contactsExpanded">
-
+
{{ 'core.teachers' | translate }}
-
+
From 1a8c4fde04526667b941ed7da2474c7eee26ee4e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?=
Date: Mon, 14 Nov 2022 14:18:25 +0100
Subject: [PATCH 7/8] MOBILE-4065 a11y: Course downloads does not stop click
propagation
---
.../pages/course-storage/course-storage.html | 14 ++++-----
.../pages/course-storage/course-storage.ts | 30 ++++++++++++++++---
2 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/src/addons/storagemanager/pages/course-storage/course-storage.html b/src/addons/storagemanager/pages/course-storage/course-storage.html
index 8d2a1b7b3..8ec983aab 100644
--- a/src/addons/storagemanager/pages/course-storage/course-storage.html
+++ b/src/addons/storagemanager/pages/course-storage/course-storage.html
@@ -29,13 +29,13 @@
{{ 'core.calculating' | translate }}
-
+
{{ prefetchCourseData.statusTranslatable | translate }}
-
@@ -90,8 +90,8 @@
{{section.count}} / {{section.total}}
- 0"
- color="danger" fill="clear">
+ 0" color="danger" fill="clear">
@@ -128,9 +128,9 @@
+ (action)="prefetchModule(module)">
- 0" color="danger">
diff --git a/src/addons/storagemanager/pages/course-storage/course-storage.ts b/src/addons/storagemanager/pages/course-storage/course-storage.ts
index 3c3d001ef..5e7a143d5 100644
--- a/src/addons/storagemanager/pages/course-storage/course-storage.ts
+++ b/src/addons/storagemanager/pages/course-storage/course-storage.ts
@@ -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 {
+ async deleteForCourse(event: Event): Promise {
+ 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 {
+ async deleteForSection(event: Event, section: AddonStorageManagerCourseSection): Promise {
+ 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 {
+ async deleteForModule(
+ event: Event,
+ module: AddonStorageManagerModule,
+ section: AddonStorageManagerCourseSection,
+ ): Promise {
+ 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 {
+ async prefetchCourse(event: Event): Promise {
+ event.stopPropagation();
+ event.preventDefault();
+
const courses = await CoreCourses.getUserCourses(true);
let course = courses.find((course) => course.id == this.courseId);
if (!course) {
From 8f518d1b79a66b4ccb5df387d251be1fcc9253c7 Mon Sep 17 00:00:00 2001
From: Noel De Martin
Date: Mon, 14 Nov 2022 14:53:14 +0100
Subject: [PATCH 8/8] MOBILE-4065 tests: Fix translate mock
---
src/testing/utils.ts | 29 ++++++++++++++++++++++-------
1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/src/testing/utils.ts b/src/testing/utils.ts
index 18acf31a3..0072cb595 100644
--- a/src/testing/utils.ts
+++ b/src/testing/utils.ts
@@ -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 {
@@ -45,8 +45,14 @@ type ServiceInjectionToken = AbstractType | Type | string;
let testBedInitialized = false;
const textUtils = new CoreTextUtilsProvider();
-const DEFAULT_SERVICE_SINGLETON_MOCKS: [CoreSingletonProxy, Record][] = [
- [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 Promise.resolve(mock({ dismiss: jest.fn() })),
})],
- [CoreUtils, mock({
+ [CoreUtils, mock(new CoreUtilsProvider(), {
nextTick: () => Promise.resolve(),
})],
];
@@ -273,8 +279,17 @@ export function mockSingleton(
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, 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);