Skip to content

Commit 8a1cbdd

Browse files
committed
Merge branch 'master' into m-t-N
2 parents 7a14d77 + c1380b5 commit 8a1cbdd

30 files changed

+595
-210
lines changed

package-lock.json

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"@storybook/theming": "7.0.18",
9595
"@types/jasmine": "3.8.0",
9696
"@types/node": "12.20.55",
97+
"@types/resize-observer-browser": "^0.1.7",
9798
"babel-loader": "8.0.5",
9899
"chai": "4.2.0",
99100
"codelyzer": "6.0.0",

src/combobox/combobox.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,9 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit, OnD
728728
this.showClearButton = !!searchString;
729729
this.view.filterBy(searchString);
730730
if (searchString !== "") {
731-
this.openDropdown();
731+
if (!this.open) {
732+
this.openDropdown();
733+
}
732734
} else {
733735
this.selectedValue = "";
734736
if (this.type === "multi" &&

src/dialog/dialog.directive.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,11 +289,9 @@ export class DialogDirective implements OnInit, OnDestroy, OnChanges {
289289
* Helper method to close the dialogRef.
290290
*/
291291
close(meta: CloseMeta = { reason: CloseReasons.interaction }) {
292-
setTimeout(() => {
293-
if (this.dialogRef) {
294-
this.dialogRef.instance.doClose(meta);
295-
}
296-
});
292+
if (this.dialogRef) {
293+
this.dialogRef.instance.doClose(meta);
294+
}
297295
}
298296

299297
/**

src/dropdown/list/dropdown-list.component.ts

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
121121
/**
122122
* The list items belonging to the `DropdownList`.
123123
*/
124-
@Input() set items (value: Array<ListItem> | Observable<Array<ListItem>>) {
124+
@Input() set items(value: Array<ListItem> | Observable<Array<ListItem>>) {
125125
if (isObservable(value)) {
126126
if (this._itemsSubscription) {
127127
this._itemsSubscription.unsubscribe();
@@ -283,6 +283,11 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
283283
filterBy(query = "") {
284284
if (query) {
285285
this.displayItems = this.getListItems().filter(item => item.content.toLowerCase().includes(query.toLowerCase()));
286+
// Reset index if items were found
287+
// Prevent selecting index in list that are undefined.
288+
if (this.displayItems) {
289+
this.index = 0;
290+
}
286291
} else {
287292
this.displayItems = this.getListItems();
288293
}
@@ -328,23 +333,26 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
328333
* Returns the `HTMLElement` for the item that is subsequent to the selected item.
329334
*/
330335
getNextElement(): HTMLElement {
331-
if (this.index < this.displayItems.length - 1) {
332-
this.index++;
333-
}
334-
let item = this.displayItems[this.index];
335-
if (item.disabled) {
336-
return this.getNextElement();
336+
// Only return native elements if they are rendered
337+
const elemList = this.listElementList ? this.listElementList.toArray() : [];
338+
if (!elemList.length) {
339+
return null;
337340
}
338341

339-
let elemList = this.listElementList ? this.listElementList.toArray() : [];
340-
341-
// TODO: update to optional chaining after upgrading typescript
342-
// to v3.7+
343-
if (elemList[this.index] && elemList[this.index].nativeElement) {
344-
return elemList[this.index].nativeElement;
345-
} else {
346-
return null;
342+
/**
343+
* Start checking from next index
344+
* Continue looping through the list until a non disabeled element is found or
345+
* end of list is reached
346+
*/
347+
for (let i = this.index + 1; i < elemList.length; i++) {
348+
// If the values in the list are not disabled
349+
if (!this.displayItems[i].disabled) {
350+
this.index = i;
351+
return elemList[i].nativeElement;
352+
}
347353
}
354+
355+
return elemList[this.index].nativeElement;
348356
}
349357

350358
/**
@@ -368,23 +376,26 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
368376
* Returns the `HTMLElement` for the item that precedes the selected item.
369377
*/
370378
getPrevElement(): HTMLElement {
371-
if (this.index > 0) {
372-
this.index--;
373-
}
374-
let item = this.displayItems[this.index];
375-
if (item.disabled) {
376-
return this.getPrevElement();
379+
// Only return native elements if they are rendered
380+
const elemList = this.listElementList ? this.listElementList.toArray() : [];
381+
if (!elemList.length) {
382+
return null;
377383
}
378384

379-
let elemList = this.listElementList ? this.listElementList.toArray() : [];
380-
381-
// TODO: update to optional chaining after upgrading typescript
382-
// to v3.7+
383-
if (elemList[this.index] && elemList[this.index].nativeElement) {
384-
return elemList[this.index].nativeElement;
385-
} else {
386-
return null;
385+
/**
386+
* Start checking from next index
387+
* Continue looping through the list until a non disabeled element is found or
388+
* end of list is reached
389+
*/
390+
for (let i = this.index - 1; i < this.index && i >= 0; i--) {
391+
// If the values in the list are not disabled
392+
if (!this.displayItems[i].disabled) {
393+
this.index = i;
394+
return elemList[i].nativeElement;
395+
}
387396
}
397+
398+
return elemList[this.index].nativeElement;
388399
}
389400

390401
/**
@@ -502,7 +513,7 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
502513
event.preventDefault();
503514
if (event.key === "ArrowDown") {
504515
if (this.hasNextElement()) {
505-
this.getNextElement().scrollIntoView({block: "end"});
516+
this.getNextElement().scrollIntoView({ block: "end" });
506517
} else {
507518
this.blurIntent.emit("bottom");
508519
}
@@ -524,7 +535,7 @@ export class DropdownList implements AbstractDropdownView, AfterViewInit, OnDest
524535
*/
525536
doClick(event, item) {
526537
event.preventDefault();
527-
if (!item.disabled) {
538+
if (item && !item.disabled) {
528539
this.list.nativeElement.focus();
529540
if (this.type === "single") {
530541
item.selected = true;

src/file-uploader/file-uploader.component.ts

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ export class FileUploader implements ControlValueAccessor {
174174
}
175175

176176
/**
177-
* Specifies the property to be used as the return value to `ngModel`
177+
* Specifies the property to be used as the return value to `ngModel` and reactive forms.
178+
* Updates `this.files`.
178179
*/
179180
get value(): Set<FileItem> {
180181
return this.files;
@@ -243,35 +244,37 @@ export class FileUploader implements ControlValueAccessor {
243244
event.stopPropagation();
244245
event.preventDefault();
245246

246-
const transferredFiles = Array.from(event.dataTransfer.files);
247+
const transferredFiles: Array<File> = Array.from(event.dataTransfer.files);
248+
const newFiles = new Set<FileItem>(this.files);
247249

248250
transferredFiles.filter(({ name, type }) => {
249251
// Get the file extension and add a "." to the beginning.
250252
const fileExtension = name.split(".").pop().replace(/^/, ".");
251253
// Check if the accept array contains the mime type or extension of the file.
252254
return this.accept.includes(type) || this.accept.includes(fileExtension) || !this.accept.length;
253255
}).forEach(file => {
254-
if (!this.files.size || this.multiple) {
256+
if (!newFiles.size || this.multiple) {
255257
const fileItem = this.createFileItem(file);
256-
this.files.add(fileItem);
258+
newFiles.add(fileItem);
257259
}
258260
});
259261

260-
this.filesChange.emit(this.files);
261-
this.value = this.files;
262+
this.value = newFiles;
263+
this.filesChange.emit(newFiles);
262264
this.dragOver = false;
263265
}
264266

265267
removeFile(fileItem) {
266-
let shouldEmit = true;
267-
if (this.files) {
268-
shouldEmit = this.files.has(fileItem);
269-
this.files.delete(fileItem);
268+
// Deleting an item from this.files removes the <ibm-file> component,
269+
// which triggers its ngOnDestroy(), which fires the (remove) event again.
270+
// So, (remove) may double-fire and we need to handle it here.
271+
if (this.files && this.files.has(fileItem)) {
272+
const newFiles = new Set<FileItem>(this.files);
273+
newFiles.delete(fileItem);
274+
this.filesChange.emit(newFiles);
275+
this.value = newFiles;
270276
}
271277
this.fileInput.nativeElement.value = "";
272-
if (shouldEmit) {
273-
this.filesChange.emit(this.files);
274-
}
275278
}
276279

277280
public isTemplate(value) {

src/forms/forms.module.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import { ButtonModule } from "carbon-components-angular/button";
1616
ToggleModule,
1717
RadioModule,
1818
InputModule,
19-
ButtonModule
19+
ButtonModule,
20+
InputModule
2021
],
2122
imports: [
2223
CommonModule,

src/forms/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ export {
2323
InputModule,
2424
Label,
2525
TextArea,
26-
TextInput
26+
TextInput,
27+
TextInputLabelComponent,
28+
TextareaLabelComponent
2729
} from "carbon-components-angular/input";
2830
export {
2931
ButtonModule,

src/input/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ export * from "./input.directive";
22
export * from "./input.module";
33
export * from "./label.component";
44
export * from "./text-area.directive";
5+
export * from "./text-input-label.component";
6+
export * from "./textarea-label.component";

src/input/input.module.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@ import { CommonModule } from "@angular/common";
77
import { Label } from "./label.component";
88
import { TextInput } from "./input.directive";
99
import { TextArea } from "./text-area.directive";
10+
import { TextareaLabelComponent } from "./textarea-label.component";
11+
import { TextInputLabelComponent } from "./text-input-label.component";
1012
import { IconModule } from "carbon-components-angular/icon";
1113

1214
@NgModule({
1315
declarations: [
1416
Label,
1517
TextInput,
16-
TextArea
18+
TextArea,
19+
TextareaLabelComponent,
20+
TextInputLabelComponent
1721
],
1822
exports: [
1923
Label,
24+
TextareaLabelComponent,
25+
TextInputLabelComponent,
2026
TextInput,
2127
TextArea
2228
],

0 commit comments

Comments
 (0)