From 92f65b38ad00c23411734f71a7eb444e6455d81a Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Mon, 11 Mar 2024 15:31:29 +0100 Subject: [PATCH 1/6] MOBILE-4268 core: Remove storybook --- .storybook/main.js | 11 -- .storybook/preview.js | 11 -- .storybook/styles.scss | 7 - .storybook/tsconfig.json | 11 -- package.json | 1 - src/assets/storybook/courses.json | 1 - src/assets/storybook/geopattern.svg | 2 - src/assets/storybook/sites/companylisa.ts | 32 ---- src/assets/storybook/sites/schoolbarbara.ts | 32 ---- src/assets/storybook/sites/schooljeffery.ts | 32 ---- .../stories/components/components.module.ts | 43 ------ .../course-image-cards-page.html | 22 --- .../course-image-cards-page.scss | 16 -- .../course-image-cards-page.ts | 28 ---- .../course-image-list-page.html | 19 --- .../course-image-list-page.ts | 27 ---- .../empty-box-page/empty-box-page.html | 15 -- .../empty-box-page/empty-box-page.ts | 27 ---- .../empty-box-wrapper/empty-box-wrapper.html | 3 - .../empty-box-wrapper/empty-box-wrapper.ts | 38 ----- .../sites-list-wrapper.html | 23 --- .../sites-list-wrapper/sites-list-wrapper.ts | 60 -------- .../stories/course-image.stories.ts | 104 ------------- .../components/stories/empty-box.stories.ts | 79 ---------- .../components/stories/error-info.stories.ts | 50 ------- .../components/stories/sites-list.stories.ts | 78 ---------- .../components/stories/user-avatar.stories.ts | 37 ----- .../stories/components/components.module.ts | 37 ----- .../global-search-results-page.html | 18 --- .../global-search-results-page.ts | 141 ------------------ .../stories/global-search-result.stories.ts | 136 ----------------- src/storybook/storybook.module.ts | 78 ---------- src/storybook/stubs/classes/site.ts | 59 -------- src/storybook/stubs/classes/sqlitedb.ts | 32 ---- src/storybook/stubs/services/db.ts | 35 ----- src/storybook/stubs/services/filepool.ts | 32 ---- src/storybook/stubs/services/http.ts | 38 ----- .../stubs/services/pushnotifications.ts | 32 ---- src/storybook/stubs/services/sites.ts | 86 ----------- src/storybook/utils/helpers.ts | 30 ---- tsconfig.app.json | 1 - 41 files changed, 1564 deletions(-) delete mode 100644 .storybook/main.js delete mode 100644 .storybook/preview.js delete mode 100644 .storybook/styles.scss delete mode 100644 .storybook/tsconfig.json delete mode 100644 src/assets/storybook/courses.json delete mode 100644 src/assets/storybook/geopattern.svg delete mode 100644 src/assets/storybook/sites/companylisa.ts delete mode 100644 src/assets/storybook/sites/schoolbarbara.ts delete mode 100644 src/assets/storybook/sites/schooljeffery.ts delete mode 100644 src/core/components/stories/components/components.module.ts delete mode 100644 src/core/components/stories/components/course-image-cards-page/course-image-cards-page.html delete mode 100644 src/core/components/stories/components/course-image-cards-page/course-image-cards-page.scss delete mode 100644 src/core/components/stories/components/course-image-cards-page/course-image-cards-page.ts delete mode 100644 src/core/components/stories/components/course-image-list-page/course-image-list-page.html delete mode 100644 src/core/components/stories/components/course-image-list-page/course-image-list-page.ts delete mode 100644 src/core/components/stories/components/empty-box-page/empty-box-page.html delete mode 100644 src/core/components/stories/components/empty-box-page/empty-box-page.ts delete mode 100644 src/core/components/stories/components/empty-box-wrapper/empty-box-wrapper.html delete mode 100644 src/core/components/stories/components/empty-box-wrapper/empty-box-wrapper.ts delete mode 100644 src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.html delete mode 100644 src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.ts delete mode 100644 src/core/components/stories/course-image.stories.ts delete mode 100644 src/core/components/stories/empty-box.stories.ts delete mode 100644 src/core/components/stories/error-info.stories.ts delete mode 100644 src/core/components/stories/sites-list.stories.ts delete mode 100644 src/core/components/stories/user-avatar.stories.ts delete mode 100644 src/core/features/search/stories/components/components.module.ts delete mode 100644 src/core/features/search/stories/components/global-search-results-page/global-search-results-page.html delete mode 100644 src/core/features/search/stories/components/global-search-results-page/global-search-results-page.ts delete mode 100644 src/core/features/search/stories/global-search-result.stories.ts delete mode 100644 src/storybook/storybook.module.ts delete mode 100644 src/storybook/stubs/classes/site.ts delete mode 100644 src/storybook/stubs/classes/sqlitedb.ts delete mode 100644 src/storybook/stubs/services/db.ts delete mode 100644 src/storybook/stubs/services/filepool.ts delete mode 100644 src/storybook/stubs/services/http.ts delete mode 100644 src/storybook/stubs/services/pushnotifications.ts delete mode 100644 src/storybook/stubs/services/sites.ts delete mode 100644 src/storybook/utils/helpers.ts diff --git a/.storybook/main.js b/.storybook/main.js deleted file mode 100644 index 6d3a8b83b..000000000 --- a/.storybook/main.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - framework: '@storybook/angular', - addons: [ - '@storybook/addon-controls', - '@storybook/addon-viewport', - 'storybook-addon-designs', - 'storybook-addon-rtl-direction', - 'storybook-dark-mode', - ], - stories: ['../src/**/*.stories.ts'], -} diff --git a/.storybook/preview.js b/.storybook/preview.js deleted file mode 100644 index c3b705822..000000000 --- a/.storybook/preview.js +++ /dev/null @@ -1,11 +0,0 @@ -import '!style-loader!css-loader!sass-loader!../src/theme/theme.design-system.scss'; -import '!style-loader!css-loader!sass-loader!./styles.scss'; - -export const parameters = { - layout: 'centered', - darkMode: { - darkClass: 'dark', - classTarget: 'html', - stylePreview: true, - }, -}; diff --git a/.storybook/styles.scss b/.storybook/styles.scss deleted file mode 100644 index 8be75bade..000000000 --- a/.storybook/styles.scss +++ /dev/null @@ -1,7 +0,0 @@ -storybook-dynamic-app-root { - color: var(--ion-text-color); -} - -.core-error-info { - max-width: 300px; -} diff --git a/.storybook/tsconfig.json b/.storybook/tsconfig.json deleted file mode 100644 index a8a27caf8..000000000 --- a/.storybook/tsconfig.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../tsconfig.json", - "include": [ - "../src/**/*" - ], - "exclude": [ - "../src/**/tests/**", - "../src/testing/**", - "../src/**/*.test.ts" - ] -} diff --git a/package.json b/package.json index 6de706636..2032c4f3f 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "dev:ios": "ionic cordova run ios", "prod:android": "npm run prod --prefix cordova-plugin-moodleapp && NODE_ENV=production ionic cordova run android --prod", "prod:ios": "NODE_ENV=production ionic cordova run ios --prod", - "storybook": "start-storybook -p 6006", "test": "NODE_ENV=testing gulp && jest --verbose", "test:ci": "NODE_ENV=testing gulp && jest -ci --runInBand --verbose", "test:watch": "NODE_ENV=testing gulp watch & jest --watch", diff --git a/src/assets/storybook/courses.json b/src/assets/storybook/courses.json deleted file mode 100644 index 81b9390e0..000000000 --- a/src/assets/storybook/courses.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":1,"courseimage":"https://picsum.photos/500/500","shortname":"Moodle and Mountaineering","summary":"This course will introduce you to the basics of Alpine Mountaineering, while at the same time highlighting some of the great features of Moodle."},{"id":2,"courseimage":"assets/storybook/geopattern.svg","shortname":"Digital Literacy","summary":"This course explores Digital Literacy and its importance for teachers and students. The course is optimised for the Moodle App. Please try it out!"},{"id":3,"shortname":"Class and Conflict in World Cinema","summary":"In this module we will analyse two very significant films - City of God and La Haine, both of which depict violent lives in poor conditions, the former in the favelas of Brazil and the latter in a Parisian banlieue. We will look at how conflict and class are portrayed, focusing particularly on the use of mise en scène."}] diff --git a/src/assets/storybook/geopattern.svg b/src/assets/storybook/geopattern.svg deleted file mode 100644 index 60e7e8a9d..000000000 --- a/src/assets/storybook/geopattern.svg +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/src/assets/storybook/sites/companylisa.ts b/src/assets/storybook/sites/companylisa.ts deleted file mode 100644 index 3689523d7..000000000 --- a/src/assets/storybook/sites/companylisa.ts +++ /dev/null @@ -1,32 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreSiteFixture } from '@/storybook/stubs/classes/site'; - -export const companyLisaSite: CoreSiteFixture = { - id: 'companylisasite', - info: { - version: '2022041900', - sitename: 'Company', - username: 'lisa', - firstname: 'Lisa', - lastname: 'Díaz', - fullname: 'Lisa Díaz', - lang: 'en', - userid: 1, - siteurl: 'https://company.example.edu', - userpictureurl: 'https://i.pravatar.cc/300?user=companylisa', - functions: [], - }, -}; diff --git a/src/assets/storybook/sites/schoolbarbara.ts b/src/assets/storybook/sites/schoolbarbara.ts deleted file mode 100644 index c4fa3eda3..000000000 --- a/src/assets/storybook/sites/schoolbarbara.ts +++ /dev/null @@ -1,32 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreSiteFixture } from '@/storybook/stubs/classes/site'; - -export const schoolBarbaraSite: CoreSiteFixture = { - id: 'schoolbarbarasite', - info: { - version: '2022041900', - sitename: 'School', - username: 'barbara', - firstname: 'Barbara', - lastname: 'Gardner', - fullname: 'Barbara Gardner', - lang: 'en', - userid: 1, - siteurl: 'https://campus.example.edu', - userpictureurl: 'https://i.pravatar.cc/300?user=schoolbarbara', - functions: [], - }, -}; diff --git a/src/assets/storybook/sites/schooljeffery.ts b/src/assets/storybook/sites/schooljeffery.ts deleted file mode 100644 index c7823f2b0..000000000 --- a/src/assets/storybook/sites/schooljeffery.ts +++ /dev/null @@ -1,32 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreSiteFixture } from '@/storybook/stubs/classes/site'; - -export const schoolJefferySite: CoreSiteFixture = { - id: 'schooljefferysite', - info: { - version: '2022041900', - sitename: 'School', - username: 'jeffery', - firstname: 'Jeffery', - lastname: 'Sanders', - fullname: 'Jeffery Sanders', - lang: 'en', - userid: 2, - siteurl: 'https://campus.example.edu', - userpictureurl: 'https://i.pravatar.cc/300?user=schooljeffery', - functions: [], - }, -}; diff --git a/src/core/components/stories/components/components.module.ts b/src/core/components/stories/components/components.module.ts deleted file mode 100644 index d0648467e..000000000 --- a/src/core/components/stories/components/components.module.ts +++ /dev/null @@ -1,43 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { CoreEmptyBoxPageComponent } from './empty-box-page/empty-box-page'; -import { CoreEmptyBoxWrapperComponent } from './empty-box-wrapper/empty-box-wrapper'; -import { StorybookModule } from '@/storybook/storybook.module'; -import { CoreSearchComponentsModule } from '@features/search/components/components.module'; -import { CoreComponentsModule } from '@components/components.module'; -import { CommonModule } from '@angular/common'; -import { CoreCourseImageCardsPageComponent } from '@components/stories/components/course-image-cards-page/course-image-cards-page'; -import { CoreCourseImageListPageComponent } from '@components/stories/components/course-image-list-page/course-image-list-page'; -import { CoreSitesListWrapperComponent } from './sites-list-wrapper/sites-list-wrapper'; -import { CoreDirectivesModule } from '@directives/directives.module'; - -@NgModule({ - declarations: [ - CoreCourseImageCardsPageComponent, - CoreCourseImageListPageComponent, - CoreEmptyBoxPageComponent, - CoreEmptyBoxWrapperComponent, - CoreSitesListWrapperComponent, - ], - imports: [ - CommonModule, - StorybookModule, - CoreDirectivesModule, - CoreComponentsModule, - CoreSearchComponentsModule, - ], -}) -export class CoreComponentsStorybookModule {} diff --git a/src/core/components/stories/components/course-image-cards-page/course-image-cards-page.html b/src/core/components/stories/components/course-image-cards-page/course-image-cards-page.html deleted file mode 100644 index f1ad00bca..000000000 --- a/src/core/components/stories/components/course-image-cards-page/course-image-cards-page.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - -

