Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@
{
"endOfLine": "auto"
}
]
],
"@angular-eslint/template/button-has-type": "error"
}
},
{
Expand Down
61 changes: 18 additions & 43 deletions .storybook/msw/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -350,50 +350,25 @@ export const mockProjectUsers = [
},
];

export const mockUsers = [
{
id: 6,
firstName: 'Chloe',
lastName: 'Alibert',
picture: null,
department: {
id: 1,
name: 'Direction',
},
},
{
id: 7,
firstName: 'Marie-Françoise',
lastName: 'Archer',
picture: {
href: 'https://demo-ux1.ilucca.net/getFile.ashx?id=e845a0ca-dbd9-452d-adc0-c873a847a37d',
},
department: {
id: 1,
name: 'Direction',
},
},
{
id: 49,
firstName: 'Laurence',
lastName: 'Atali',
picture: null,
department: {
id: 2,
name: 'Support',
},
},
{
id: 66,
firstName: 'Chloe',
lastName: 'Alibert',
const firstNames = ['Chloe', 'Marie-Françoise', 'Laurence', 'Gaspard', 'Jean', 'Pierre', 'Paul', 'Jacques', 'Marie', 'Sophie', 'Julie', 'Julien'];
const lastNames = ['Alibert', 'Archer', 'Atali', 'Bouvier', 'Lefevre', 'Durand', 'Dupont', 'Martin', 'Bernard', 'Robert', 'Richard', 'Petit'];
const departments = ['Direction', 'Support', 'Commercial'];

export const mockUsers = firstNames
.flatMap((firstName) => lastNames.flatMap((lastName) => ({ firstName, lastName })))
.map((user, index) => ({
id: index + 1,
picture: null,
department: {
id: 3,
name: 'Commercial',
},
},
];
department: { id: (index % departments.length) + 1, name: departments[index % departments.length] },
...user,
}));

// Create homonym in different department
mockUsers.unshift({
...mockUsers[0],
id: mockUsers.length + 1,
department: { id: 2, name: 'Commercial' },
});

export const mockMe = {
id: 35,
Expand Down
21 changes: 15 additions & 6 deletions packages/ng/core-select/input/select-input.component.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
/* eslint-disable @angular-eslint/no-output-on-prefix */
import { OverlayConfig, OverlayContainer } from '@angular/cdk/overlay';
import {
booleanAttribute,
ChangeDetectorRef,
Directive,
ElementRef,
EventEmitter,
HostBinding,
HostListener,
inject,
Input,
OnDestroy,
OnInit,
Output,
TemplateRef,
Type,
ViewChild,
booleanAttribute,
inject,
} from '@angular/core';
import { BehaviorSubject, ReplaySubject, Subject } from 'rxjs';
import { ControlValueAccessor } from '@angular/forms';
import { BehaviorSubject, Observable, ReplaySubject, Subject, switchMap, take } from 'rxjs';
import { LuOptionGrouping, LuSimpleSelectDefaultOptionComponent } from '../option';
import { LuSelectPanelRef } from '../panel';
import { LuOptionContext, SELECT_LABEL, SELECT_LABEL_ID } from '../select.model';
import { ControlValueAccessor } from '@angular/forms';

@Directive()
export abstract class ALuSelectInputComponent<TOption, TValue> implements OnDestroy, OnInit, ControlValueAccessor {
Expand Down Expand Up @@ -167,6 +167,8 @@ export abstract class ALuSelectInputComponent<TOption, TValue> implements OnDest
break;
case 'Enter':
if (this.isPanelOpen) {
// Prevent form submission when selecting a value with Enter
$event.preventDefault();
this.panelRef.selectCurrentlyHighlightedValue();
} else {
this.panelRef?.handleKeyManagerEvent($event);
Expand Down Expand Up @@ -248,8 +250,10 @@ export abstract class ALuSelectInputComponent<TOption, TValue> implements OnDest
}

this.panelRef.valueChanged.subscribe((value) => this.updateValue(value));
this.panelRef.nextPage.subscribe(() => this.nextPage.emit());
this.panelRef.previousPage.subscribe(() => this.previousPage.emit());

this.#pageChanged(this.panelRef.nextPage).subscribe(() => this.nextPage.emit());
this.#pageChanged(this.panelRef.previousPage).subscribe(() => this.previousPage.emit());

this.panelRef.activeOptionIdChanged.subscribe((optionId) => {
this.activeDescendant$.next(optionId);
this.changeDetectorRef.markForCheck();
Expand Down Expand Up @@ -294,4 +298,9 @@ export abstract class ALuSelectInputComponent<TOption, TValue> implements OnDest
this.onChange?.(value);
this.onTouched?.();
}

// Ensure nextPage/previousPage does not emit too often
#pageChanged(pageEmitter: EventEmitter<void>): Observable<void> {
return this.options$.pipe(switchMap(() => pageEmitter.pipe(take(1))));
}
}
17 changes: 14 additions & 3 deletions packages/ng/core-select/user/user-homonym.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { HttpClient } from '@angular/common/http';
import { Injectable, inject } from '@angular/core';
import { ILuApiCollectionResponse } from '@lucca-front/ng/api';
import { LuDisplayFormat, luUserDisplay } from '@lucca-front/ng/user';
import { Observable, map, of, startWith } from 'rxjs';
import { Observable, map, of, startWith, tap } from 'rxjs';
import { LuCoreSelectUser } from './user-option.model';

@Injectable({ providedIn: 'root' })
export class LuCoreSelectUserHomonymsService {
protected http = inject(HttpClient);
protected cache: Record<number, string> = {};

protected extractHomonyms<T extends LuCoreSelectUser>(users: T[], format: LuDisplayFormat): Set<T> {
const usersByFullName: Record<string, T[]> = {};
Expand Down Expand Up @@ -38,7 +39,7 @@ export class LuCoreSelectUserHomonymsService {
if (homonyms.has(user)) {
return {
...user,
additionalInformation: additionalInformation[user.id],
additionalInformation: this.cache[user.id] || additionalInformation[user.id],
};
}
return user;
Expand All @@ -49,7 +50,11 @@ export class LuCoreSelectUserHomonymsService {
}

protected getAdditionalInformationByUserId<T extends LuCoreSelectUser>(homonyms: T[]): Observable<Record<number, string>> {
const userIds = homonyms.map((user) => user.id);
const userIds = homonyms.map((user) => user.id).filter((id) => !this.cache[id]);

if (userIds.length === 0) {
return of({});
}

return this.http
.get<ILuApiCollectionResponse<{ id: number; department?: { name: string } }>>(`/api/v3/users`, {
Expand All @@ -69,6 +74,12 @@ export class LuCoreSelectUserHomonymsService {
{},
),
),
tap((infos) => {
this.cache = {
...this.cache,
...infos,
};
}),
);
}
}
7 changes: 3 additions & 4 deletions packages/ng/core-select/user/users.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,11 +158,10 @@ export class LuCoreSelectUsersDirective<T extends LuCoreSelectUser = LuCoreSelec
shareReplay({ bufferSize: 1, refCount: true }),
);

const options$ = super.buildOptions();
const meFilter$ = displayMe$.pipe(switchMap((displayMe) => (displayMe ? me$ : of(null))));

return displayMe$.pipe(
switchMap((displayMe) => combineLatest([options$, displayMe ? me$ : of(null)])),
map(([options, me]) => (me ? [me, ...(options ?? []).filter((o) => o.id !== me.id)] : options)),
return combineLatest([super.buildOptions(), meFilter$]).pipe(
map(([options, meFilter]) => (meFilter ? [meFilter, ...(options ?? []).filter((o) => o.id !== meFilter.id)] : options)),
switchMap((users) => this.#userHomonymsService.handleHomonyms(users, this.displayFormat)),
);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/ng/forms/text-input/text-input.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
#inputElement
/>
<div class="textField-input-affix">
<button class="textField-input-affix-clear clear" (click)="clearValue()" *ngIf="hasClearer && inputElement.value">
<button class="textField-input-affix-clear clear" type="button" (click)="clearValue()" *ngIf="hasClearer && inputElement.value">
<span aria-hidden="true" class="lucca-icon icon-close"></span>
<span class="u-mask">{{ intl.clear }}</span>
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<button
*ngIf="hasValue && clearable && (disabled$ | async) === false"
role="button"
type="button"
class="multipleSelect-clear clear"
(click)="clearValue($event)"
>
Expand Down