-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(showcase): add elf usage for server state
- Loading branch information
1 parent
84b7415
commit 07807b5
Showing
50 changed files
with
866 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# ElfComponent | ||
|
||
the elf page |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { AsyncPipe } from '@angular/common'; | ||
import { AfterViewInit, ChangeDetectionStrategy, Component, QueryList, ViewChildren, ViewEncapsulation } from '@angular/core'; | ||
import { RouterLink } from '@angular/router'; | ||
import { O3rComponent } from '@o3r/core'; | ||
import { | ||
CopyTextPresComponent, | ||
ElfPresComponent, | ||
IN_PAGE_NAV_PRES_DIRECTIVES, | ||
InPageNavLink, | ||
InPageNavLinkDirective, | ||
InPageNavPresService | ||
} from '../../components'; | ||
|
||
@O3rComponent({ componentType: 'Page' }) | ||
@Component({ | ||
selector: 'o3r-sdk', | ||
standalone: true, | ||
imports: [ | ||
CopyTextPresComponent, | ||
RouterLink, | ||
ElfPresComponent, | ||
IN_PAGE_NAV_PRES_DIRECTIVES, | ||
AsyncPipe | ||
], | ||
templateUrl: './elf.template.html', | ||
styleUrls: ['./elf.style.scss'], | ||
encapsulation: ViewEncapsulation.None, | ||
changeDetection: ChangeDetectionStrategy.OnPush | ||
}) | ||
export class ElfComponent implements AfterViewInit { | ||
@ViewChildren(InPageNavLinkDirective) | ||
private readonly inPageNavLinkDirectives!: QueryList<InPageNavLink>; | ||
public links$ = this.inPageNavPresService.links$; | ||
|
||
constructor(private readonly inPageNavPresService: InPageNavPresService) {} | ||
|
||
public ngAfterViewInit() { | ||
this.inPageNavPresService.initialize(this.inPageNavLinkDirectives); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { PetApi } from '@ama-sdk/showcase-sdk'; | ||
import { PetApiFixture } from '@ama-sdk/showcase-sdk/fixtures'; | ||
import { AsyncPipe } from '@angular/common'; | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
import { RouterModule } from '@angular/router'; | ||
|
||
import { ElfComponent } from './elf.component'; | ||
import '@angular/localize/init'; | ||
|
||
describe('SdkComponent', () => { | ||
let component: ElfComponent; | ||
let fixture: ComponentFixture<ElfComponent>; | ||
const petApiFixture = new PetApiFixture(); | ||
petApiFixture.findPetsByStatus = petApiFixture.findPetsByStatus.mockResolvedValue([]); | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
imports: [ | ||
ElfComponent, | ||
RouterModule.forRoot([]), | ||
AsyncPipe | ||
], | ||
providers: [ | ||
{provide: PetApi, useValue: petApiFixture} | ||
] | ||
}); | ||
fixture = TestBed.createComponent(ElfComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<h1>ELF Study</h1> | ||
<div class="row"> | ||
<div class="right-nav order-1 order-lg-2 col-12 col-lg-2 sticky-lg-top pt-5 pt-lg-0"> | ||
<o3r-in-page-nav-pres | ||
id="sdk-nav" | ||
[links]="links$ | async" | ||
> | ||
</o3r-in-page-nav-pres> | ||
</div> | ||
<div class="order-2 order-lg-1 col-12 col-lg-10"> | ||
<h2 id="sdk-description">Description</h2> | ||
<div> | ||
<p>This page aims to display showcases of usages with <a href="https://ngneat.github.io/elf/" target="_blank">ELF</a>.</p> | ||
</div> | ||
|
||
<h2 id="sdk-example">Example</h2> | ||
<div> | ||
<p> | ||
Let's try to use the API <a href="https://petstore3.swagger.io" target="_blank" rel="noopener">https://petstore3.swagger.io</a> | ||
<br> | ||
Fortunately, this API provides the specification as <a href="https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml" target="_blank" rel="noopener">Yaml file</a> | ||
that we can use to generate an SDK. | ||
<br> | ||
Here, you can check the <a href="https://github.com/AmadeusITGroup/otter/blob/main/packages/@ama-sdk/showcase-sdk" target="_blank" rel="noopener">generated SDK</a> | ||
</p> | ||
<o3r-elf-pres></o3r-elf-pres> | ||
</div> | ||
</div> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './elf.component'; |
Empty file.
159 changes: 159 additions & 0 deletions
159
apps/showcase/src/components/showcase/elf/elf-pres.component.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
import { Tag } from '@ama-sdk/showcase-sdk'; | ||
import type { Pet } from '@ama-sdk/showcase-sdk'; | ||
import { ChangeDetectionStrategy, Component, computed, inject, OnInit, signal, ViewEncapsulation } from '@angular/core'; | ||
import { toSignal } from '@angular/core/rxjs-interop'; | ||
import { FormsModule } from '@angular/forms'; | ||
import { DfMedia } from '@design-factory/design-factory'; | ||
import { NgbHighlight, NgbPagination, NgbPaginationPages } from '@ng-bootstrap/ng-bootstrap'; | ||
import { O3rComponent } from '@o3r/core'; | ||
import { OtterPickerPresComponent } from '../../utilities'; | ||
import { PetFacade } from '../../../stores'; | ||
import { take } from 'rxjs'; | ||
|
||
const FILTER_PAG_REGEX = /[^0-9]/g; | ||
|
||
@O3rComponent({ componentType: 'Component' }) | ||
@Component({ | ||
selector: 'o3r-elf-pres', | ||
standalone: true, | ||
imports: [ | ||
NgbHighlight, | ||
FormsModule, | ||
NgbPagination, | ||
OtterPickerPresComponent, | ||
NgbPaginationPages | ||
], | ||
templateUrl: './elf-pres.template.html', | ||
styleUrls: ['./elf-pres.style.scss'], | ||
encapsulation: ViewEncapsulation.None, | ||
changeDetection: ChangeDetectionStrategy.OnPush | ||
}) | ||
export class ElfPresComponent implements OnInit { | ||
private readonly mediaService = inject(DfMedia); | ||
private readonly petFacade = inject(PetFacade); | ||
|
||
/** | ||
* Name input used to create new pets | ||
*/ | ||
public readonly petName = signal(''); | ||
|
||
/** | ||
* File input used to create new pets | ||
*/ | ||
public readonly petImage = signal(''); | ||
|
||
/** | ||
* Search term used to filter the list of pets | ||
*/ | ||
public readonly searchTerm = signal(''); | ||
|
||
/** | ||
* Number of items to display on a table page | ||
*/ | ||
public readonly pageSize = signal(10); | ||
|
||
/** | ||
* Currently opened page on the table | ||
*/ | ||
public readonly currentPage = signal(1); | ||
|
||
/** | ||
* Complete list of pets retrieved from the API | ||
*/ | ||
public readonly pets = toSignal(this.petFacade.pets, {initialValue: []}); | ||
|
||
/** | ||
* Loading state of the API | ||
*/ | ||
public readonly isLoading = toSignal(this.petFacade.loading, {initialValue: false}); | ||
|
||
/** | ||
* Error state of the API | ||
*/ | ||
public readonly hasErrors = toSignal(this.petFacade.error, {initialValue: false}); | ||
|
||
/** | ||
* List of pets filtered according to search term | ||
*/ | ||
public readonly filteredPets = computed(() => { | ||
let pets = this.pets(); | ||
if (this.searchTerm()) { | ||
const matchString = new RegExp(this.searchTerm().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'), 'i'); | ||
const matchTag = (tag: Tag) => tag.name && matchString.test(tag.name); | ||
pets = pets.filter((pet) => | ||
(pet.id && matchString.test(String(pet.id))) || | ||
matchString.test(pet.name) || | ||
(pet.category?.name && matchString.test(pet.category.name)) || | ||
(pet.tags && pet.tags.some(matchTag))); | ||
} | ||
return pets; | ||
}); | ||
|
||
/** | ||
* Total amount of pet in the filtered list | ||
*/ | ||
public readonly totalPetsAmount = computed(() => this.filteredPets().length); | ||
|
||
/** | ||
* List of pets displayed in the currently selected table page | ||
*/ | ||
public readonly displayedPets = computed(() => | ||
this.filteredPets().slice((this.currentPage() - 1) * this.pageSize(), (this.currentPage()) * this.pageSize()) | ||
); | ||
|
||
/** | ||
* True if screen size is 'xs' or 'sm' | ||
*/ | ||
public readonly isSmallScreen = toSignal<boolean>(this.mediaService.getObservable(['xs', 'sm'])); | ||
|
||
/** Base URL where the images can be fetched */ | ||
public baseUrl = location.href.split('/#', 1)[0]; | ||
|
||
private getNextId() { | ||
return this.pets().reduce<number>((maxId, pet) => pet.id && pet.id < Number.MAX_SAFE_INTEGER ? Math.max(maxId, pet.id) : maxId, 0) + 1; | ||
} | ||
|
||
/** | ||
* Trigger a full reload of the list of pets by calling the API | ||
*/ | ||
public reload() { | ||
this.petFacade.fetchPets(); | ||
} | ||
|
||
public ngOnInit() { | ||
this.petFacade.lastFetch.pipe(take(1)).subscribe((lastFetch) => { | ||
if (Date.now() - lastFetch > 300_000) { | ||
this.reload(); | ||
} | ||
}); | ||
} | ||
|
||
/** | ||
* Call the API to create a new pet | ||
*/ | ||
public create() { | ||
const pet: Pet = { | ||
id: this.getNextId(), | ||
name: this.petName(), | ||
category: {name: 'otter'}, | ||
tags: [{name: 'otter'}], | ||
status: 'available', | ||
photoUrls: this.petName() ? [this.petImage()] : [] | ||
}; | ||
this.petFacade.createPet(pet); | ||
} | ||
|
||
public delete(petToDelete: Pet) { | ||
if (petToDelete.id) { | ||
this.petFacade.deletePet(petToDelete.id); | ||
} | ||
} | ||
|
||
public getTags(pet: Pet) { | ||
return pet.tags?.map((tag) => tag.name).join(','); | ||
} | ||
|
||
public formatPaginationInput(input: HTMLInputElement) { | ||
input.value = input.value.replace(FILTER_PAG_REGEX, ''); | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
apps/showcase/src/components/showcase/elf/elf-pres.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { PetApi } from '@ama-sdk/showcase-sdk'; | ||
import { PetApiFixture } from '@ama-sdk/showcase-sdk/fixtures'; | ||
import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
|
||
import { ElfPresComponent } from './elf-pres.component'; | ||
import '@angular/localize/init'; | ||
|
||
describe('ElfPresComponent', () => { | ||
let component: ElfPresComponent; | ||
let fixture: ComponentFixture<ElfPresComponent>; | ||
const petApiFixture = new PetApiFixture(); | ||
petApiFixture.findPetsByStatus = petApiFixture.findPetsByStatus.mockResolvedValue([]); | ||
|
||
beforeEach(() => { | ||
TestBed.configureTestingModule({ | ||
imports: [ElfPresComponent], | ||
providers: [ | ||
{provide: PetApi, useValue: petApiFixture} | ||
] | ||
}); | ||
fixture = TestBed.createComponent(ElfPresComponent); | ||
component = fixture.componentInstance; | ||
fixture.detectChanges(); | ||
}); | ||
|
||
it('should create', () => { | ||
expect(component).toBeTruthy(); | ||
}); | ||
}); |
18 changes: 18 additions & 0 deletions
18
apps/showcase/src/components/showcase/elf/elf-pres.style.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
o3r-elf-pres { | ||
.table-container { | ||
min-height: 41rem; | ||
} | ||
|
||
.table-column-photo, .table-column-actions { | ||
width: 2em; | ||
} | ||
|
||
.scroll-container { | ||
width: 100%; | ||
overflow-x: auto; | ||
} | ||
|
||
td, th { | ||
vertical-align: middle; | ||
} | ||
} |
Oops, something went wrong.