Course Cards

-
-
-
- - -
- -
- - {{ course.shortname }} - - - {{ course.summary }} - -
-
-
diff --git a/src/core/components/stories/components/course-image-cards-page/course-image-cards-page.scss b/src/core/components/stories/components/course-image-cards-page/course-image-cards-page.scss deleted file mode 100644 index 23f07c6fd..000000000 --- a/src/core/components/stories/components/course-image-cards-page/course-image-cards-page.scss +++ /dev/null @@ -1,16 +0,0 @@ -:host { - - ion-card { - max-width: 350px; - margin-right: auto; - margin-left: auto; - } - - .course-image-wrapper { - width: 100%; - height: 0; - padding-top: 40%; - position: relative; - } - -} diff --git a/src/core/components/stories/components/course-image-cards-page/course-image-cards-page.ts b/src/core/components/stories/components/course-image-cards-page/course-image-cards-page.ts deleted file mode 100644 index f2b6b0323..000000000 --- a/src/core/components/stories/components/course-image-cards-page/course-image-cards-page.ts +++ /dev/null @@ -1,28 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { CoreCourseListItem } from '@features/courses/services/courses'; -import courses from '@/assets/storybook/courses.json'; - -@Component({ - selector: 'core-course-image-cards-page', - templateUrl: 'course-image-cards-page.html', - styleUrls: ['./course-image-cards-page.scss'], -}) -export class CoreCourseImageCardsPageComponent { - - courses: Partial[] = courses; - -} diff --git a/src/core/components/stories/components/course-image-list-page/course-image-list-page.html b/src/core/components/stories/components/course-image-list-page/course-image-list-page.html deleted file mode 100644 index f9638bea7..000000000 --- a/src/core/components/stories/components/course-image-list-page/course-image-list-page.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - -

Courses List

-
-
-
- - - - - - {{ course.shortname }} - - - - -
diff --git a/src/core/components/stories/components/course-image-list-page/course-image-list-page.ts b/src/core/components/stories/components/course-image-list-page/course-image-list-page.ts deleted file mode 100644 index cf66b61f0..000000000 --- a/src/core/components/stories/components/course-image-list-page/course-image-list-page.ts +++ /dev/null @@ -1,27 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { CoreCourseListItem } from '@features/courses/services/courses'; -import courses from '@/assets/storybook/courses.json'; - -@Component({ - selector: 'core-course-image-list-page', - templateUrl: 'course-image-list-page.html', -}) -export class CoreCourseImageListPageComponent { - - courses: Partial[] = courses; - -} diff --git a/src/core/components/stories/components/empty-box-page/empty-box-page.html b/src/core/components/stories/components/empty-box-page/empty-box-page.html deleted file mode 100644 index 0bd2c567a..000000000 --- a/src/core/components/stories/components/empty-box-page/empty-box-page.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - -

Search

-
-
-
- -
- - -
-
-
diff --git a/src/core/components/stories/components/empty-box-page/empty-box-page.ts b/src/core/components/stories/components/empty-box-page/empty-box-page.ts deleted file mode 100644 index a290850b8..000000000 --- a/src/core/components/stories/components/empty-box-page/empty-box-page.ts +++ /dev/null @@ -1,27 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input } from '@angular/core'; - -@Component({ - selector: 'core-empty-box-page', - templateUrl: 'empty-box-page.html', -}) -export class CoreEmptyBoxPageComponent { - - @Input() icon!: string; - @Input() content!: string; - @Input() dimmed!: boolean; - -} diff --git a/src/core/components/stories/components/empty-box-wrapper/empty-box-wrapper.html b/src/core/components/stories/components/empty-box-wrapper/empty-box-wrapper.html deleted file mode 100644 index 10dc9fa5f..000000000 --- a/src/core/components/stories/components/empty-box-wrapper/empty-box-wrapper.html +++ /dev/null @@ -1,3 +0,0 @@ - -
-
diff --git a/src/core/components/stories/components/empty-box-wrapper/empty-box-wrapper.ts b/src/core/components/stories/components/empty-box-wrapper/empty-box-wrapper.ts deleted file mode 100644 index 6b99f2a47..000000000 --- a/src/core/components/stories/components/empty-box-wrapper/empty-box-wrapper.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnChanges } from '@angular/core'; -import { DomSanitizer } from '@singletons'; -import { SafeHtml } from '@angular/platform-browser'; - -@Component({ - selector: 'core-empty-box-wrapper', - templateUrl: 'empty-box-wrapper.html', -}) -export class CoreEmptyBoxWrapperComponent implements OnChanges { - - @Input() icon!: string; - @Input() content!: string; - @Input() dimmed!: boolean; - - html?: SafeHtml; - - /** - * @inheritdoc - */ - ngOnChanges(): void { - this.html = DomSanitizer.bypassSecurityTrustHtml(this.content); - } - -} diff --git a/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.html b/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.html deleted file mode 100644 index a7b72397b..000000000 --- a/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - -

Extra text for user {{ site.fullname }}

