Merge pull request #3982 from NoelDeMartin/MOBILE-4268
MOBILE-4268: Apply design system to Error Accordionmain
commit
776e3fa481
|
@ -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'],
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
};
|
|
@ -1,7 +0,0 @@
|
|||
storybook-dynamic-app-root {
|
||||
color: var(--ion-text-color);
|
||||
}
|
||||
|
||||
.core-error-info {
|
||||
max-width: 300px;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"include": [
|
||||
"../src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"../src/**/tests/**",
|
||||
"../src/testing/**",
|
||||
"../src/**/*.test.ts"
|
||||
]
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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."}]
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 14 KiB |
|
@ -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: [],
|
||||
},
|
||||
};
|
|
@ -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: [],
|
||||
},
|
||||
};
|
|
@ -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: [],
|
||||
},
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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' }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
|
@ -1,75 +0,0 @@
|
|||
.core-error-info {
|
||||
background: var(--gray-200);
|
||||
border-radius: var(--radius-xs);
|
||||
font-size: var(--body-font-size-sm);
|
||||
color: var(--gray-900);
|
||||
|
||||
p:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.core-error-info--code {
|
||||
padding: var(--spacing-2) var(--spacing-2) 0 var(--spacing-2);
|
||||
font-size: var(--body-font-size-md);
|
||||
}
|
||||
|
||||
.core-error-info--details p {
|
||||
padding: var(--spacing-2) var(--spacing-2) 0 var(--spacing-2);
|
||||
color: var(--gray-500);
|
||||
}
|
||||
|
||||
.core-error-info--checkbox {
|
||||
display: none;
|
||||
|
||||
& + .core-error-info--details,
|
||||
& + .core-error-info--code + .core-error-info--details {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 600ms ease-in-out;
|
||||
|
||||
& + .core-error-info--toggle {
|
||||
display: flex;
|
||||
padding: var(--spacing-2);
|
||||
min-height: var(--a11y-min-target-size);
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: currentColor;
|
||||
width: 11px;
|
||||
}
|
||||
|
||||
.core-error-info--hide-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&:checked + .core-error-info--details,
|
||||
&:checked + .core-error-info--code + .core-error-info--details {
|
||||
max-height: 110px;
|
||||
|
||||
& + .core-error-info--toggle .core-error-info--hide-content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
& + .core-error-info--toggle .core-error-info--show-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,92 +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, ElementRef, Input, OnChanges, OnInit } from '@angular/core';
|
||||
import { Translate } from '@singletons';
|
||||
import { CoreForms } from '@singletons/form';
|
||||
|
||||
/**
|
||||
* Component to show error details.
|
||||
*
|
||||
* Given that this component has to be injected dynamically in some situations (for example, error alerts),
|
||||
* 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'],
|
||||
})
|
||||
export class CoreErrorInfoComponent implements OnInit, OnChanges {
|
||||
|
||||
/**
|
||||
* Render an instance of the component into an HTML string.
|
||||
*
|
||||
* @param errorDetails Error details.
|
||||
* @param errorCode Error code.
|
||||
* @returns Component HTML.
|
||||
*/
|
||||
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');
|
||||
const showDetailsLabel = Translate.instant('core.errordetailsshow');
|
||||
|
||||
return `
|
||||
<div class="core-error-info">
|
||||
<input id="${toggleId}" type="checkbox" class="core-error-info--checkbox" />
|
||||
${errorCode ? `<div class="core-error-info--code"><strong>${errorCodeLabel}</strong></div>` : ''}
|
||||
<div class="core-error-info--details">
|
||||
<p>${errorDetails}</p>
|
||||
</div>
|
||||
<label for="${toggleId}" class="core-error-info--toggle" aria-hidden="true">
|
||||
<span class="core-error-info--hide-content">
|
||||
${hideDetailsLabel}
|
||||
<ion-icon name="chevron-up" />
|
||||
</span>
|
||||
<span class="core-error-info--show-content">
|
||||
${showDetailsLabel}
|
||||
<ion-icon name="chevron-down" />
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
@Input() errorDetails!: string;
|
||||
@Input() errorCode?: string;
|
||||
|
||||
constructor(private element: ElementRef) {}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnChanges(): void {
|
||||
this.render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render component html in the element created by Angular.
|
||||
*/
|
||||
private render(): void {
|
||||
this.element.nativeElement.innerHTML = CoreErrorInfoComponent.render(this.errorDetails, this.errorCode);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 {}
|
|
@ -1,22 +0,0 @@
|
|||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
<h1>Course Cards</h1>
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-card *ngFor="let course of courses">
|
||||
<div class="course-image-wrapper">
|
||||
<core-course-image [course]="course" [fill]="true" />
|
||||
</div>
|
||||
<ion-card-header>
|
||||
<ion-card-title>{{ course.shortname }}</ion-card-title>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
{{ course.summary }}
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ion-content>
|
||||
</ion-app>
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<CoreCourseListItem>[] = courses;
|
||||
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
<h1>Courses List</h1>
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item class="ion-text-wrap" *ngFor="let course of courses">
|
||||
<core-course-image [course]="course" slot="start" />
|
||||
<ion-label>
|
||||
{{ course.shortname }}
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
</ion-app>
|
|
@ -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<CoreCourseListItem>[] = courses;
|
||||
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
<h1>Search</h1>
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<div class="core-flex-fill">
|
||||
<core-search-box />
|
||||
<core-empty-box-wrapper [icon]="icon" [content]="content" [dimmed]="dimmed" class="core-flex-fill" />
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
<core-empty-box [icon]="icon" [dimmed]="dimmed">
|
||||
<div [innerHTML]="html"></div>
|
||||
</core-empty-box>
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
<ion-app>
|
||||
<ion-content class="limited-width">
|
||||
<core-sites-list *ngIf="accountsList" [accountsList]="accountsList" [sitesClickable]="sitesClickable"
|
||||
[currentSiteClickable]="currentSiteClickable" (onSiteClicked)="siteClicked($event)">
|
||||
|
||||
<ng-template *ngIf="extraText !== 'none'" #siteLabel let-site="site">
|
||||
<p *ngIf="extraText === 'text'">Extra text for user {{ site.fullname }}</p>
|
||||
<ion-badge *ngIf="extraText === 'badge'" color="light">{{ site.badge }} MB</ion-badge>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #siteItem let-site="site">
|
||||
<ion-button *ngIf="extraDetails === 'delete-button'" fill="clear" color="danger" slot="end">
|
||||
<ion-icon name="fas-trash" slot="icon-only" />
|
||||
</ion-button>
|
||||
|
||||
<ion-badge *ngIf="extraDetails === 'badge'" slot="end">
|
||||
<span>{{site.badge}}</span>
|
||||
</ion-badge>
|
||||
</ng-template>
|
||||
|
||||
</core-sites-list>
|
||||
</ion-content>
|
||||
</ion-app>
|
|
@ -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<void> {
|
||||
this.accountsList = await CoreLoginHelper.getAccountsList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
async ngOnChanges(changes: SimpleChanges): Promise<void> {
|
||||
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}`);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <Meta> {
|
||||
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<Args>(({ 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 }));
|
|
@ -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 <Meta<Args>> {
|
||||
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>((args) => ({
|
||||
component: CoreEmptyBoxWrapperComponent,
|
||||
props: {
|
||||
...args,
|
||||
content: marked(args.content),
|
||||
},
|
||||
}));
|
||||
|
||||
const PageTemplate = story<Args>((args) => ({
|
||||
component: CoreEmptyBoxPageComponent,
|
||||
props: {
|
||||
...args,
|
||||
content: marked(args.content),
|
||||
},
|
||||
}));
|
||||
|
||||
export const Primary = story<Args>(WrapperTemplate);
|
||||
|
||||
export const Example = story<Args>(PageTemplate, {
|
||||
icon: 'fas-magnifying-glass',
|
||||
content: '**No results for "Test Search"**\n\n<small>Check for typos or try using different keywords</small>',
|
||||
});
|
||||
|
||||
export const DimmedExample = story<Args>(PageTemplate, {
|
||||
icon: 'fas-magnifying-glass',
|
||||
content: 'What are you searching for?',
|
||||
dimmed: true,
|
||||
});
|
|
@ -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 <Meta<Args>> {
|
||||
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> = (args) => ({
|
||||
component: CoreErrorInfoComponent,
|
||||
props: args,
|
||||
});
|
||||
|
||||
export const Primary = story<Args>(Template);
|
|
@ -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 <Meta<Args>> {
|
||||
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<Args>(({ sitesClickable, currentSiteClickable, extraText, extraDetails }) => ({
|
||||
component: CoreSitesListWrapperComponent,
|
||||
props: {
|
||||
sitesClickable,
|
||||
currentSiteClickableSelect: currentSiteClickable,
|
||||
extraText,
|
||||
extraDetails,
|
||||
},
|
||||
}));
|
||||
|
||||
export const Primary = story<Args>(Template);
|
|
@ -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 <Meta> {
|
||||
title: 'Core/User Avatar',
|
||||
component: CoreUserAvatarComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
declarations: [CoreUserAvatarComponent],
|
||||
imports: [StorybookModule],
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
||||
const Template: Story = () => ({
|
||||
component: CoreUserAvatarComponent,
|
||||
});
|
||||
|
||||
export const Primary = story(Template);
|
|
@ -38,9 +38,9 @@ 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 { CoreErrorAccordion } from '@services/error-accordion';
|
||||
import { CoreUserSupportConfig } from '@features/user/classes/support/support-config';
|
||||
import { CoreUserGuestSupportConfig } from '@features/user/classes/support/guest-support-config';
|
||||
import { CoreLoginError } from '@classes/errors/loginerror';
|
||||
|
@ -408,22 +408,20 @@ 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) {
|
||||
errorMessage = `<p>${errorMessage}</p><div class="core-error-info-container"></div>`;
|
||||
if (debug) {
|
||||
errorMessage = `<p>${errorMessage}</p><div class="core-error-accordion-container"></div>`;
|
||||
}
|
||||
|
||||
const alertSupportConfig = supportConfig;
|
||||
|
@ -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,11 @@ export class CoreLoginSitePage implements OnInit {
|
|||
buttons: buttons as AlertButton[],
|
||||
});
|
||||
|
||||
if (errorDetails) {
|
||||
// Avoid sanitizing JS.
|
||||
const containerElement = alertElement.querySelector('.core-error-info-container');
|
||||
if (debug) {
|
||||
const containerElement = alertElement.querySelector('.core-error-accordion-container');
|
||||
|
||||
if (containerElement) {
|
||||
containerElement.innerHTML = CoreErrorInfoComponent.render(errorDetails, errorCode);
|
||||
await CoreErrorAccordion.render(containerElement, debug.code, debug.details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -124,7 +124,10 @@ describe('Credentials page', () => {
|
|||
getUserToken: () => {
|
||||
throw new CoreLoginError({
|
||||
message: '',
|
||||
errorcode: 'invalidlogin',
|
||||
debug: {
|
||||
code: 'invalidlogin',
|
||||
details: 'Invalid login',
|
||||
},
|
||||
});
|
||||
},
|
||||
checkSite: async () => (siteCheck),
|
||||
|
|
|
@ -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 {}
|
|
@ -1,18 +0,0 @@
|
|||
<ion-app>
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
<h1>Search Results</h1>
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="limited-width">
|
||||
<div>
|
||||
<core-search-box />
|
||||
<ion-list>
|
||||
<core-search-global-search-result *ngFor="let result of results" [result]="result"
|
||||
(onClick)="resultClicked(result.title)" />
|
||||
</ion-list>
|
||||
</div>
|
||||
</ion-content>
|
||||
</ion-app>
|
|
@ -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}`);
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <Meta<Args>> {
|
||||
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<Args>(({ 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<Args>(Template);
|
||||
export const ResultsPage = story<Args>(() => ({ component: CoreSearchGlobalSearchResultsPageComponent }));
|
|
@ -0,0 +1,123 @@
|
|||
// (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 { Injectable } from '@angular/core';
|
||||
import { Translate, makeSingleton } from '@singletons';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { CoreDom } from '@singletons/dom';
|
||||
import { CoreForms } from '@singletons/form';
|
||||
import { CoreLogger } from '@singletons/logger';
|
||||
|
||||
/**
|
||||
* Service used to render an Error Accordion component.
|
||||
*
|
||||
* This is declared as a service instead of an Angular Component because the HTML
|
||||
* has to be injected dynamically in alerts (only HTML and Ionic components work).
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreErrorAccordionService {
|
||||
|
||||
private logger: CoreLogger;
|
||||
|
||||
constructor() {
|
||||
this.logger = CoreLogger.getInstance('CoreErrorAccordion');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an instance of the component into an HTML string.
|
||||
*
|
||||
* @param element Root element.
|
||||
* @param errorCode Error code.
|
||||
* @param errorDetails Error details.
|
||||
*/
|
||||
async render(element: Element, errorCode: string, errorDetails: string): Promise<void> {
|
||||
const html = this.html(errorCode, errorDetails);
|
||||
|
||||
element.innerHTML = html;
|
||||
|
||||
await this.hydrate(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get component html.
|
||||
*
|
||||
* @param errorCode Error code.
|
||||
* @param errorDetails Error details.
|
||||
* @returns HTML.
|
||||
*/
|
||||
private html(errorCode: string, errorDetails: string): string {
|
||||
const contentId = CoreForms.uniqueId('error-accordion-content');
|
||||
const errorCodeLabel = Translate.instant('core.errorcode', { errorCode });
|
||||
const hideDetailsLabel = Translate.instant('core.errordetailshide');
|
||||
const showDetailsLabel = Translate.instant('core.errordetailsshow');
|
||||
|
||||
return `
|
||||
<div class="core-error-accordion">
|
||||
<h3 class="core-error-accordion--code">${errorCodeLabel}</h3>
|
||||
<div id="${contentId}" class="core-error-accordion--details" role="region" aria-hidden="true">
|
||||
<p>${errorDetails}</p>
|
||||
</div>
|
||||
<button type="button" class="core-error-accordion--toggle" aria-expanded="false" aria-controls="${contentId}">
|
||||
<div class="core-error-accordion--toggle-text">
|
||||
<span class="core-error-accordion--show-details">
|
||||
${showDetailsLabel}
|
||||
</span>
|
||||
<span class="core-error-accordion--hide-details">
|
||||
${hideDetailsLabel}
|
||||
</span>
|
||||
</div>
|
||||
<ion-icon name="chevron-down" />
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrate component.
|
||||
*
|
||||
* @param element Root element.
|
||||
*/
|
||||
private async hydrate(element: Element): Promise<void> {
|
||||
const wrapper = element.querySelector<HTMLDivElement>('.core-error-accordion');
|
||||
const description = element.querySelector<HTMLParagraphElement>('.core-error-accordion--details');
|
||||
const button = element.querySelector<HTMLButtonElement>('.core-error-accordion--toggle');
|
||||
const hideText = element.querySelector<HTMLSpanElement>('.core-error-accordion--hide-details');
|
||||
|
||||
if (!wrapper || !description || !button || !hideText) {
|
||||
this.logger.error('Couldn\'t render error-accordion, one of the child elements is missing');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await CoreDom.waitToBeVisible(wrapper);
|
||||
|
||||
button.onclick = () => {
|
||||
wrapper.classList.toggle('expanded');
|
||||
description.setAttribute('aria-hidden', description.getAttribute('aria-hidden') === 'true' ? 'false' : 'true');
|
||||
button.setAttribute('aria-expanded', button.getAttribute('aria-expanded') === 'true' ? 'false' : 'true');
|
||||
};
|
||||
|
||||
hideText.style.display = 'none';
|
||||
wrapper.style.setProperty('--width', `${wrapper.clientWidth}px`);
|
||||
wrapper.style.setProperty('--description-height', `${description.clientHeight}px`);
|
||||
wrapper.classList.add('hydrated');
|
||||
|
||||
await CoreUtils.nextTick();
|
||||
|
||||
hideText.style.display = 'revert';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreErrorAccordion = makeSingleton(CoreErrorAccordionService);
|
|
@ -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<CoreSiteSchema[]>('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<CoreLoginError> {
|
||||
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.
|
||||
}
|
||||
|
||||
|
@ -499,19 +509,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) {
|
||||
|
@ -536,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',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -657,23 +673,33 @@ export class CoreSitesProvider {
|
|||
* @returns A promise rejected with the error info.
|
||||
*/
|
||||
protected async treatInvalidAppVersion(result: number, siteId?: string): Promise<never> {
|
||||
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) {
|
||||
|
@ -681,8 +707,8 @@ export class CoreSitesProvider {
|
|||
}
|
||||
|
||||
throw new CoreLoginError({
|
||||
debug,
|
||||
message: Translate.instant(errorKey, translateParams),
|
||||
errorcode: errorCode,
|
||||
loggedOut: true,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ import { Subscription } from 'rxjs';
|
|||
import { CoreNetwork } from '@services/network';
|
||||
import { CoreSiteError } from '@classes/errors/siteerror';
|
||||
import { CoreUserSupport } from '@features/user/services/support';
|
||||
import { CoreErrorInfoComponent } from '@components/error-info/error-info';
|
||||
import { CoreErrorAccordion } from '@services/error-accordion';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
import { CoreCancellablePromise } from '@classes/cancellable-promise';
|
||||
import { CoreLang } from '@services/lang';
|
||||
|
@ -1037,8 +1037,8 @@ 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) {
|
||||
alertOptions.message = `<p>${alertOptions.message}</p><div class="core-error-info-container"></div>`;
|
||||
if (error.debug) {
|
||||
alertOptions.message = `<p>${alertOptions.message}</p><div class="core-error-accordion-container"></div>`;
|
||||
}
|
||||
|
||||
const supportConfig = error.supportConfig;
|
||||
|
@ -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) {
|
||||
const containerElement = alertElement.querySelector('.core-error-info-container');
|
||||
if (error instanceof CoreSiteError && error.debug) {
|
||||
const containerElement = alertElement.querySelector('.core-error-accordion-container');
|
||||
|
||||
if (containerElement) {
|
||||
containerElement.innerHTML = CoreErrorInfoComponent.render(error.errorDetails, error.errorcode);
|
||||
await CoreErrorAccordion.render(containerElement, error.debug.code, error.debug.details);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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' }),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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<typeof englishTranslations> {
|
||||
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 {}
|
|
@ -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<string, unknown> = {};
|
||||
|
||||
constructor (fixture: CoreSiteFixture) {
|
||||
super(fixture.id, fixture.info.siteurl, '', { info: fixture.info });
|
||||
|
||||
this.stubWSResponse<CoreSiteConfigResponse>('tool_mobile_get_config', {
|
||||
settings: [],
|
||||
warnings: [],
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
readObservable<T = unknown>(wsFunction: string, data: unknown, preSets?: CoreSiteWSPreSets): WSObservable<T> {
|
||||
if (wsFunction in this.wsStubs) {
|
||||
return of(this.wsStubs[wsFunction] as T);
|
||||
}
|
||||
|
||||
return super.readObservable<T>(wsFunction, data, preSets);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare as stubbed response for a given WS.
|
||||
*
|
||||
* @param wsFunction WS function.
|
||||
* @param response Response.
|
||||
*/
|
||||
stubWSResponse<T=unknown>(wsFunction: string, response: T): void {
|
||||
this.wsStubs[wsFunction] = response;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<SQLiteObject> {
|
||||
return new Proxy({
|
||||
executeSql: () => Promise.resolve({ insertId: Math.random().toString() }),
|
||||
}, {}) as unknown as SQLiteObject;
|
||||
}
|
||||
|
||||
}
|
|
@ -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];
|
||||
}
|
||||
|
||||
}
|
|
@ -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<string> {
|
||||
return fileUrl;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreFilepoolStub = makeSingleton<CoreFilepoolProviderStub>(CoreFilepoolProvider);
|
|
@ -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<any> {
|
||||
return from(fetch(url).then(response => response.text()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const HttpStub = makeSingleton<HttpClientStub>(HttpClient);
|
|
@ -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<number> {
|
||||
return Math.round(Math.random() * 100);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CorePushNotificationsStub = makeSingleton<CorePushNotificationsProviderStub>(CorePushNotificationsProvider);
|
|
@ -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<CoreSiteBasicInfo[]> {
|
||||
const sites = CoreSitesProviderStub.SITES_FIXTURES.map(site => (<SiteDBEntry> {
|
||||
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<CoreSite> {
|
||||
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<CoreSitesProviderStub>(CoreSitesProvider);
|
|
@ -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<T>(template: Story<T>, defaultArgs: Partial<T> = {}): Story<T> {
|
||||
const story = template.bind({});
|
||||
|
||||
story.args = defaultArgs;
|
||||
|
||||
return story;
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
.core-error-accordion {
|
||||
--toggle-icon-animation-duration: 300ms;
|
||||
--toggle-icon-animation-function: ease-in;
|
||||
--background-color: var(--gray-300);
|
||||
--toggle-icon-size: 16px;
|
||||
|
||||
background: var(--background-color);
|
||||
border-radius: var(--radius-xs);
|
||||
|
||||
.core-error-accordion--code {
|
||||
margin: 0;
|
||||
text-align: start;
|
||||
color: var(--text-color-main);
|
||||
font: var(--subtitle-md-font);
|
||||
padding-top: var(--spacing-2);
|
||||
padding-bottom: var(--spacing-2);
|
||||
padding-inline-start: var(--spacing-3);
|
||||
padding-inline-end: var(--spacing-3);
|
||||
}
|
||||
|
||||
.core-error-accordion--details {
|
||||
opacity: 0;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
transition-property: opacity, height;
|
||||
transition-duration: var(--toggle-icon-animation-duration);
|
||||
transition-timing-function: var(--toggle-icon-animation-function);
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
padding-top: var(--spacing-2);
|
||||
padding-bottom: var(--spacing-2);
|
||||
padding-inline-start: var(--spacing-3);
|
||||
padding-inline-end: var(--spacing-3);
|
||||
text-align: start;
|
||||
font: var(--body-md-font);
|
||||
color: var(--text-color-secondary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.core-error-accordion--toggle {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
background: transparent;
|
||||
justify-content: space-between;
|
||||
color: var(--text-color-secondary);
|
||||
font: var(--label-lg-font);
|
||||
padding-top: var(--spacing-2);
|
||||
padding-bottom: var(--spacing-2);
|
||||
padding-inline-start: var(--spacing-3);
|
||||
padding-inline-end: var(--spacing-3);
|
||||
|
||||
.core-error-accordion--toggle-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.core-error-accordion--show-details,
|
||||
.core-error-accordion--hide-details {
|
||||
text-align: start;
|
||||
transition: opacity var(--toggle-icon-animation-duration) var(--toggle-icon-animation-function);
|
||||
}
|
||||
|
||||
ion-icon {
|
||||
width: var(--toggle-icon-size);
|
||||
margin-inline-start: var(--spacing-4);
|
||||
transition: transform var(--toggle-icon-animation-duration) var(--toggle-icon-animation-function);
|
||||
transform: rotate(0);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: var(--state-color-hover);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
box-shadow: none;
|
||||
background: var(--state-color-focused);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
box-shadow: none;
|
||||
outline: 2px solid var(--state-color-keyboard-focus);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--state-color-pressed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.hydrated {
|
||||
width: var(--width);
|
||||
|
||||
.core-error-accordion--details {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.core-error-accordion--toggle {
|
||||
|
||||
.core-error-accordion--toggle-text {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.core-error-accordion--hide-details {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
|
||||
.core-error-accordion--details {
|
||||
opacity: 1;
|
||||
height: var(--description-height);
|
||||
}
|
||||
|
||||
.core-error-accordion--toggle {
|
||||
|
||||
.core-error-accordion--hide-details {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.core-error-accordion--show-details {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
ion-icon {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
html.dark .core-error-accordion {
|
||||
--background-color: var(--gray-700);
|
||||
}
|
|
@ -5,9 +5,7 @@ html {
|
|||
--spacing-#{$i}: #{$i*4}px;
|
||||
}
|
||||
|
||||
// Font sizes
|
||||
|
||||
// Body font size
|
||||
// Typography
|
||||
--font-size-sm: 12px;
|
||||
--font-size-md: 14px;
|
||||
--font-size-lg: 16px;
|
||||
|
@ -15,19 +13,29 @@ html {
|
|||
--font-weight-normal: 400;
|
||||
--font-weight-medium: 500;
|
||||
|
||||
// Typography - Body
|
||||
--body-font-size-sm: var(--font-size-sm);
|
||||
--body-font-size-md: var(--font-size-md);
|
||||
--body-font-size-lg: var(--font-size-lg);
|
||||
--body-font-weight: var(--font-weight-normal);
|
||||
--body-line-height: 150%;
|
||||
|
||||
--body-sm-font: normal normal var(--body-font-weight) var(--body-font-size-sm)/var(--body-line-height) var(--ion-font-family);
|
||||
--body-md-font: normal normal var(--body-font-weight) var(--body-font-size-md)/var(--body-line-height) var(--ion-font-family);
|
||||
--body-lg-font: normal normal var(--body-font-weight) var(--body-font-size-lg)/var(--body-line-height) var(--ion-font-family);
|
||||
|
||||
// Typography - Links
|
||||
--link-sm-font-size: var(--font-size-sm);
|
||||
--link-md-font-size: var(--font-size-md);
|
||||
--link-lg-font-size: var(--font-size-lg);
|
||||
--link-font-weight: var(--font-weight-normal);
|
||||
--link-line-height: 150%;
|
||||
|
||||
// Labels
|
||||
--link-sm-font: normal normal var(--link-font-weight) var(--link-sm-font-size)/var(--link-line-height) var(--ion-font-family);
|
||||
--link-md-font: normal normal var(--link-font-weight) var(--link-md-font-size)/var(--link-line-height) var(--ion-font-family);
|
||||
--link-lg-font: normal normal var(--link-font-weight) var(--link-lg-font-size)/var(--link-line-height) var(--ion-font-family);
|
||||
|
||||
// Typography - Labels
|
||||
--label-sm-font-size: 10px;
|
||||
--label-md-font-size: 12px;
|
||||
--label-lg-font-size: 14px;
|
||||
|
@ -37,7 +45,11 @@ html {
|
|||
--label-md-line-height: 16px;
|
||||
--label-lg-line-height: 20px;
|
||||
|
||||
// Subtitles
|
||||
--label-sm-font: normal normal var(--label-font-weight) var(--label-sm-font-size)/var(--label-sm-line-height) var(--ion-font-family);
|
||||
--label-md-font: normal normal var(--label-font-weight) var(--label-md-font-size)/var(--label-md-line-height) var(--ion-font-family);
|
||||
--label-lg-font: normal normal var(--label-font-weight) var(--label-lg-font-size)/var(--label-lg-line-height) var(--ion-font-family);
|
||||
|
||||
// Typography - Subtitles
|
||||
--subtitle-sm-font-size: 14px;
|
||||
--subtitle-md-font-size: 16px;
|
||||
--subtitle-lg-font-size: 20px;
|
||||
|
@ -45,7 +57,11 @@ html {
|
|||
--subtitle-font-weight: var(--font-weight-medium);
|
||||
--subtitle-line-height: 150%;
|
||||
|
||||
// Headings
|
||||
--subtitle-sm-font: normal normal var(--subtitle-font-weight) var(--subtitle-sm-font-size)/var(--subtitle-line-height) var(--ion-font-family);
|
||||
--subtitle-md-font: normal normal var(--subtitle-font-weight) var(--subtitle-md-font-size)/var(--subtitle-line-height) var(--ion-font-family);
|
||||
--subtitle-lg-font: normal normal var(--subtitle-font-weight) var(--subtitle-lg-font-size)/var(--subtitle-line-height) var(--ion-font-family);
|
||||
|
||||
// Typography - Headings
|
||||
--heading-1-font-size: 28px;
|
||||
--heading-2-font-size: 24px;
|
||||
--heading-3-font-size: 22px;
|
||||
|
@ -94,6 +110,26 @@ html {
|
|||
|
||||
// A11y
|
||||
--a11y-min-target-size: 44px;
|
||||
|
||||
// Colors
|
||||
--blue: #0f6cbf;
|
||||
|
||||
--text-color-main: var(--gray-900);
|
||||
--text-color-secondary: var(--gray-800);
|
||||
|
||||
--state-color-hover: rgb(40 40 40, 4%); // --gray-900 4%
|
||||
--state-color-pressed: rgb(40 40 40, 12%); // --gray-900 12%
|
||||
--state-color-focused: rgb(40 40 40, 12%); // --gray-900 12%
|
||||
--state-color-keyboard-focus: var(--blue);
|
||||
|
||||
}
|
||||
|
||||
html.dark {
|
||||
|
||||
// Colors
|
||||
--text-color-main: var(--gray-200);
|
||||
--text-color-secondary: var(--gray-300);
|
||||
|
||||
}
|
||||
|
||||
/** @deprecated since 4.3 **/
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
/* Components */
|
||||
@import "components/collapsible-header.scss";
|
||||
@import "components/collapsible-item.scss";
|
||||
@import "components/error-accordion.scss";
|
||||
@import "components/format-text.scss";
|
||||
@import "components/rubrics.scss";
|
||||
@import "components/mod-label.scss";
|
||||
@import "components/ion-icon.scss";
|
||||
@import "../core/components/error-info/error-info.scss";
|
||||
@import "components/mod-label.scss";
|
||||
@import "components/rubrics.scss";
|
||||
@import "components/videojs.scss";
|
||||
|
||||
/* Some styles from 3rd party libraries. */
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
"src/**/tests/**",
|
||||
"src/**/stories/**",
|
||||
"src/testing/**",
|
||||
"src/storybook/**",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.stories.*"
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue