diff --git a/apps/code-examples/src/app/code-examples/lookup/lookup/add-item/demo.component.html b/apps/code-examples/src/app/code-examples/lookup/lookup/add-item/demo.component.html
index 74a389599f..293f6c5cc2 100644
--- a/apps/code-examples/src/app/code-examples/lookup/lookup/add-item/demo.component.html
+++ b/apps/code-examples/src/app/code-examples/lookup/lookup/add-item/demo.component.html
@@ -15,10 +15,10 @@
formControlName="favoriteNames"
idProperty="name"
[enableShowMore]="true"
- (searchAsync)="searchAsync($event)"
[showAddButton]="true"
[showMoreConfig]="showMoreConfig"
(addClick)="addClick($event)"
+ (searchAsync)="searchAsync($event)"
/>
-
-
-
-
-
-
Form model:
-
{{ favoritesForm.value | json }}
-
-
-
diff --git a/apps/code-examples/src/app/code-examples/lookup/lookup/async/demo.component.spec.ts b/apps/code-examples/src/app/code-examples/lookup/lookup/async/demo.component.spec.ts
deleted file mode 100644
index fc8c35b894..0000000000
--- a/apps/code-examples/src/app/code-examples/lookup/lookup/async/demo.component.spec.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
-import { ComponentFixture, TestBed } from '@angular/core/testing';
-import { NoopAnimationsModule } from '@angular/platform-browser/animations';
-import { SkyInputBoxHarness } from '@skyux/forms/testing';
-import { SkyLookupHarness } from '@skyux/lookup/testing';
-
-import { of } from 'rxjs';
-
-import { DemoComponent } from './demo.component';
-import { DemoService } from './demo.service';
-
-describe('Lookup asynchronous search demo', () => {
- let mockSvc!: jasmine.SpyObj
;
-
- async function setupTest(): Promise<{
- lookupHarness: SkyLookupHarness;
- fixture: ComponentFixture;
- }> {
- const fixture = TestBed.createComponent(DemoComponent);
- const loader = TestbedHarnessEnvironment.loader(fixture);
-
- const lookupHarness = await (
- await loader.getHarness(
- SkyInputBoxHarness.with({ dataSkyId: 'favorite-names-field' }),
- )
- ).queryHarness(SkyLookupHarness);
-
- return { lookupHarness, fixture };
- }
-
- beforeEach(() => {
- // Create a mock search service. In a real-world application, the search
- // service would make a web request which should be avoided in unit tests.
- mockSvc = jasmine.createSpyObj('DemoService', ['search']);
-
- TestBed.configureTestingModule({
- imports: [DemoComponent, NoopAnimationsModule],
- providers: [
- {
- provide: DemoService,
- useValue: mockSvc,
- },
- ],
- });
- });
-
- it('should set the expected initial value', async () => {
- const { lookupHarness } = await setupTest();
-
- await expectAsync(lookupHarness.getSelectionsText()).toBeResolvedTo([
- 'Shirley',
- ]);
- });
-
- it('should update the form control when a favorite name is selected', async () => {
- const { lookupHarness, fixture } = await setupTest();
-
- mockSvc.search.and.callFake((searchText) =>
- of({
- hasMore: false,
- people:
- searchText === 'b'
- ? [
- {
- name: 'Bernard',
- },
- ]
- : [],
- totalCount: 1,
- }),
- );
-
- await lookupHarness.enterText('b');
- await lookupHarness.selectSearchResult({
- text: 'Bernard',
- });
-
- expect(fixture.componentInstance.favoritesForm.value.favoriteNames).toEqual(
- [{ name: 'Shirley' }, { name: 'Bernard' }],
- );
- });
-
- it('should respect the selection descriptor', async () => {
- const { lookupHarness } = await setupTest();
-
- mockSvc.search.and.callFake(() =>
- of({
- hasMore: false,
- people: [
- {
- id: '21',
- name: 'Bernard',
- },
- ],
- totalCount: 1,
- }),
- );
-
- await lookupHarness.clickShowMoreButton();
-
- const picker = await lookupHarness.getShowMorePicker();
-
- await expectAsync(picker.getSearchAriaLabel()).toBeResolvedTo(
- 'Search names',
- );
- await expectAsync(picker.getSaveButtonAriaLabel()).toBeResolvedTo(
- 'Select names',
- );
- });
-});
diff --git a/apps/code-examples/src/app/code-examples/lookup/lookup/async/demo.component.ts b/apps/code-examples/src/app/code-examples/lookup/lookup/async/demo.component.ts
deleted file mode 100644
index 1fd8d8d5b5..0000000000
--- a/apps/code-examples/src/app/code-examples/lookup/lookup/async/demo.component.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import { CommonModule } from '@angular/common';
-import { Component, OnInit, inject } from '@angular/core';
-import {
- AbstractControl,
- FormBuilder,
- FormControl,
- FormGroup,
- FormsModule,
- ReactiveFormsModule,
- ValidationErrors,
-} from '@angular/forms';
-import { SkyFormErrorModule, SkyInputBoxModule } from '@skyux/forms';
-import { SkyWaitService } from '@skyux/indicators';
-import {
- SkyAutocompleteSearchAsyncArgs,
- SkyLookupAddClickEventArgs,
- SkyLookupModule,
- SkyLookupShowMoreConfig,
-} from '@skyux/lookup';
-
-import { map } from 'rxjs/operators';
-
-import { DemoService } from './demo.service';
-import { Person } from './person';
-
-@Component({
- standalone: true,
- selector: 'app-demo',
- templateUrl: './demo.component.html',
- imports: [
- CommonModule,
- FormsModule,
- ReactiveFormsModule,
- SkyFormErrorModule,
- SkyInputBoxModule,
- SkyLookupModule,
- ],
-})
-export class DemoComponent implements OnInit {
- public favoritesForm: FormGroup<{
- favoriteNames: FormControl;
- }>;
-
- public showMoreConfig: SkyLookupShowMoreConfig = {
- nativePickerConfig: {
- selectionDescriptor: 'names',
- },
- };
-
- readonly #svc = inject(DemoService);
- readonly #waitSvc = inject(SkyWaitService);
-
- constructor() {
- const names = new FormControl([{ name: 'Shirley' }], {
- validators: [
- (control: AbstractControl): ValidationErrors => {
- if (
- control.value?.some((person: Person) => !person.name.match(/e/i))
- ) {
- return { letterE: true };
- }
-
- return {};
- },
- ],
- });
-
- this.favoritesForm = inject(FormBuilder).group({
- favoriteNames: names,
- });
- }
-
- public ngOnInit(): void {
- // If you need to execute some logic after the lookup values change,
- // subscribe to Angular's built-in value changes observable.
- this.favoritesForm.valueChanges.subscribe((changes) => {
- console.log('Lookup value changes:', changes);
- });
- }
-
- protected onSubmit(): void {
- alert('Form submitted with: ' + JSON.stringify(this.favoritesForm.value));
- }
-
- protected searchAsync(args: SkyAutocompleteSearchAsyncArgs): void {
- // In a real-world application the search service might return an Observable
- // created by calling HttpClient.get(). Assigning that Observable to the result
- // allows the lookup component to cancel the web request if it does not complete
- // before the user searches again.
- args.result = this.#svc.search(args.searchText).pipe(
- map((result) => ({
- hasMore: result.hasMore,
- items: result.people,
- totalCount: result.totalCount,
- })),
- );
- }
-
- protected addClick(args: SkyLookupAddClickEventArgs): void {
- const person: Person = {
- name: 'Newman',
- };
-
- this.#waitSvc.blockingWrap(this.#svc.addPerson(person)).subscribe(() => {
- args.itemAdded({
- item: person,
- });
- });
- }
-}
diff --git a/apps/code-examples/src/app/code-examples/lookup/lookup/async/person.ts b/apps/code-examples/src/app/code-examples/lookup/lookup/async/person.ts
deleted file mode 100644
index 3763f53ace..0000000000
--- a/apps/code-examples/src/app/code-examples/lookup/lookup/async/person.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export interface Person {
- name: string;
-}
diff --git a/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.component.html b/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.component.html
index 1335134c00..b31a0a9ae9 100644
--- a/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.component.html
+++ b/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.component.html
@@ -13,12 +13,11 @@
>
{
+ let mockSvc!: jasmine.SpyObj
;
+
async function setupTest(): Promise<{
lookupHarness: SkyLookupHarness;
fixture: ComponentFixture;
@@ -25,8 +114,18 @@ describe('Lookup custom picker demo', () => {
}
beforeEach(() => {
+ // Create a mock search service. In a real-world application, the search
+ // service would make a web request which should be avoided in unit tests.
+ mockSvc = jasmine.createSpyObj('DemoService', ['search']);
+
TestBed.configureTestingModule({
imports: [DemoComponent, NoopAnimationsModule],
+ providers: [
+ {
+ provide: DemoService,
+ useValue: mockSvc,
+ },
+ ],
});
});
@@ -41,6 +140,19 @@ describe('Lookup custom picker demo', () => {
it('should update the form control when a favorite name is selected', async () => {
const { lookupHarness, fixture } = await setupTest();
+ mockSvc.search.and.callFake(() =>
+ of({
+ hasMore: false,
+ people: [
+ {
+ name: 'Abed',
+ formal: 'Mr. Nadir',
+ },
+ ],
+ totalCount: 1,
+ }),
+ );
+
await lookupHarness.enterText('Be');
const allResultHarnesses = await lookupHarness.getSearchResults();
@@ -61,6 +173,14 @@ describe('Lookup custom picker demo', () => {
it('should use a custom picker', async () => {
const { lookupHarness, fixture } = await setupTest();
+ mockSvc.search.and.callFake(() =>
+ of({
+ hasMore: false,
+ people: people,
+ totalCount: 20,
+ }),
+ );
+
// Show the custom picker.
await lookupHarness.clickShowMoreButton();
diff --git a/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.component.ts b/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.component.ts
index 62129758b7..e3ee7018c4 100644
--- a/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.component.ts
+++ b/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.component.ts
@@ -8,14 +8,19 @@ import {
ReactiveFormsModule,
} from '@angular/forms';
import { SkyInputBoxModule } from '@skyux/forms';
+import { SkyWaitService } from '@skyux/indicators';
import {
- SkyAutocompleteSearchFunctionFilter,
+ SkyAutocompleteSearchAsyncArgs,
+ SkyLookupAddClickEventArgs,
SkyLookupModule,
SkyLookupShowMoreConfig,
SkyLookupShowMoreCustomPickerContext,
} from '@skyux/lookup';
import { SkyModalService } from '@skyux/modals';
+import { map } from 'rxjs/operators';
+
+import { DemoService } from './demo.service';
import { Person } from './person';
import { PickerModalComponent } from './picker-modal.component';
@@ -37,107 +42,23 @@ export class DemoComponent implements OnInit {
}>;
protected showMoreConfig: SkyLookupShowMoreConfig;
- protected searchFilters: SkyAutocompleteSearchFunctionFilter[];
-
- protected people: Person[] = [
- {
- name: 'Abed',
- formal: 'Mr. Nadir',
- },
- {
- name: 'Alex',
- formal: 'Mr. Osbourne',
- },
- {
- name: 'Ben',
- formal: 'Mr. Chang',
- },
- {
- name: 'Britta',
- formal: 'Ms. Perry',
- },
- {
- name: 'Buzz',
- formal: 'Mr. Hickey',
- },
- {
- name: 'Craig',
- formal: 'Mr. Pelton',
- },
- {
- name: 'Elroy',
- formal: 'Mr. Patashnik',
- },
- {
- name: 'Garrett',
- formal: 'Mr. Lambert',
- },
- {
- name: 'Ian',
- formal: 'Mr. Duncan',
- },
- {
- name: 'Jeff',
- formal: 'Mr. Winger',
- },
- {
- name: 'Leonard',
- formal: 'Mr. Rodriguez',
- },
- {
- name: 'Neil',
- formal: 'Mr. Neil',
- },
- {
- name: 'Pierce',
- formal: 'Mr. Hawthorne',
- },
- {
- name: 'Preston',
- formal: 'Mr. Koogler',
- },
- {
- name: 'Rachel',
- formal: 'Ms. Rachel',
- },
- {
- name: 'Shirley',
- formal: 'Ms. Bennett',
- },
- {
- name: 'Todd',
- formal: 'Mr. Jacobson',
- },
- {
- name: 'Troy',
- formal: 'Mr. Barnes',
- },
- {
- name: 'Vaughn',
- formal: 'Mr. Miller',
- },
- {
- name: 'Vicki',
- formal: 'Ms. Jenkins',
- },
- ];
readonly #modalSvc = inject(SkyModalService);
+ readonly #svc = inject(DemoService);
+ readonly #waitSvc = inject(SkyWaitService);
constructor() {
+ const names = new FormControl([
+ {
+ name: 'Shirley',
+ formal: 'Ms. Bennett',
+ },
+ ]);
+
this.favoritesForm = inject(FormBuilder).group({
- favoriteNames: [[this.people[15]]],
+ favoriteNames: names,
});
- this.searchFilters = [
- (_, item: Person): boolean => {
- const names = this.favoritesForm.value.favoriteNames;
-
- // Only show people in the search results that have not been chosen already.
- return !names?.some((option) => option.name === item.name);
- },
- ];
-
this.showMoreConfig = {
customPicker: {
open: (context): void => {
@@ -171,8 +92,31 @@ export class DemoComponent implements OnInit {
});
}
- protected onAddButtonClicked(): void {
- alert('Add button clicked!');
+ protected searchAsync(args: SkyAutocompleteSearchAsyncArgs): void {
+ // In a real-world application the search service might return an Observable
+ // created by calling HttpClient.get(). Assigning that Observable to the result
+ // allows the lookup component to cancel the web request if it does not complete
+ // before the user searches again.
+ args.result = this.#svc.search(args.searchText).pipe(
+ map((result) => ({
+ hasMore: result.hasMore,
+ items: result.people,
+ totalCount: result.totalCount,
+ })),
+ );
+ }
+
+ protected addClick(args: SkyLookupAddClickEventArgs): void {
+ const person: Person = {
+ name: 'Newman',
+ formal: 'Mr. Parker',
+ };
+
+ this.#waitSvc.blockingWrap(this.#svc.addPerson(person)).subscribe(() => {
+ args.itemAdded({
+ item: person,
+ });
+ });
}
protected onSubmit(): void {
diff --git a/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.service.ts b/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.service.ts
new file mode 100644
index 0000000000..e26d31eb38
--- /dev/null
+++ b/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/demo.service.ts
@@ -0,0 +1,121 @@
+import { Injectable } from '@angular/core';
+
+import { Observable, of } from 'rxjs';
+import { delay } from 'rxjs/operators';
+
+import { Person } from './person';
+import { LookupAsyncDemoSearchResults } from './search-results';
+
+const people: Person[] = [
+ {
+ name: 'Abed',
+ formal: 'Mr. Nadir',
+ },
+ {
+ name: 'Alex',
+ formal: 'Mr. Osbourne',
+ },
+ {
+ name: 'Ben',
+ formal: 'Mr. Chang',
+ },
+ {
+ name: 'Britta',
+ formal: 'Ms. Perry',
+ },
+ {
+ name: 'Buzz',
+ formal: 'Mr. Hickey',
+ },
+ {
+ name: 'Craig',
+ formal: 'Mr. Pelton',
+ },
+ {
+ name: 'Elroy',
+ formal: 'Mr. Patashnik',
+ },
+ {
+ name: 'Garrett',
+ formal: 'Mr. Lambert',
+ },
+ {
+ name: 'Ian',
+ formal: 'Mr. Duncan',
+ },
+ {
+ name: 'Jeff',
+ formal: 'Mr. Winger',
+ },
+ {
+ name: 'Leonard',
+ formal: 'Mr. Rodriguez',
+ },
+ {
+ name: 'Neil',
+ formal: 'Mr. Neil',
+ },
+ {
+ name: 'Pierce',
+ formal: 'Mr. Hawthorne',
+ },
+ {
+ name: 'Preston',
+ formal: 'Mr. Koogler',
+ },
+ {
+ name: 'Rachel',
+ formal: 'Ms. Rachel',
+ },
+ {
+ name: 'Shirley',
+ formal: 'Ms. Bennett',
+ },
+ {
+ name: 'Todd',
+ formal: 'Mr. Jacobson',
+ },
+ {
+ name: 'Troy',
+ formal: 'Mr. Barnes',
+ },
+ {
+ name: 'Vaughn',
+ formal: 'Mr. Miller',
+ },
+ {
+ name: 'Vicki',
+ formal: 'Ms. Jenkins',
+ },
+];
+
+@Injectable({
+ providedIn: 'root',
+})
+export class DemoService {
+ public search(searchText: string): Observable {
+ // Simulate a network call with latency. A real-world application might
+ // use Angular's HttpClient to create an Observable from a call to a
+ // web service.
+ searchText = searchText.toUpperCase();
+
+ const matchingPeople = people.filter((person) =>
+ person.name.toUpperCase().includes(searchText),
+ );
+
+ return of({
+ hasMore: false,
+ people: matchingPeople,
+ totalCount: matchingPeople.length,
+ }).pipe(delay(800));
+ }
+
+ public addPerson(person: Person): Observable {
+ // Simulate adding a person with a network call.
+ if (!people.some((item) => item.name === person.name)) {
+ people.unshift(person);
+ }
+
+ return of(1).pipe(delay(800));
+ }
+}
diff --git a/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/picker-modal.component.html b/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/picker-modal.component.html
index 02b66f7e46..b8924f54ef 100644
--- a/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/picker-modal.component.html
+++ b/apps/code-examples/src/app/code-examples/lookup/lookup/custom-picker/picker-modal.component.html
@@ -1,26 +1,32 @@
Names
-
+ @if (peopleForm) {
+
+ } @else {
+
+
+
+ }