- {{ site.badge }} MB -
- - - - - - - - {{site.badge}} - - - -
-
-
diff --git a/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.ts b/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.ts deleted file mode 100644 index 06dc02b22..000000000 --- a/src/core/components/stories/components/sites-list-wrapper/sites-list-wrapper.ts +++ /dev/null @@ -1,60 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; -import { CoreAccountsList, CoreLoginHelper } from '@features/login/services/login-helper'; -import { CoreSiteBasicInfo } from '@services/sites'; - -@Component({ - selector: 'core-sites-list-wrapper', - templateUrl: 'sites-list-wrapper.html', -}) -export class CoreSitesListWrapperComponent implements OnInit, OnChanges { - - @Input() sitesClickable = false; - @Input() currentSiteClickableSelect = 'undefined'; - @Input() extraText: 'text' | 'badge' | 'none' = 'none'; - @Input() extraDetails: 'delete-button' | 'badge' | 'none' = 'none'; - - accountsList?: CoreAccountsList; - currentSiteClickable?: boolean; - - /** - * @inheritdoc - */ - async ngOnInit(): Promise { - this.accountsList = await CoreLoginHelper.getAccountsList(); - } - - /** - * @inheritdoc - */ - async ngOnChanges(changes: SimpleChanges): Promise { - if (changes.currentSiteClickableSelect) { - this.currentSiteClickable = this.currentSiteClickableSelect === 'undefined' ? - undefined : - this.currentSiteClickableSelect === 'true'; - } - } - - /** - * Site clicked. - * - * @param site Site. - */ - siteClicked(site: CoreSiteBasicInfo): void { - alert(`clicked on ${site.id} - ${site.fullname}`); - } - -} diff --git a/src/core/components/stories/course-image.stories.ts b/src/core/components/stories/course-image.stories.ts deleted file mode 100644 index cd919a6fe..000000000 --- a/src/core/components/stories/course-image.stories.ts +++ /dev/null @@ -1,104 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Meta, moduleMetadata } from '@storybook/angular'; - -import { story } from '@/storybook/utils/helpers'; - -import { CoreCourseImageComponent } from '@components/course-image/course-image'; -import { APP_INITIALIZER } from '@angular/core'; -import { CoreSitesStub } from '@/storybook/stubs/services/sites'; -import { CoreCourseImageListPageComponent } from '@components/stories/components/course-image-list-page/course-image-list-page'; -import { CoreComponentsStorybookModule } from '@components/stories/components/components.module'; -import { CoreCourseImageCardsPageComponent } from '@components/stories/components/course-image-cards-page/course-image-cards-page'; - -interface Args { - type: 'image' | 'geopattern' | 'color'; - fill: boolean; -} - -export default { - title: 'Core/Course Image', - component: CoreCourseImageComponent, - decorators: [ - moduleMetadata({ - imports: [CoreComponentsStorybookModule], - providers: [ - { - provide: APP_INITIALIZER, - multi: true, - useValue: () => { - const site = CoreSitesStub.getRequiredCurrentSite(); - - site.stubWSResponse('tool_mobile_get_config', { - settings: [ - { name: 'core_admin_coursecolor1', value: '#F9B000' }, - { name: 'core_admin_coursecolor2', value: '#EF4B00' }, - { name: 'core_admin_coursecolor3', value: '#4338FB' }, - { name: 'core_admin_coursecolor4', value: '#E142FB' }, - { name: 'core_admin_coursecolor5', value: '#FF0064' }, - { name: 'core_admin_coursecolor6', value: '#FF0F18' }, - { name: 'core_admin_coursecolor7', value: '#039B06' }, - { name: 'core_admin_coursecolor8', value: '#039B88' }, - { name: 'core_admin_coursecolor9', value: '#EF009B' }, - { name: 'core_admin_coursecolor10', value: '#020B6E' }, - ], - warnings: [], - }); - }, - }, - ], - }), - ], - argTypes: { - type: { - control: { - type: 'select', - options: ['image', 'geopattern', 'color'], - }, - }, - }, - args: { - type: 'image', - fill: false, - }, -}; - -const Template = story(({ type, ...args }) => { - const getImageSource = () => { - switch (type) { - case 'image': - return 'https://picsum.photos/500/500'; - case 'geopattern': - return 'assets/storybook/geopattern.svg'; - case 'color': - return undefined; - } - }; - - return { - component: CoreCourseImageComponent, - props: { - ...args, - course: { - id: 1, - courseimage: getImageSource(), - }, - }, - }; -}); - -export const Primary = story(Template); -export const ListPage = story(() => ({ component: CoreCourseImageListPageComponent })); -export const CardsPage = story(() => ({ component: CoreCourseImageCardsPageComponent })); diff --git a/src/core/components/stories/empty-box.stories.ts b/src/core/components/stories/empty-box.stories.ts deleted file mode 100644 index 8b2922fa6..000000000 --- a/src/core/components/stories/empty-box.stories.ts +++ /dev/null @@ -1,79 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Meta, moduleMetadata } from '@storybook/angular'; -import { marked } from 'marked'; - -import { story } from '@/storybook/utils/helpers'; - -import { CoreEmptyBoxComponent } from '@components/empty-box/empty-box'; -import { CoreEmptyBoxWrapperComponent } from './components/empty-box-wrapper/empty-box-wrapper'; -import { CoreEmptyBoxPageComponent } from './components/empty-box-page/empty-box-page'; -import { CoreComponentsStorybookModule } from './components/components.module'; - -interface Args { - icon: string; - content: string; - dimmed: boolean; -} - -export default > { - title: 'Core/Empty Box', - component: CoreEmptyBoxComponent, - decorators: [ - moduleMetadata({ imports: [CoreComponentsStorybookModule] }), - ], - argTypes: { - icon: { - control: { - type: 'select', - options: ['fas-magnifying-glass', 'fas-user', 'fas-check'], - }, - }, - }, - args: { - icon: 'fas-user', - content: 'No users', - dimmed: false, - }, -}; - -const WrapperTemplate = story((args) => ({ - component: CoreEmptyBoxWrapperComponent, - props: { - ...args, - content: marked(args.content), - }, -})); - -const PageTemplate = story((args) => ({ - component: CoreEmptyBoxPageComponent, - props: { - ...args, - content: marked(args.content), - }, -})); - -export const Primary = story(WrapperTemplate); - -export const Example = story(PageTemplate, { - icon: 'fas-magnifying-glass', - content: '**No results for "Test Search"**\n\nCheck for typos or try using different keywords', -}); - -export const DimmedExample = story(PageTemplate, { - icon: 'fas-magnifying-glass', - content: 'What are you searching for?', - dimmed: true, -}); diff --git a/src/core/components/stories/error-info.stories.ts b/src/core/components/stories/error-info.stories.ts deleted file mode 100644 index 9c67a6d13..000000000 --- a/src/core/components/stories/error-info.stories.ts +++ /dev/null @@ -1,50 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Meta, moduleMetadata, Story } from '@storybook/angular'; - -import { story } from '@/storybook/utils/helpers'; -import { StorybookModule } from '@/storybook/storybook.module'; - -import { CoreErrorInfoComponent } from '@components/error-info/error-info'; - -interface Args { - errorCode: string; - errorDetails: string; -} - -export default > { - title: 'Core/Error Info', - component: CoreErrorInfoComponent, - decorators: [ - moduleMetadata({ - declarations: [CoreErrorInfoComponent], - imports: [StorybookModule], - }), - ], - args: { - errorCode: '', - errorDetails: - 'AJAX endpoint not found. ' + - 'This can happen if the Moodle site is too old or it blocks access to this endpoint. ' + - 'The Moodle app only supports Moodle systems 3.5 onwards.', - }, -}; - -const Template: Story = (args) => ({ - component: CoreErrorInfoComponent, - props: args, -}); - -export const Primary = story(Template); diff --git a/src/core/components/stories/sites-list.stories.ts b/src/core/components/stories/sites-list.stories.ts deleted file mode 100644 index 0fa75f7e5..000000000 --- a/src/core/components/stories/sites-list.stories.ts +++ /dev/null @@ -1,78 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Meta, moduleMetadata } from '@storybook/angular'; - -import { story } from '@/storybook/utils/helpers'; -import { CoreSitesListComponent } from '@components/sites-list/sites-list'; -import { CoreSitesListWrapperComponent } from './components/sites-list-wrapper/sites-list-wrapper'; -import { CoreComponentsStorybookModule } from './components/components.module'; - -interface Args { - sitesClickable: boolean; - currentSiteClickable: 'true' | 'false' | 'undefined'; - extraText: 'text' | 'badge' | 'none'; - extraDetails: 'delete-button' | 'badge' | 'none'; -} - -export default > { - title: 'Core/Sites List', - component: CoreSitesListComponent, - decorators: [ - moduleMetadata({ imports: [CoreComponentsStorybookModule] }), - ], - argTypes: { - sitesClickable: { - control: { - type: 'boolean', - }, - }, - currentSiteClickable: { - control: { - type: 'select', - options: ['true', 'false', 'undefined'], - }, - }, - extraText: { - control: { - type: 'select', - options: ['text', 'badge', 'none'], - }, - }, - extraDetails: { - control: { - type: 'select', - options: ['delete-button', 'badge', 'none'], - }, - }, - }, - args: { - sitesClickable: false, - currentSiteClickable: 'undefined', - extraText: 'none', - extraDetails: 'none', - }, -}; - -const Template = story(({ sitesClickable, currentSiteClickable, extraText, extraDetails }) => ({ - component: CoreSitesListWrapperComponent, - props: { - sitesClickable, - currentSiteClickableSelect: currentSiteClickable, - extraText, - extraDetails, - }, -})); - -export const Primary = story(Template); diff --git a/src/core/components/stories/user-avatar.stories.ts b/src/core/components/stories/user-avatar.stories.ts deleted file mode 100644 index 64303d414..000000000 --- a/src/core/components/stories/user-avatar.stories.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Meta, moduleMetadata, Story } from '@storybook/angular'; - -import { story } from '@/storybook/utils/helpers'; -import { StorybookModule } from '@/storybook/storybook.module'; - -import { CoreUserAvatarComponent } from '@components/user-avatar/user-avatar'; - -export default { - title: 'Core/User Avatar', - component: CoreUserAvatarComponent, - decorators: [ - moduleMetadata({ - declarations: [CoreUserAvatarComponent], - imports: [StorybookModule], - }), - ], -}; - -const Template: Story = () => ({ - component: CoreUserAvatarComponent, -}); - -export const Primary = story(Template); diff --git a/src/core/features/search/stories/components/components.module.ts b/src/core/features/search/stories/components/components.module.ts deleted file mode 100644 index 1e59da784..000000000 --- a/src/core/features/search/stories/components/components.module.ts +++ /dev/null @@ -1,37 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { NgModule } from '@angular/core'; -import { StorybookModule } from '@/storybook/storybook.module'; -import { CoreSearchComponentsModule } from '@features/search/components/components.module'; -import { CoreComponentsModule } from '@components/components.module'; -import { CommonModule } from '@angular/common'; -import { - CoreSearchGlobalSearchResultsPageComponent, -} from '@features/search/stories/components/global-search-results-page/global-search-results-page'; -import { CoreSharedModule } from '@/core/shared.module'; - -@NgModule({ - declarations: [ - CoreSearchGlobalSearchResultsPageComponent, - ], - imports: [ - CoreSharedModule, - CommonModule, - StorybookModule, - CoreComponentsModule, - CoreSearchComponentsModule, - ], -}) -export class CoreSearchComponentsStorybookModule {} diff --git a/src/core/features/search/stories/components/global-search-results-page/global-search-results-page.html b/src/core/features/search/stories/components/global-search-results-page/global-search-results-page.html deleted file mode 100644 index eeb81da2c..000000000 --- a/src/core/features/search/stories/components/global-search-results-page/global-search-results-page.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - -

Search Results

