MOBILE-4459 storybook: Create storybook for new sites-list component

main
Dani Palou 2023-11-06 09:53:39 +01:00
parent 2244864c07
commit a0f81719f7
16 changed files with 364 additions and 18 deletions

View File

@ -0,0 +1,32 @@
// (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: [],
},
};

View File

@ -1 +0,0 @@
{"id":"123456","info":{"version":"2022041900","sitename":"School","username":"barbara","firstname":"Barbara","lastname":"Gardner","fullname":"Barbara Gardner","lang":"en","userid":1,"siteurl":"https://campus.example.edu","userpictureurl":"","functions":[]}}

View File

@ -0,0 +1,32 @@
// (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: [],
},
};

View File

@ -0,0 +1,32 @@
// (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: [],
},
};

View File

@ -21,6 +21,8 @@ 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: [
@ -28,10 +30,12 @@ import { CoreCourseImageListPageComponent } from '@components/stories/components
CoreCourseImageListPageComponent,
CoreEmptyBoxPageComponent,
CoreEmptyBoxWrapperComponent,
CoreSitesListWrapperComponent,
],
imports: [
CommonModule,
StorybookModule,
CoreDirectivesModule,
CoreComponentsModule,
CoreSearchComponentsModule,
],

View File

@ -0,0 +1,23 @@
<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-icon>
</ion-button>
<ion-badge *ngIf="extraDetails === 'badge'" slot="end">
<span>{{site.badge}}</span>
</ion-badge>
</ng-template>
</core-sites-list>
</ion-content>
</ion-app>

View File

@ -0,0 +1,60 @@
// (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}`);
}
}

View File

@ -0,0 +1,78 @@
// (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);

View File

@ -50,7 +50,7 @@ export class CoreLoginSitesModalComponent implements OnInit {
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
this.accountsList = await CoreLoginHelper.getAccountsList(this.currentSiteId);
this.accountsList = await CoreLoginHelper.getAccountsList();
this.loaded = true;
}

View File

@ -1301,10 +1301,9 @@ export class CoreLoginHelperProvider {
/**
* Get the accounts list classified per site.
*
* @param currentSiteId If loggedin, current Site Id.
* @returns Promise resolved with account list.
*/
async getAccountsList(currentSiteId?: string): Promise<CoreAccountsList> {
async getAccountsList(): Promise<CoreAccountsList> {
const sites = await CoreUtils.ignoreErrors(CoreSites.getSortedSites(), [] as CoreSiteBasicInfo[]);
const accountsList: CoreAccountsList = {
@ -1312,14 +1311,11 @@ export class CoreLoginHelperProvider {
otherSites: [],
count: sites.length,
};
const currentSiteId = CoreSites.getCurrentSiteId();
let siteUrl = '';
if (currentSiteId) {
const index = sites.findIndex((site) => site.id == currentSiteId);
accountsList.currentSite = sites.splice(index, 1)[0];
siteUrl = accountsList.currentSite.siteUrlWithoutProtocol;
siteUrl = sites.find((site) => site.id == currentSiteId)?.siteUrlWithoutProtocol ?? '';
}
const otherSites: Record<string, CoreSiteBasicInfo[]> = {};
@ -1328,7 +1324,9 @@ export class CoreLoginHelperProvider {
await Promise.all(sites.map(async (site) => {
site.badge = await CoreUtils.ignoreErrors(CorePushNotifications.getSiteCounter(site.id)) || 0;
if (site.siteUrlWithoutProtocol == siteUrl) {
if (site.id === currentSiteId) {
accountsList.currentSite = site;
} else if (site.siteUrlWithoutProtocol == siteUrl) {
accountsList.sameSite.push(site);
} else {
if (!otherSites[site.siteUrlWithoutProtocol]) {

View File

@ -104,10 +104,8 @@ export class CoreSettingsSynchronizationPage implements OnInit, OnDestroy {
* @inheritdoc
*/
async ngOnInit(): Promise<void> {
const currentSiteId = CoreSites.getCurrentSiteId();
try {
this.accountsList = await CoreLoginHelper.getAccountsList(currentSiteId);
this.accountsList = await CoreLoginHelper.getAccountsList();
} catch {
// Ignore errors.
}

View File

@ -18,7 +18,7 @@ import { CoreSharedFilesHelper } from '@features/sharedfiles/services/sharedfile
import { FileEntry } from '@ionic-native/file/ngx';
import { CoreFile } from '@services/file';
import { CoreNavigator } from '@services/navigator';
import { CoreSiteBasicInfo, CoreSites } from '@services/sites';
import { CoreSiteBasicInfo } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
/**
@ -94,7 +94,7 @@ export class CoreSharedFilesChooseSitePage implements OnInit {
* @returns Promise resolved when done.
*/
protected async loadSites(): Promise<void> {
this.accountsList = await CoreLoginHelper.getAccountsList(CoreSites.getCurrentSiteId());
this.accountsList = await CoreLoginHelper.getAccountsList();
}
/**

View File

@ -1300,7 +1300,19 @@ export class CoreSitesProvider {
async getSites(ids?: string[]): Promise<CoreSiteBasicInfo[]> {
const sites = await this.sitesTable.getMany();
return this.siteDBRecordsToBasicInfo(sites, ids);
}
/**
* Convert sites DB records to site basic info.
*
* @param sites DB records.
* @param ids IDs of sites to return, undefined to return them all.
* @returns Sites basic info.
*/
protected siteDBRecordsToBasicInfo(sites: SiteDBEntry[], ids?: string[]): CoreSiteBasicInfo[] {
const formattedSites: CoreSiteBasicInfo[] = [];
sites.forEach((site) => {
if (!ids || ids.indexOf(site.id) > -1) {
const isDemoModeSite = CoreLoginHelper.isDemoModeSite(site.siteUrl);

View File

@ -28,6 +28,8 @@ 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 {
@ -56,6 +58,7 @@ export class StaticTranslateLoader extends TranslateLoader {
{ provide: CoreSitesProvider, useClass: CoreSitesProviderStub },
{ provide: CoreDbProvider, useClass: CoreDbProviderStub },
{ provide: CoreFilepoolProvider, useClass: CoreFilepoolProviderStub },
{ provide: CorePushNotificationsProvider, useClass: CorePushNotificationsProviderStub },
{ provide: HttpClient, useClass: HttpClientStub },
{
provide: APP_INITIALIZER,

View File

@ -0,0 +1,32 @@
// (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);

View File

@ -12,9 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import school from '@/assets/storybook/sites/school.json';
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 { CoreSitesProvider } from '@services/sites';
import { CoreError } from '@classes/errors/error';
import { CoreSite } from '@classes/site';
import { SiteDBEntry } from '@services/database/sites';
import { CoreSiteBasicInfo, CoreSitesProvider } from '@services/sites';
import { makeSingleton } from '@singletons';
/**
@ -22,17 +27,55 @@ import { makeSingleton } from '@singletons';
*/
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 ?? school);
this.currentSite = new CoreSiteStub(fixture ?? schoolBarbaraSite);
}
return this.getRequiredCurrentSite();