-
-
-
- -
- - - - -
-
-
diff --git a/src/core/features/search/stories/components/global-search-results-page/global-search-results-page.ts b/src/core/features/search/stories/components/global-search-results-page/global-search-results-page.ts deleted file mode 100644 index 6154c5d22..000000000 --- a/src/core/features/search/stories/components/global-search-results-page/global-search-results-page.ts +++ /dev/null @@ -1,141 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Component } from '@angular/core'; -import { CoreCourseListItem } from '@features/courses/services/courses'; -import { CoreUserWithAvatar } from '@components/user-avatar/user-avatar'; -import { CoreSearchGlobalSearchResult } from '@features/search/services/global-search'; -import courses from '@/assets/storybook/courses.json'; - -@Component({ - selector: 'core-search-global-search-results-page', - templateUrl: 'global-search-results-page.html', -}) -export class CoreSearchGlobalSearchResultsPageComponent { - - results: CoreSearchGlobalSearchResult[] = [ - { - id: 1, - url: '', - title: 'Activity forum test', - content: 'this is a content test for a forum to see in the search result.', - context: { - courseName: 'Course 102', - userName: 'Stephania Krovalenko', - }, - module: { - name: 'forum', - iconurl: 'assets/img/mod/forum.svg', - area: 'activity', - }, - }, - { - id: 2, - url: '', - title: 'Activity assignment test', - content: 'this is a content test for a forum to see in the search result.', - context: { - courseName: 'Course 102', - }, - module: { - name: 'assign', - iconurl: 'assets/img/mod/assign.svg', - area: '', - }, - }, - { - id: 3, - url: '', - title: 'Course 101', - course: courses[0] as CoreCourseListItem, - }, - { - id: 4, - url: '', - title: 'John the Tester', - user: { - fullname: 'John Doe', - profileimageurl: 'https://placekitten.com/300/300', - } as CoreUserWithAvatar, - }, - { - id: 5, - url: '', - title: 'Search result title', - content: 'this is a content test for a forum to see in the search result.', - context: { - userName: 'Stephania Krovalenko', - }, - module: { - name: 'forum', - iconurl: 'assets/img/mod/forum.svg', - area: 'post', - }, - }, - { - id: 6, - url: '', - title: 'Side block', - context: { - courseName: 'Moodle Site', - }, - component: { - name: 'block_html', - iconurl: 'https://master.mm.moodledemo.net/theme/image.php?theme=boost&component=core&image=e%2Fanchor', - }, - }, - { - id: 7, - url: '', - title: 'Course section', - context: { - courseName: 'Course 101', - }, - component: { - name: 'core_course', - iconurl: 'https://master.mm.moodledemo.net/theme/image.php?theme=boost&component=core&image=i%2Fsection', - }, - }, - { - id: 8, - url: '', - title: 'This item has long text everywhere, so make sure that it looks good anyways. ' + - 'Even if the screen you\'re using is also big, this should still be a problem because this text is *really* long.', - content: 'You would normally see lorem ipsum here, but we decided to just write some gibberish here to make it more ' + - 'real. We all know that lorem ipsum is fabricated text, and even though it serves its purpose, it isn\'t as ' + - 'engaging as some real, hand-crafted text (not sure why this should be engaging, anyways).', - context: { - courseName: 'And it\'s not just the title, either. Other things like the Course title also take more than ' + - 'you would expect in a normal site (or even not so normal).', - userName: 'To top it off, it has a user name as well! What is this madness? Well, at some point you just have to ' + - 'get creative. Honestly, I\'m surprised if you\'re even reading this. Kudos to you for being thorough.', - }, - module: { - name: 'book', - iconurl: 'assets/img/mod/book.svg', - area: '', - }, - }, - ]; - - /** - * Result clicked. - * - * @param title Result title. - */ - resultClicked(title: string): void { - alert(`clicked on ${title}`); - } - -} diff --git a/src/core/features/search/stories/global-search-result.stories.ts b/src/core/features/search/stories/global-search-result.stories.ts deleted file mode 100644 index 8bc43c2bd..000000000 --- a/src/core/features/search/stories/global-search-result.stories.ts +++ /dev/null @@ -1,136 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Meta, moduleMetadata } from '@storybook/angular'; - -import { story } from '@/storybook/utils/helpers'; - -import { CoreSearchGlobalSearchResultComponent } from '@features/search/components/global-search-result/global-search-result'; -import { CoreSearchComponentsStorybookModule } from '@features/search/stories/components/components.module'; -import { - CoreSearchGlobalSearchResultsPageComponent, -} from '@features/search/stories/components/global-search-results-page/global-search-results-page'; -import { APP_INITIALIZER } from '@angular/core'; -import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate'; -import { AddonModForumModuleHandler } from '@addons/mod/forum/services/handlers/module'; -import { AddonModAssignModuleHandler } from '@addons/mod/assign/services/handlers/module'; -import { CoreSearchGlobalSearchResult } from '@features/search/services/global-search'; -import { CoreUserWithAvatar } from '@components/user-avatar/user-avatar'; -import { CoreCourseListItem } from '@features/courses/services/courses'; -import courses from '@/assets/storybook/courses.json'; - -interface Args { - title: string; - content: string; - image: 'course' | 'user' | 'none'; - module: 'forum-activity' | 'forum-post' | 'assign' | 'none'; - courseContext: boolean; - userContext: boolean; - showCourse: boolean; -} - -export default > { - title: 'Core/Search/Global Search Result', - component: CoreSearchGlobalSearchResultComponent, - decorators: [ - moduleMetadata({ - imports: [CoreSearchComponentsStorybookModule], - providers: [ - { - provide: APP_INITIALIZER, - multi: true, - useValue() { - CoreCourseModuleDelegate.registerHandler(AddonModForumModuleHandler.instance); - CoreCourseModuleDelegate.registerHandler(AddonModAssignModuleHandler.instance); - CoreCourseModuleDelegate.updateHandlers(); - }, - }, - ], - }), - ], - argTypes: { - image: { - control: { - type: 'select', - options: ['course', 'user', 'none'], - }, - }, - module: { - control: { - type: 'select', - options: ['forum-activity', 'forum-post', 'assign', 'none'], - }, - }, - }, - args: { - title: 'Result #1', - content: 'This item seems really interesting, maybe you should click through', - image: 'none', - module: 'none', - courseContext: false, - userContext: false, - showCourse: true, - }, - parameters: { - design: { - type: 'figma', - url: 'https://www.figma.com/file/h3E7pkfgyImJPaYmTfnwuF/Global-Search?node-id=118%3A4610', - }, - }, -}; - -const Template = story(({ image, courseContext, userContext, module, showCourse, ...args }) => { - const result: CoreSearchGlobalSearchResult = { - ...args, - id: 1, - url: '', - }; - - if (courseContext || userContext) { - result.context = { - courseName: courseContext ? 'Course 101' : undefined, - userName: userContext ? 'John Doe' : undefined, - }; - } - - if (module !== 'none') { - const name = module.startsWith('forum') ? 'forum' : module; - - result.module = { - name, - iconurl: `assets/img/mod/${name}.svg`, - area: module.startsWith('forum') ? module.substring(6) : '', - }; - } - - switch (image) { - case 'course': - result.course = courses[0] as CoreCourseListItem; - break; - case 'user': - result.user = { - fullname: 'John Doe', - profileimageurl: 'https://placekitten.com/300/300', - } as CoreUserWithAvatar; - break; - } - - return { - component: CoreSearchGlobalSearchResultComponent, - props: { result, showCourse }, - }; -}); - -export const Primary = story(Template); -export const ResultsPage = story(() => ({ component: CoreSearchGlobalSearchResultsPageComponent })); diff --git a/src/storybook/storybook.module.ts b/src/storybook/storybook.module.ts deleted file mode 100644 index b8aa614d7..000000000 --- a/src/storybook/storybook.module.ts +++ /dev/null @@ -1,78 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { IonicModule } from '@ionic/angular'; -import { NgModule, ApplicationInitStatus, APP_INITIALIZER } from '@angular/core'; -import { Observable, of } from 'rxjs'; -import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; - -import englishTranslations from '@/assets/lang/en.json'; -import { CoreApplicationInitStatus } from '@classes/application-init-status'; -import { Translate } from '@singletons'; -import { CoreSitesProviderStub, CoreSitesStub } from '@/storybook/stubs/services/sites'; -import { CoreSitesProvider } from '@services/sites'; -import { CoreDbProviderStub } from '@/storybook/stubs/services/db'; -import { CoreDbProvider } from '@services/db'; -import { CoreFilepoolProviderStub } from '@/storybook/stubs/services/filepool'; -import { CoreFilepoolProvider } from '@services/filepool'; -import { HttpClientStub } from '@/storybook/stubs/services/http'; -import { HttpClient } from '@angular/common/http'; -import { CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications'; -import { CorePushNotificationsProviderStub } from './stubs/services/pushnotifications'; - -// For translate loader. AoT requires an exported function for factories. -export class StaticTranslateLoader extends TranslateLoader { - - getTranslation(): Observable { - return of(englishTranslations); - } - -} - -/** - * Module declaring dependencies for Storybook components. - */ -@NgModule({ - imports: [ - IonicModule.forRoot(), - TranslateModule.forRoot({ - loader: { - provide: TranslateLoader, - useClass: StaticTranslateLoader, - }, - }), - ], - providers: [ - { provide: ApplicationInitStatus, useClass: CoreApplicationInitStatus }, - { provide: CoreSitesProvider, useClass: CoreSitesProviderStub }, - { provide: CoreDbProvider, useClass: CoreDbProviderStub }, - { provide: CoreFilepoolProvider, useClass: CoreFilepoolProviderStub }, - { provide: CorePushNotificationsProvider, useClass: CorePushNotificationsProviderStub }, - { provide: HttpClient, useClass: HttpClientStub }, - { - provide: APP_INITIALIZER, - multi: true, - useValue: () => { - Translate.setDefaultLang('en'); - Translate.use('en'); - CoreSitesStub.stubCurrentSite(); - }, - }, - ], - exports: [ - IonicModule, - TranslateModule, - ], -}) -export class StorybookModule {} diff --git a/src/storybook/stubs/classes/site.ts b/src/storybook/stubs/classes/site.ts deleted file mode 100644 index 4d266913b..000000000 --- a/src/storybook/stubs/classes/site.ts +++ /dev/null @@ -1,59 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CoreSiteWSPreSets, WSObservable } from '@classes/sites/authenticated-site'; -import { CoreSite, CoreSiteConfigResponse } from '@classes/sites/site'; -import { CoreSiteInfo } from '@classes/sites/unauthenticated-site'; -import { of } from 'rxjs'; - -export interface CoreSiteFixture { - id: string; - info: CoreSiteInfo; -} - -export class CoreSiteStub extends CoreSite { - - protected wsStubs: Record = {}; - - constructor (fixture: CoreSiteFixture) { - super(fixture.id, fixture.info.siteurl, '', { info: fixture.info }); - - this.stubWSResponse('tool_mobile_get_config', { - settings: [], - warnings: [], - }); - } - - /** - * @inheritdoc - */ - readObservable(wsFunction: string, data: unknown, preSets?: CoreSiteWSPreSets): WSObservable { - if (wsFunction in this.wsStubs) { - return of(this.wsStubs[wsFunction] as T); - } - - return super.readObservable(wsFunction, data, preSets); - } - - /** - * Prepare as stubbed response for a given WS. - * - * @param wsFunction WS function. - * @param response Response. - */ - stubWSResponse(wsFunction: string, response: T): void { - this.wsStubs[wsFunction] = response; - } - -} diff --git a/src/storybook/stubs/classes/sqlitedb.ts b/src/storybook/stubs/classes/sqlitedb.ts deleted file mode 100644 index f35345951..000000000 --- a/src/storybook/stubs/classes/sqlitedb.ts +++ /dev/null @@ -1,32 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { SQLiteDB } from '@classes/sqlitedb'; -import { SQLiteObject } from '@awesome-cordova-plugins/sqlite/ngx'; - -/** - * SQlite database stub. - */ -export class SQLiteDBStub extends SQLiteDB { - - /** - * @inheritdoc - */ - async createDatabase(): Promise { - return new Proxy({ - executeSql: () => Promise.resolve({ insertId: Math.random().toString() }), - }, {}) as unknown as SQLiteObject; - } - -} diff --git a/src/storybook/stubs/services/db.ts b/src/storybook/stubs/services/db.ts deleted file mode 100644 index e74c90bb2..000000000 --- a/src/storybook/stubs/services/db.ts +++ /dev/null @@ -1,35 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { SQLiteDBStub } from '@/storybook/stubs/classes/sqlitedb'; -import { SQLiteDB } from '@classes/sqlitedb'; -import { CoreDbProvider } from '@services/db'; - -/** - * Database provider stub. - */ -export class CoreDbProviderStub extends CoreDbProvider { - - /** - * @inheritdoc - */ - getDB(name: string, forceNew?: boolean): SQLiteDB { - if (this.dbInstances[name] === undefined || forceNew) { - this.dbInstances[name] = new SQLiteDBStub(name); - } - - return this.dbInstances[name]; - } - -} diff --git a/src/storybook/stubs/services/filepool.ts b/src/storybook/stubs/services/filepool.ts deleted file mode 100644 index d5c604aad..000000000 --- a/src/storybook/stubs/services/filepool.ts +++ /dev/null @@ -1,32 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { makeSingleton } from '@singletons'; -import { CoreFilepoolProvider } from '@services/filepool'; - -/** - * Filepool provider stub. - */ -export class CoreFilepoolProviderStub extends CoreFilepoolProvider { - - /** - * @inheritdoc - */ - async getSrcByUrl(siteId: string, fileUrl: string): Promise { - return fileUrl; - } - -} - -export const CoreFilepoolStub = makeSingleton(CoreFilepoolProvider); diff --git a/src/storybook/stubs/services/http.ts b/src/storybook/stubs/services/http.ts deleted file mode 100644 index 55e397d72..000000000 --- a/src/storybook/stubs/services/http.ts +++ /dev/null @@ -1,38 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { makeSingleton } from '@singletons'; -import { HttpClient, HttpHandler } from '@angular/common/http'; -import { from, Observable } from 'rxjs'; - -/** - * Http service stub. - */ -export class HttpClientStub extends HttpClient { - - constructor() { - super(null as unknown as HttpHandler); - } - - /** - * @inheritdoc - */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get(url: string): Observable { - return from(fetch(url).then(response => response.text())); - } - -} - -export const HttpStub = makeSingleton(HttpClient); diff --git a/src/storybook/stubs/services/pushnotifications.ts b/src/storybook/stubs/services/pushnotifications.ts deleted file mode 100644 index 9d380c32c..000000000 --- a/src/storybook/stubs/services/pushnotifications.ts +++ /dev/null @@ -1,32 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { CorePushNotificationsProvider } from '@features/pushnotifications/services/pushnotifications'; -import { makeSingleton } from '@singletons'; - -/** - * Sites provider stub. - */ -export class CorePushNotificationsProviderStub extends CorePushNotificationsProvider { - - /** - * @inheritdoc - */ - async getSiteCounter(): Promise { - return Math.round(Math.random() * 100); - } - -} - -export const CorePushNotificationsStub = makeSingleton(CorePushNotificationsProvider); diff --git a/src/storybook/stubs/services/sites.ts b/src/storybook/stubs/services/sites.ts deleted file mode 100644 index 52a60f351..000000000 --- a/src/storybook/stubs/services/sites.ts +++ /dev/null @@ -1,86 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { companyLisaSite } from '@/assets/storybook/sites/companylisa'; -import { schoolBarbaraSite } from '@/assets/storybook/sites/schoolbarbara'; -import { schoolJefferySite } from '@/assets/storybook/sites/schooljeffery'; -import { CoreSiteFixture, CoreSiteStub } from '@/storybook/stubs/classes/site'; -import { CoreError } from '@classes/errors/error'; -import { CoreSite } from '@classes/sites/site'; -import { SiteDBEntry } from '@services/database/sites'; -import { CoreSiteBasicInfo, CoreSitesProvider } from '@services/sites'; -import { makeSingleton } from '@singletons'; - -/** - * Sites provider stub. - */ -export class CoreSitesProviderStub extends CoreSitesProvider { - - protected static readonly SITES_FIXTURES = [schoolBarbaraSite, schoolJefferySite, companyLisaSite]; - - /** - * @inheritdoc - */ - getRequiredCurrentSite!: () => CoreSiteStub; - - /** - * @inheritdoc - */ - async getSites(ids?: string[]): Promise { - const sites = CoreSitesProviderStub.SITES_FIXTURES.map(site => ( { - id: site.id, - siteUrl: site.info.siteurl, - info: JSON.stringify(site.info), - token: '', - privateToken: '', - loggedOut: 0, - })); - - return this.siteDBRecordsToBasicInfo(sites, ids); - } - - /** - * @inheritdoc - */ - async getSite(siteId?: string): Promise { - if (!siteId) { - if (this.currentSite) { - return this.currentSite; - } - - throw new CoreError('No current site found.'); - } - - const siteFixture = CoreSitesProviderStub.SITES_FIXTURES.find(site => site.id === siteId); - if (!siteFixture) { - throw new CoreError('SiteId not found.'); - } - - return new CoreSiteStub(siteFixture); - } - - /** - * @inheritdoc - */ - stubCurrentSite(fixture?: CoreSiteFixture): CoreSiteStub { - if (!this.currentSite) { - this.currentSite = new CoreSiteStub(fixture ?? schoolBarbaraSite); - } - - return this.getRequiredCurrentSite(); - } - -} - -export const CoreSitesStub = makeSingleton(CoreSitesProvider); diff --git a/src/storybook/utils/helpers.ts b/src/storybook/utils/helpers.ts deleted file mode 100644 index 5ed8ae7b9..000000000 --- a/src/storybook/utils/helpers.ts +++ /dev/null @@ -1,30 +0,0 @@ -// (C) Copyright 2015 Moodle Pty Ltd. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import { Story } from '@storybook/angular'; - -/** - * Define story. - * - * @param template Story template. - * @param defaultArgs Default arguments. - * @returns Story. - */ -export function story(template: Story, defaultArgs: Partial = {}): Story { - const story = template.bind({}); - - story.args = defaultArgs; - - return story; -} diff --git a/tsconfig.app.json b/tsconfig.app.json index addf75e8c..411dc5e5b 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -25,7 +25,6 @@ "src/**/tests/**", "src/**/stories/**", "src/testing/**", - "src/storybook/**", "src/**/*.test.ts", "src/**/*.stories.*" ] From 6ce688e76ca31eef1ff990583528a8cb9a9d5fa6 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Mon, 18 Mar 2024 09:36:45 +0100 Subject: [PATCH 2/6] MOBILE-4270 core: Report login errors --- src/core/services/sites.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index d16dbb053..00e0e400b 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -499,19 +499,21 @@ export class CoreSitesProvider { try { data = await firstValueFrom(Http.post(loginUrl, params).pipe(timeout(CoreWS.getRequestTimeout()))); } catch (error) { - throw new CoreError( - this.isLoggedIn() - ? Translate.instant('core.siteunavailablehelp', { site: this.currentSite?.siteUrl }) - : Translate.instant('core.sitenotfoundhelp'), - ); + throw this.createCannotConnectLoginError(siteUrl, { + debug: { + code: 'logintokenerror', + details: error.message, + }, + }); } if (data === undefined) { - throw new CoreError( - this.isLoggedIn() - ? Translate.instant('core.siteunavailablehelp', { site: this.currentSite?.siteUrl }) - : Translate.instant('core.sitenotfoundhelp'), - ); + throw this.createCannotConnectLoginError(siteUrl, { + debug: { + code: 'logintokenempty', + details: 'The request to /login/token.php returned an empty response', + }, + }); } if (data.token !== undefined) { From eb0738cb0f442b7140009ff4d396bf3741e052da Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Tue, 7 Mar 2023 11:28:19 +0100 Subject: [PATCH 3/6] MOBILE-4268 core: Require details with error code --- src/core/classes/errors/ajaxwserror.ts | 47 ++++-- src/core/classes/errors/siteerror.ts | 27 +++- src/core/classes/sites/authenticated-site.ts | 6 +- src/core/components/error-info/error-info.ts | 6 +- src/core/features/login/pages/site/site.ts | 17 +- .../features/login/services/login-helper.ts | 5 +- .../features/login/tests/credentials.test.ts | 5 +- src/core/services/sites.ts | 74 ++++++--- src/core/services/utils/dom.ts | 8 +- src/core/services/ws.ts | 146 +++++++++++------- 10 files changed, 219 insertions(+), 122 deletions(-) diff --git a/src/core/classes/errors/ajaxwserror.ts b/src/core/classes/errors/ajaxwserror.ts index 73dcfa37d..53e8b10e6 100644 --- a/src/core/classes/errors/ajaxwserror.ts +++ b/src/core/classes/errors/ajaxwserror.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { CoreSiteError } from '@classes/errors/siteerror'; +import { CoreSiteError, CoreSiteErrorOptions } from '@classes/errors/siteerror'; /** * Error returned by WS. @@ -29,10 +29,7 @@ export class CoreAjaxWSError extends CoreSiteError { // eslint-disable-next-line @typescript-eslint/no-explicit-any constructor(error: any, available?: number) { - super({ - message: error.message || error.error, - errorcode: error.errorcode, - }); + super(getErrorOptions(error)); this.exception = error.exception; this.warningcode = error.warningcode; @@ -40,15 +37,37 @@ export class CoreAjaxWSError extends CoreSiteError { this.moreinfourl = error.moreinfourl; this.debuginfo = error.debuginfo; this.backtrace = error.backtrace; - - this.available = available; - if (this.available === undefined) { - if (this.errorcode) { - this.available = this.errorcode == 'invalidrecord' ? -1 : 1; - } else { - this.available = 0; - } - } + this.available = available ?? ( + this.debug + ? (this.debug.code == 'invalidrecord' ? -1 : 1) + : 0 + ); } } + +/** + * Get error options from unknown error instance. + * + * @param error The error. + * @returns Options + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function getErrorOptions(error: any): CoreSiteErrorOptions { + const options: CoreSiteErrorOptions = { + message: error.message || error.error, + }; + + if ('debug' in error) { + options.debug = error.debug; + } + + if ('errorcode' in error) { + options.debug = { + code: error.errorcode, + details: error.message || error.error, + }; + } + + return options; +} diff --git a/src/core/classes/errors/siteerror.ts b/src/core/classes/errors/siteerror.ts index bb31c41b6..3eed531e6 100644 --- a/src/core/classes/errors/siteerror.ts +++ b/src/core/classes/errors/siteerror.ts @@ -20,24 +20,39 @@ import { CoreUserSupportConfig } from '@features/user/classes/support/support-co */ export class CoreSiteError extends CoreError { - errorcode?: string; - errorDetails?: string; + debug?: CoreSiteErrorDebug; supportConfig?: CoreUserSupportConfig; constructor(options: CoreSiteErrorOptions) { super(options.message); - this.errorcode = options.errorcode; - this.errorDetails = options.errorDetails; + this.debug = options.debug; this.supportConfig = options.supportConfig; } + /** + * @deprecated This getter should not be called directly, but it's defined for backwards compatibility with many + * parts of the code that type errors as any and use it. We cannot rename those because the errors could also be + * CoreWSError instances which do have an "errorcode" property. + * + * @returns error code. + */ + get errorcode(): string | undefined { + return this.debug?.code; + } + } +export type CoreSiteErrorDebug = { + code: string; // Technical error code useful for technical assistance. + details: string; // Technical error details useful for technical assistance. +}; + export type CoreSiteErrorOptions = { message: string; - errorcode?: string; // Technical error code useful for technical assistance. - errorDetails?: string; // Technical error details useful for technical assistance. + + // Debugging information. + debug?: CoreSiteErrorDebug; // Configuration to use to contact site support. If this attribute is present, it means // that the error warrants contacting support. diff --git a/src/core/classes/sites/authenticated-site.ts b/src/core/classes/sites/authenticated-site.ts index 7769d479f..a71f28237 100644 --- a/src/core/classes/sites/authenticated-site.ts +++ b/src/core/classes/sites/authenticated-site.ts @@ -943,8 +943,10 @@ export class CoreAuthenticatedSite extends CoreUnauthenticatedSite { throw new CoreSiteError({ supportConfig: new CoreUserAuthenticatedSupportConfig(this), message: Translate.instant('core.siteunavailablehelp', { site: this.siteUrl }), - errorcode: 'invalidresponse', - errorDetails: Translate.instant('core.errorinvalidresponse', { method: 'tool_mobile_call_external_functions' }), + debug: { + code: 'invalidresponse', + details: Translate.instant('core.errorinvalidresponse', { method: 'tool_mobile_call_external_functions' }), + }, }); } diff --git a/src/core/components/error-info/error-info.ts b/src/core/components/error-info/error-info.ts index 4104e7f3e..31666e599 100644 --- a/src/core/components/error-info/error-info.ts +++ b/src/core/components/error-info/error-info.ts @@ -36,7 +36,7 @@ export class CoreErrorInfoComponent implements OnInit, OnChanges { * @param errorCode Error code. * @returns Component HTML. */ - static render(errorDetails: string, errorCode?: string): string { + static render(errorDetails: string, errorCode: string): string { const toggleId = CoreForms.uniqueId('error-info-toggle'); const errorCodeLabel = Translate.instant('core.errorcode', { errorCode }); const hideDetailsLabel = Translate.instant('core.errordetailshide'); @@ -45,7 +45,7 @@ export class CoreErrorInfoComponent implements OnInit, OnChanges { return `
- ${errorCode ? `
${errorCodeLabel}
` : ''} +
${errorCodeLabel}

${errorDetails}

@@ -64,7 +64,7 @@ export class CoreErrorInfoComponent implements OnInit, OnChanges { } @Input() errorDetails!: string; - @Input() errorCode?: string; + @Input() errorCode!: string; constructor(private element: ElementRef) {} diff --git a/src/core/features/login/pages/site/site.ts b/src/core/features/login/pages/site/site.ts index cbca54a66..f30d0f4c8 100644 --- a/src/core/features/login/pages/site/site.ts +++ b/src/core/features/login/pages/site/site.ts @@ -38,7 +38,7 @@ import { CoreCustomURLSchemes, CoreCustomURLSchemesHandleError } from '@services import { CoreTextUtils } from '@services/utils/text'; import { CoreForms } from '@singletons/form'; import { AlertButton } from '@ionic/core'; -import { CoreSiteError } from '@classes/errors/siteerror'; +import { CoreSiteError, CoreSiteErrorDebug } from '@classes/errors/siteerror'; import { CoreUserSupport } from '@features/user/services/support'; import { CoreErrorInfoComponent } from '@components/error-info/error-info'; import { CoreUserSupportConfig } from '@features/user/classes/support/support-config'; @@ -408,21 +408,19 @@ export class CoreLoginSitePage implements OnInit { let siteExists = false; let supportConfig: CoreUserSupportConfig | undefined = undefined; let errorTitle: string | undefined; - let errorDetails: string | undefined; - let errorCode: string | undefined; + let debug: CoreSiteErrorDebug | undefined; if (error instanceof CoreSiteError) { supportConfig = error.supportConfig; - errorDetails = error.errorDetails; - errorCode = error.errorcode; siteExists = supportConfig instanceof CoreUserGuestSupportConfig; + debug = error.debug; } if (error instanceof CoreLoginError) { errorTitle = error.title; } - if (errorDetails) { + if (debug) { errorMessage = `

${errorMessage}

`; } @@ -438,7 +436,7 @@ export class CoreLoginSitePage implements OnInit { handler: () => CoreUserSupport.contact({ supportConfig: alertSupportConfig, subject: Translate.instant('core.cannotconnect'), - message: `Error: ${errorCode}\n\n${errorDetails}`, + message: `Error: ${debug?.code}\n\n${debug?.details}`, }), } : ( @@ -458,11 +456,10 @@ export class CoreLoginSitePage implements OnInit { buttons: buttons as AlertButton[], }); - if (errorDetails) { - // Avoid sanitizing JS. + if (debug) { const containerElement = alertElement.querySelector('.core-error-info-container'); if (containerElement) { - containerElement.innerHTML = CoreErrorInfoComponent.render(errorDetails, errorCode); + containerElement.innerHTML = CoreErrorInfoComponent.render(debug.details, debug.code); } } } diff --git a/src/core/features/login/services/login-helper.ts b/src/core/features/login/services/login-helper.ts index 98fb1c72c..44ca38f7a 100644 --- a/src/core/features/login/services/login-helper.ts +++ b/src/core/features/login/services/login-helper.ts @@ -39,7 +39,6 @@ import { CorePushNotifications } from '@features/pushnotifications/services/push import { CorePath } from '@singletons/path'; import { CorePromisedValue } from '@classes/promised-value'; import { SafeHtml } from '@angular/platform-browser'; -import { CoreLoginError } from '@classes/errors/loginerror'; import { CoreSettingsHelper } from '@features/settings/services/settings-helper'; import { CoreSiteIdentityProvider, @@ -916,8 +915,8 @@ export class CoreLoginHelperProvider { /** * Show a modal warning that the credentials introduced were not correct. */ - protected showInvalidLoginModal(error: CoreLoginError): void { - CoreDomUtils.showErrorModal(error.errorDetails ?? error.message); + protected showInvalidLoginModal(error: CoreWSError): void { + CoreDomUtils.showErrorModal(error.message); } /** diff --git a/src/core/features/login/tests/credentials.test.ts b/src/core/features/login/tests/credentials.test.ts index e4ca21f81..3e8d83fbd 100644 --- a/src/core/features/login/tests/credentials.test.ts +++ b/src/core/features/login/tests/credentials.test.ts @@ -124,7 +124,10 @@ describe('Credentials page', () => { getUserToken: () => { throw new CoreLoginError({ message: '', - errorcode: 'invalidlogin', + debug: { + code: 'invalidlogin', + details: 'Invalid login', + }, }); }, checkSite: async () => (siteCheck), diff --git a/src/core/services/sites.ts b/src/core/services/sites.ts index 00e0e400b..09a27bafd 100644 --- a/src/core/services/sites.ts +++ b/src/core/services/sites.ts @@ -66,6 +66,7 @@ import { CoreSiteInfo, CoreSiteInfoResponse, CoreSitePublicConfigResponse } from import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site'; import { firstValueFrom } from 'rxjs'; import { CoreHTMLClasses } from '@singletons/html-classes'; +import { CoreSiteErrorDebug } from '@classes/errors/siteerror'; export const CORE_SITE_SCHEMAS = new InjectionToken('CORE_SITE_SCHEMAS'); export const CORE_SITE_CURRENT_SITE_ID_CONFIG = 'current_site_id'; @@ -362,18 +363,22 @@ export class CoreSitesProvider { if (!config.enablewebservices) { throw this.createCannotConnectLoginError(config.httpswwwroot || config.wwwroot, { supportConfig: new CoreUserGuestSupportConfig(temporarySite, config), - errorcode: 'webservicesnotenabled', - errorDetails: Translate.instant('core.login.webservicesnotenabled'), critical: true, + debug: { + code: 'webservicesnotenabled', + details: Translate.instant('core.login.webservicesnotenabled'), + }, }); } if (!config.enablemobilewebservice) { throw this.createCannotConnectLoginError(config.httpswwwroot || config.wwwroot, { supportConfig: new CoreUserGuestSupportConfig(temporarySite, config), - errorcode: 'mobileservicesnotenabled', - errorDetails: Translate.instant('core.login.mobileservicesnotenabled'), critical: true, + debug: { + code: 'mobileservicesnotenabled', + details: Translate.instant('core.login.mobileservicesnotenabled'), + }, }); } @@ -421,14 +426,16 @@ export class CoreSitesProvider { siteUrl: string, error: CoreError | CoreAjaxError | CoreAjaxWSError, ): Promise { - if (error instanceof CoreAjaxError || !('errorcode' in error)) { + if (error instanceof CoreAjaxError || (!('debug' in error) && !('errorcode' in error))) { // The WS didn't return data, probably cannot connect. return new CoreLoginError({ title: Translate.instant('core.cannotconnect'), message: Translate.instant('core.siteunavailablehelp', { site: siteUrl }), - errorcode: 'publicconfigfailed', - errorDetails: error.message || '', critical: false, // Allow fallback to http if siteUrl uses https. + debug: { + code: 'publicconfigfailed', + details: error.message || 'Failed getting public config', + }, }); } @@ -437,28 +444,31 @@ export class CoreSitesProvider { critical: true, title: Translate.instant('core.cannotconnect'), message: Translate.instant('core.siteunavailablehelp', { site: siteUrl }), - errorcode: error.errorcode, supportConfig: error.supportConfig, - errorDetails: error.errorDetails ?? error.message, + debug: error.debug, }; - if (error.errorcode === 'codingerror') { + if (error.debug?.code === 'codingerror') { // This could be caused by a redirect. Check if it's the case. const redirect = await CoreUtils.checkRedirect(siteUrl); options.message = Translate.instant('core.siteunavailablehelp', { site: siteUrl }); if (redirect) { - options.errorcode = 'sitehasredirect'; - options.errorDetails = Translate.instant('core.login.sitehasredirect'); options.critical = false; // Keep checking fallback URLs. + options.debug = { + code: 'sitehasredirect', + details: Translate.instant('core.login.sitehasredirect'), + }; } - } else if (error.errorcode === 'invalidrecord') { + } else if (error.debug?.code === 'invalidrecord') { // WebService not found, site not supported. options.message = Translate.instant('core.siteunavailablehelp', { site: siteUrl }); - options.errorcode = 'invalidmoodleversion'; - options.errorDetails = Translate.instant('core.login.invalidmoodleversion', { $a: CoreSite.MINIMUM_MOODLE_VERSION }); - } else if (error.errorcode === 'redirecterrordetected') { + options.debug = { + code: 'invalidmoodleversion', + details: Translate.instant('core.login.invalidmoodleversion', { $a: CoreSite.MINIMUM_MOODLE_VERSION }), + }; + } else if (error.debug?.code === 'redirecterrordetected') { options.critical = false; // Keep checking fallback URLs. } @@ -538,16 +548,20 @@ export class CoreSitesProvider { if (redirect) { throw this.createCannotConnectLoginError(siteUrl, { supportConfig: await CoreUserGuestSupportConfig.forSite(siteUrl), - errorcode: 'sitehasredirect', - errorDetails: Translate.instant('core.login.sitehasredirect'), + debug: { + code: 'sitehasredirect', + details: Translate.instant('core.login.sitehasredirect'), + }, }); } } throw this.createCannotConnectLoginError(siteUrl, { supportConfig: await CoreUserGuestSupportConfig.forSite(siteUrl), - errorcode: data.errorcode, - errorDetails: data.error, + debug: { + code: data.errorcode ?? 'loginfailed', + details: data.error ?? 'Could not get a user token in /login/token.php', + }, }); } @@ -659,23 +673,33 @@ export class CoreSitesProvider { * @returns A promise rejected with the error info. */ protected async treatInvalidAppVersion(result: number, siteId?: string): Promise { - let errorCode: string | undefined; + let debug: CoreSiteErrorDebug | undefined; let errorKey: string | undefined; let translateParams = {}; switch (result) { case CoreSitesProvider.MOODLE_APP: errorKey = 'core.login.connecttomoodleapp'; - errorCode = 'connecttomoodleapp'; + debug = { + code: 'connecttomoodleapp', + details: 'Cannot connect to app', + }; break; case CoreSitesProvider.WORKPLACE_APP: errorKey = 'core.login.connecttoworkplaceapp'; - errorCode = 'connecttoworkplaceapp'; + debug = { + code: 'connecttoworkplaceapp', + details: 'Cannot connect to app', + }; break; default: - errorCode = 'invalidmoodleversion'; errorKey = 'core.login.invalidmoodleversion'; translateParams = { $a: CoreSite.MINIMUM_MOODLE_VERSION }; + debug = { + code: 'invalidmoodleversion', + details: 'Cannot connect to app', + }; + break; } if (siteId) { @@ -683,8 +707,8 @@ export class CoreSitesProvider { } throw new CoreLoginError({ + debug, message: Translate.instant(errorKey, translateParams), - errorcode: errorCode, loggedOut: true, }); } diff --git a/src/core/services/utils/dom.ts b/src/core/services/utils/dom.ts index bb9864992..5eaac34c7 100644 --- a/src/core/services/utils/dom.ts +++ b/src/core/services/utils/dom.ts @@ -1037,7 +1037,7 @@ export class CoreDomUtilsProvider { if (typeof error !== 'string' && 'buttons' in error && typeof error.buttons !== 'undefined') { alertOptions.buttons = error.buttons; } else if (error instanceof CoreSiteError) { - if (error.errorDetails) { + if (error.debug) { alertOptions.message = `

${alertOptions.message}

`; } @@ -1051,7 +1051,7 @@ export class CoreDomUtilsProvider { handler: () => CoreUserSupport.contact({ supportConfig, subject: alertOptions.header, - message: `${error.errorcode}\n\n${error.errorDetails}`, + message: `${error.debug?.code}\n\n${error.debug?.details}`, }), }); } @@ -1061,11 +1061,11 @@ export class CoreDomUtilsProvider { const alertElement = await this.showAlertWithOptions(alertOptions, autocloseTime); - if (error instanceof CoreSiteError && error.errorDetails) { + if (error instanceof CoreSiteError && error.debug) { const containerElement = alertElement.querySelector('.core-error-info-container'); if (containerElement) { - containerElement.innerHTML = CoreErrorInfoComponent.render(error.errorDetails, error.errorcode); + containerElement.innerHTML = CoreErrorInfoComponent.render(error.debug.details, error.debug.code); } } diff --git a/src/core/services/ws.ts b/src/core/services/ws.ts index b1084347e..004da122c 100644 --- a/src/core/services/ws.ts +++ b/src/core/services/ws.ts @@ -497,10 +497,12 @@ export class CoreWSProvider { throw new CoreAjaxError({ message, supportConfig: await CoreUserGuestSupportConfig.forSite(preSets.siteUrl), - errorcode: 'invalidresponse', - errorDetails: Translate.instant('core.serverconnection', { - details: Translate.instant('core.errorinvalidresponse', { method }), - }), + debug: { + code: 'invalidresponse', + details: Translate.instant('core.serverconnection', { + details: Translate.instant('core.errorinvalidresponse', { method }), + }), + }, }); } else if (data.error) { throw new CoreAjaxWSError(data); @@ -527,54 +529,72 @@ export class CoreWSProvider { if (CorePlatform.isMobile()) { switch (data.status) { case NativeHttp.ErrorCode.SSL_EXCEPTION: - options.errorcode = 'invalidcertificate'; - options.errorDetails = Translate.instant('core.certificaterror', { - details: CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Invalid certificate', - }); + options.debug = { + code: 'invalidcertificate', + details: Translate.instant('core.certificaterror', { + details: CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Invalid certificate', + }), + }; break; case NativeHttp.ErrorCode.SERVER_NOT_FOUND: - options.errorcode = 'servernotfound'; - options.errorDetails = CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Server could not be found'; + options.debug = { + code: 'servernotfound', + details: CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Server could not be found', + }; break; case NativeHttp.ErrorCode.TIMEOUT: - options.errorcode = 'requesttimeout'; - options.errorDetails = CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Request timed out'; + options.debug = { + code: 'requesttimeout', + details: CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Request timed out', + }; break; case NativeHttp.ErrorCode.UNSUPPORTED_URL: - options.errorcode = 'unsupportedurl'; - options.errorDetails = CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Url not supported'; + options.debug = { + code: 'unsupportedurl', + details: CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Url not supported', + }; break; case NativeHttp.ErrorCode.NOT_CONNECTED: - options.errorcode = 'connectionerror'; - options.errorDetails = CoreTextUtils.getErrorMessageFromError(data.error) - ?? 'Connection error, is network available?'; + options.debug = { + code: 'connectionerror', + details: CoreTextUtils.getErrorMessageFromError(data.error) + ?? 'Connection error, is network available?', + }; break; case NativeHttp.ErrorCode.ABORTED: - options.errorcode = 'requestaborted'; - options.errorDetails = CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Request aborted'; + options.debug = { + code: 'requestaborted', + details: CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Request aborted', + }; break; case NativeHttp.ErrorCode.POST_PROCESSING_FAILED: - options.errorcode = 'requestprocessingfailed'; - options.errorDetails = CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Request processing failed'; + options.debug = { + code: 'requestprocessingfailed', + details: CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Request processing failed', + }; break; } } - if (!options.errorcode) { + if (!options.debug) { switch (data.status) { case 404: - options.errorcode = 'endpointnotfound'; - options.errorDetails = Translate.instant('core.ajaxendpointnotfound', { - $a: CoreSite.MINIMUM_MOODLE_VERSION, - }); + options.debug = { + code: 'endpointnotfound', + details: Translate.instant('core.ajaxendpointnotfound', { + $a: CoreSite.MINIMUM_MOODLE_VERSION, + }), + }; break; default: { const details = CoreTextUtils.getErrorMessageFromError(data.error) ?? 'Unknown error'; - options.errorcode = 'serverconnectionajax'; - options.errorDetails = Translate.instant('core.serverconnection', { - details: `[Response status code: ${data.status}] ${details}`, - }); + options.debug = { + code: 'serverconnectionajax', + details: Translate.instant('core.serverconnection', { + details: `[Response status code: ${data.status}] ${details}`, + }), + }; } break; } @@ -716,10 +736,12 @@ export class CoreWSProvider { if (!data) { throw await this.createCannotConnectSiteError(preSets.siteUrl, { - errorcode: 'serverconnectionpost', - errorDetails: Translate.instant('core.serverconnection', { - details: Translate.instant('core.errorinvalidresponse', { method }), - }), + debug: { + code: 'serverconnectionpost', + details: Translate.instant('core.serverconnection', { + details: Translate.instant('core.errorinvalidresponse', { method }), + }), + }, }); } else if (typeof data !== typeExpected) { // If responseType is text an string will be returned, parse before returning. @@ -730,8 +752,10 @@ export class CoreWSProvider { this.logger.warn(`Response expected type "${typeExpected}" cannot be parsed to number`); throw await this.createCannotConnectSiteError(preSets.siteUrl, { - errorcode: 'invalidresponse', - errorDetails: Translate.instant('core.errorinvalidresponse', { method }), + debug: { + code: 'invalidresponse', + details: Translate.instant('core.errorinvalidresponse', { method }), + }, }); } } else if (typeExpected === 'boolean') { @@ -743,24 +767,30 @@ export class CoreWSProvider { this.logger.warn(`Response expected type "${typeExpected}" is not true or false`); throw await this.createCannotConnectSiteError(preSets.siteUrl, { - errorcode: 'invalidresponse', - errorDetails: Translate.instant('core.errorinvalidresponse', { method }), + debug: { + code: 'invalidresponse', + details: Translate.instant('core.errorinvalidresponse', { method }), + }, }); } } else { this.logger.warn('Response of type "' + typeof data + `" received, expecting "${typeExpected}"`); throw await this.createCannotConnectSiteError(preSets.siteUrl, { - errorcode: 'invalidresponse', - errorDetails: Translate.instant('core.errorinvalidresponse', { method }), + debug: { + code: 'invalidresponse', + details: Translate.instant('core.errorinvalidresponse', { method }), + }, }); } } else { this.logger.warn('Response of type "' + typeof data + `" received, expecting "${typeExpected}"`); throw await this.createCannotConnectSiteError(preSets.siteUrl, { - errorcode: 'invalidresponse', - errorDetails: Translate.instant('core.errorinvalidresponse', { method }), + debug: { + code: 'invalidresponse', + details: Translate.instant('core.errorinvalidresponse', { method }), + }, }); } } @@ -803,10 +833,12 @@ export class CoreWSProvider { return retryPromise; } else if (error.status === -2) { throw await this.createCannotConnectSiteError(preSets.siteUrl, { - errorcode: 'invalidcertificate', - errorDetails: Translate.instant('core.certificaterror', { - details: CoreTextUtils.getErrorMessageFromError(error) ?? 'Unknown error', - }), + debug: { + code: 'invalidcertificate', + details: Translate.instant('core.certificaterror', { + details: CoreTextUtils.getErrorMessageFromError(error) ?? 'Unknown error', + }), + }, }); } else if (error.status > 0) { throw this.createHttpError(error, error.status); @@ -1033,24 +1065,30 @@ export class CoreWSProvider { if (data === null) { throw await this.createCannotConnectSiteError(preSets.siteUrl, { - errorcode: 'invalidresponse', - errorDetails: Translate.instant('core.errorinvalidresponse', { method: 'upload.php' }), + debug: { + code: 'invalidresponse', + details: Translate.instant('core.errorinvalidresponse', { method: 'upload.php' }), + }, }); } if (!data) { throw await this.createCannotConnectSiteError(preSets.siteUrl, { - errorcode: 'serverconnectionupload', - errorDetails: Translate.instant('core.serverconnection', { - details: Translate.instant('core.errorinvalidresponse', { method: 'upload.php' }), - }), + debug: { + code: 'serverconnectionupload', + details: Translate.instant('core.serverconnection', { + details: Translate.instant('core.errorinvalidresponse', { method: 'upload.php' }), + }), + }, }); } else if (typeof data != 'object') { this.logger.warn('Upload file: Response of type "' + typeof data + '" received, expecting "object"'); throw await this.createCannotConnectSiteError(preSets.siteUrl, { - errorcode: 'invalidresponse', - errorDetails: Translate.instant('core.errorinvalidresponse', { method: 'upload.php' }), + debug: { + code: 'invalidresponse', + details: Translate.instant('core.errorinvalidresponse', { method: 'upload.php' }), + }, }); } From e1be8caace8f32abfbd5d1c7bbeec765c93ef037 Mon Sep 17 00:00:00 2001 From: Noel De Martin Date: Mon, 18 Mar 2024 09:30:14 +0100 Subject: [PATCH 4/6] MOBILE-4268 core: Rename core-error-accordion --- .../core-error-accordion.html} | 2 +- .../error-accordion.scss} | 24 ++++++++--------- .../error-accordion.ts} | 26 +++++++++---------- src/core/features/login/pages/site/site.ts | 9 ++++--- src/core/services/utils/dom.ts | 8 +++--- src/theme/theme.scss | 2 +- 6 files changed, 36 insertions(+), 35 deletions(-) rename src/core/components/{error-info/core-error-info.html => error-accordion/core-error-accordion.html} (75%) rename src/core/components/{error-info/error-info.scss => error-accordion/error-accordion.scss} (66%) rename src/core/components/{error-info/error-info.ts => error-accordion/error-accordion.ts} (73%) diff --git a/src/core/components/error-info/core-error-info.html b/src/core/components/error-accordion/core-error-accordion.html similarity index 75% rename from src/core/components/error-info/core-error-info.html rename to src/core/components/error-accordion/core-error-accordion.html index 2808ecb68..47704faba 100644 --- a/src/core/components/error-info/core-error-info.html +++ b/src/core/components/error-accordion/core-error-accordion.html @@ -2,5 +2,5 @@ The markup for this component is rendered dynamically using the static render() method instead of using Angular's engine. The reason for using this approach is that this allows injecting this component into HTML directly, rather than requiring Angular - to control its lifecycle. + to control its lifecycle. This is necessary, for example, to render within alerts. --> diff --git a/src/core/components/error-info/error-info.scss b/src/core/components/error-accordion/error-accordion.scss similarity index 66% rename from src/core/components/error-info/error-info.scss rename to src/core/components/error-accordion/error-accordion.scss index a0723f21f..a16ec2ac9 100644 --- a/src/core/components/error-info/error-info.scss +++ b/src/core/components/error-accordion/error-accordion.scss @@ -1,4 +1,4 @@ -.core-error-info { +.core-error-accordion { background: var(--gray-200); border-radius: var(--radius-xs); font-size: var(--body-font-size-sm); @@ -12,26 +12,26 @@ margin-bottom: 0; } - .core-error-info--code { + .core-error-accordion--code { padding: var(--spacing-2) var(--spacing-2) 0 var(--spacing-2); font-size: var(--body-font-size-md); } - .core-error-info--details p { + .core-error-accordion--details p { padding: var(--spacing-2) var(--spacing-2) 0 var(--spacing-2); color: var(--gray-500); } - .core-error-info--checkbox { + .core-error-accordion--checkbox { display: none; - & + .core-error-info--details, - & + .core-error-info--code + .core-error-info--details { + & + .core-error-accordion--details, + & + .core-error-accordion--code + .core-error-accordion--details { max-height: 0; overflow: hidden; transition: max-height 600ms ease-in-out; - & + .core-error-info--toggle { + & + .core-error-accordion--toggle { display: flex; padding: var(--spacing-2); min-height: var(--a11y-min-target-size); @@ -48,7 +48,7 @@ width: 11px; } - .core-error-info--hide-content { + .core-error-accordion--hide-content { display: none; } @@ -56,15 +56,15 @@ } - &:checked + .core-error-info--details, - &:checked + .core-error-info--code + .core-error-info--details { + &:checked + .core-error-accordion--details, + &:checked + .core-error-accordion--code + .core-error-accordion--details { max-height: 110px; - & + .core-error-info--toggle .core-error-info--hide-content { + & + .core-error-accordion--toggle .core-error-accordion--hide-content { display: flex; } - & + .core-error-info--toggle .core-error-info--show-content { + & + .core-error-accordion--toggle .core-error-accordion--show-content { display: none; } diff --git a/src/core/components/error-info/error-info.ts b/src/core/components/error-accordion/error-accordion.ts similarity index 73% rename from src/core/components/error-info/error-info.ts rename to src/core/components/error-accordion/error-accordion.ts index 31666e599..b2f31d845 100644 --- a/src/core/components/error-info/error-info.ts +++ b/src/core/components/error-accordion/error-accordion.ts @@ -23,11 +23,11 @@ import { CoreForms } from '@singletons/form'; * it can be rendered using the static render() method to get the raw HTML. */ @Component({ - selector: 'core-error-info', - templateUrl: 'core-error-info.html', - styleUrls: ['error-info.scss'], + selector: 'core-error-accordion', + templateUrl: 'core-error-accordion.html', + styleUrls: ['error-accordion.scss'], }) -export class CoreErrorInfoComponent implements OnInit, OnChanges { +export class CoreErrorAccordionComponent implements OnInit, OnChanges { /** * Render an instance of the component into an HTML string. @@ -37,24 +37,24 @@ export class CoreErrorInfoComponent implements OnInit, OnChanges { * @returns Component HTML. */ static render(errorDetails: string, errorCode: string): string { - const toggleId = CoreForms.uniqueId('error-info-toggle'); + const toggleId = CoreForms.uniqueId('error-accordion-toggle'); const errorCodeLabel = Translate.instant('core.errorcode', { errorCode }); const hideDetailsLabel = Translate.instant('core.errordetailshide'); const showDetailsLabel = Translate.instant('core.errordetailsshow'); return ` -
- -
${errorCodeLabel}
-
+
+ +
${errorCodeLabel}
+

${errorDetails}

-