Skip to content

Commit 3cde947

Browse files
authored
Merge branch 'master' into master
2 parents c1ba002 + 46cb2e6 commit 3cde947

16 files changed

+427
-43
lines changed

src/combobox/combobox.component.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -546,8 +546,8 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit, OnD
546546
this.elementRef.nativeElement.querySelector("input").focus();
547547
this.view.filterBy("");
548548
this.selected.emit(event.item);
549+
this.closeDropdown();
549550
}
550-
this.closeDropdown();
551551
}
552552
});
553553
// update the rest of combobox with any pre-selected items
@@ -691,8 +691,14 @@ export class ComboBox implements OnChanges, AfterViewInit, AfterContentInit, OnD
691691
});
692692
this.view.items = this.items;
693693
this.updatePills();
694-
// clearSelected can only fire on type=multi
695-
// so we just emit getSelected() (just in case there's any disabled but selected items)
694+
/**
695+
* @todo - In next major version update to the following:
696+
* const selected = this.type === "multi" ? this.view.getSelected() : undefined;
697+
*
698+
* Previously it returned an empty array even for type === 'single'
699+
* Also resolve #ref-1245723
700+
*/
701+
// On type=multi we just emit getSelected() (just in case there's any disabled but selected items)
696702
const selected = this.view.getSelected();
697703

698704
// in case there are disabled items they should be mapped according to itemValueKey

src/combobox/stories/app-mock-query-search.component.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,43 @@ import { Component } from "@angular/core";
66
<cds-combo-box
77
appendInline="true"
88
[items]="filterItems"
9-
(search)="onSearch($event)">
9+
(search)="onSearch($event)"
10+
(selected)="selected($event)">
1011
<cds-dropdown-list></cds-dropdown-list>
1112
</cds-combo-box>
1213
`
1314
})
1415
export class MockQueryCombobox {
1516
filterItems: any = [];
17+
currentlySelected: any;
1618

1719
onSearch() {
1820
// Call API or search through items list
1921
setTimeout(() => {
20-
this.filterItems = [
22+
const array = [
2123
{ content: `Random ${Math.random()}` },
2224
{ content: `Random ${Math.random()}` },
2325
{ content: `Random ${Math.random()}` },
2426
{ content: `Random ${Math.random()}` }
2527
];
28+
29+
// Include current selected in the list to avoid auto clear
30+
if (this.currentlySelected) {
31+
array.push(this.currentlySelected);
32+
}
33+
this.filterItems = array;
2634
}, 1000);
2735
}
36+
37+
selected(event: any) {
38+
/**
39+
* #ref-1245723
40+
* Update this on major release
41+
*/
42+
if (Array.isArray(event) && !event.length) {
43+
this.currentlySelected = undefined;
44+
} else {
45+
this.currentlySelected = event;
46+
}
47+
}
2848
}

src/context-menu/context-menu-item.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ export class ContextMenuItemComponent implements OnInit, AfterContentInit, OnDes
117117
@HostListener("click", ["$event"])
118118
handleClick(event: MouseEvent & KeyboardEvent) {
119119
event.stopPropagation();
120+
if (this.disabled) {
121+
return;
122+
}
120123
if (this.hasChildren) {
121124
this.openSubMenu();
122125
this.childContextMenu.focusMenu();

src/datepicker/datepicker.component.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,9 +510,11 @@ export class DatePicker implements
510510

511511
const calendarContainer = this.flatpickrInstance.calendarContainer;
512512
const dayElement = calendarContainer && calendarContainer.querySelector(".flatpickr-day[tabindex]");
513+
const selectedDateElem = calendarContainer && calendarContainer.querySelector(".selected");
514+
const todayDateElem = calendarContainer && calendarContainer.querySelector(".today");
513515

514516
if (dayElement) {
515-
dayElement.focus();
517+
(todayDateElem || selectedDateElem || dayElement).focus();
516518

517519
// If the user manually inputs a value into the date field and presses arrow down,
518520
// datepicker input onchange will be triggered when focus is removed from it and
@@ -591,6 +593,7 @@ export class DatePicker implements
591593

592594
// add day classes and special case the "today" element based on `this.value`
593595
Array.from(dayContainer).forEach(element => {
596+
element.setAttribute("role", "button");
594597
element.classList.add("cds--date-picker__day");
595598
if (!this.value) {
596599
return;
@@ -606,7 +609,7 @@ export class DatePicker implements
606609
protected updateAttributes() {
607610
const calendarContainer = document.querySelectorAll(".flatpickr-calendar");
608611
Array.from(calendarContainer).forEach(calendar => {
609-
calendar.setAttribute("role", "region");
612+
calendar.setAttribute("role", "application");
610613
calendar.setAttribute("aria-label", this.ariaLabel);
611614
});
612615
}

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,10 @@ const noop = () => { };
7373
<ng-container *ngFor="let fileItem of files">
7474
<cds-file
7575
[fileItem]="fileItem"
76-
(remove)="removeFile(fileItem)"
77-
[size]="fileItemSize">
76+
[nameTpl]="fileNameTpl"
77+
[actionsTpl]="fileActionsTpl"
78+
[size]="fileItemSize"
79+
(remove)="removeFile(fileItem)">
7880
</cds-file>
7981
</ng-container>
8082
</div>
@@ -164,6 +166,14 @@ export class FileUploader implements ControlValueAccessor {
164166
* Set to `true` to disable upload button
165167
*/
166168
@Input() disabled = false;
169+
/**
170+
* Custom template used to render the file name of uploaded files
171+
*/
172+
@Input() fileNameTpl: TemplateRef<unknown>;
173+
/**
174+
* Custom template used to render the file actions of uploaded files
175+
*/
176+
@Input() fileActionsTpl: TemplateRef<unknown>;
167177

168178
@Output() filesChange = new EventEmitter<any>();
169179

src/file-uploader/file-uploader.stories.ts

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
/* tslint:disable variable-name */
22

33
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
4-
import { moduleMetadata, Meta } from "@storybook/angular";
5-
import { FileUploaderModule, FileUploader } from "./";
6-
import { NotificationModule } from "../notification";
4+
import { Meta, moduleMetadata } from "@storybook/angular";
75
import { ButtonModule } from "../button";
6+
import { IconModule } from "../icon";
7+
import { NotificationModule } from "../notification";
8+
import { FileUploader, FileUploaderModule } from "./";
89
import {
10+
CustomFileIconsModule,
11+
DragAndDropStory,
912
FileUploaderStory,
13+
FileUploaderWithCustomFileStory,
1014
NgModelFileUploaderStory,
11-
DragAndDropStory,
1215
ReactiveFormsStory
1316
} from "./stories";
1417

@@ -18,16 +21,19 @@ export default {
1821
moduleMetadata({
1922
declarations: [
2023
FileUploaderStory,
24+
FileUploaderWithCustomFileStory,
2125
NgModelFileUploaderStory,
2226
DragAndDropStory,
2327
ReactiveFormsStory
2428
],
2529
imports: [
30+
ButtonModule,
31+
CustomFileIconsModule,
2632
FileUploaderModule,
2733
FormsModule,
28-
ReactiveFormsModule,
34+
IconModule,
2935
NotificationModule,
30-
ButtonModule
36+
ReactiveFormsModule
3137
]
3238
})
3339
],
@@ -81,6 +87,37 @@ const Template = (args) => ({
8187
});
8288
export const Basic = Template.bind({});
8389

90+
const CustomFile = (args) => ({
91+
props: args,
92+
template: `
93+
<!--
94+
app-* components are for demo purposes only.
95+
You can create your own implementation by using the component source found at:
96+
https://github.com/IBM/carbon-components-angular/tree/master/src/file-uploader/stories/uploader-custom-file.component.ts
97+
-->
98+
<app-file-uploader-with-custom-file
99+
[title]="title"
100+
[description]="description"
101+
[buttonText]="buttonText"
102+
[buttonType]="buttonType"
103+
[accept]="accept"
104+
[multiple]="multiple"
105+
[size]="size"
106+
[fileItemSize]="fileItemSize"
107+
[disabled]="disabled">
108+
</app-file-uploader-with-custom-file>
109+
`
110+
});
111+
export const UploaderWithCustomFile = CustomFile.bind({});
112+
UploaderWithCustomFile.argTypes = {
113+
size: {
114+
control: false
115+
},
116+
buttonType: {
117+
control: false
118+
}
119+
};
120+
84121
const DragAndDropTemplate = (args) => ({
85122
props: args,
86123
template: `

src/file-uploader/file.component.ts

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import {
22
Component,
3-
Input,
4-
Output,
53
EventEmitter,
64
HostBinding,
7-
OnDestroy
5+
Input,
6+
OnDestroy,
7+
Output,
8+
TemplateRef
89
} from "@angular/core";
910

1011
import { I18n } from "carbon-components-angular/i18n";
@@ -13,43 +14,65 @@ import { FileItem } from "./file-item.interface";
1314
@Component({
1415
selector: "cds-file, ibm-file",
1516
template: `
16-
<p class="cds--file-filename" [title]="fileItem.file.name">{{fileItem.file.name}}</p>
17-
<span
18-
*ngIf="fileItem.state === 'edit'"
19-
class="cds--file__state-container">
17+
<p class="cds--file-filename" [title]="fileItem.file.name">
18+
<ng-template
19+
*ngIf="isTemplate(nameTpl); else defaultName"
20+
[ngTemplateOutlet]="nameTpl"
21+
[ngTemplateOutletContext]="{ $implicit: fileItem }">
22+
</ng-template>
23+
<ng-template #defaultName>{{ fileItem.file.name }}</ng-template>
24+
</p>
25+
<span *ngIf="fileItem.state === 'edit'" class="cds--file__state-container">
2026
<svg
2127
*ngIf="isInvalidText"
2228
cdsIcon="warning--filled"
2329
class="cds--file--invalid"
2430
size="16">
2531
</svg>
26-
<button
27-
type="button"
28-
class="cds--file-close"
29-
[attr.aria-label]="translations.REMOVE_BUTTON"
30-
tabindex="0"
31-
(click)="remove.emit()"
32-
(keyup.enter)="remove.emit()"
33-
(keyup.space)="remove.emit()">
34-
<svg cdsIcon="close" size="16"></svg>
35-
</button>
32+
<ng-template
33+
*ngIf="isTemplate(actionsTpl); else defaultActions"
34+
[ngTemplateOutlet]="actionsTpl"
35+
[ngTemplateOutletContext]="{ $implicit: fileItem }">
36+
</ng-template>
37+
<ng-template #defaultActions>
38+
<button
39+
type="button"
40+
cdsButton="ghost"
41+
iconOnly="true"
42+
[size]="size"
43+
[attr.aria-label]="translations.REMOVE_BUTTON"
44+
(click)="remove.emit()"
45+
(keyup.enter)="remove.emit()"
46+
(keyup.space)="remove.emit()">
47+
<svg cdsIcon="trash-can" size="16"></svg>
48+
</button>
49+
</ng-template>
3650
</span>
3751
<span *ngIf="fileItem.state === 'upload'">
3852
<div class="cds--inline-loading__animation">
3953
<cds-loading size="sm"></cds-loading>
4054
</div>
4155
</span>
42-
<span *ngIf="fileItem.state === 'complete'" class="cds--file__state-container">
56+
<span
57+
*ngIf="fileItem.state === 'complete'"
58+
class="cds--file__state-container">
4359
<svg
4460
cdsIcon="checkmark--filled"
4561
size="16"
4662
class="cds--file-complete"
4763
[ariaLabel]="translations.CHECKMARK">
4864
</svg>
4965
</span>
50-
<div class="cds--form-requirement" role="alert" *ngIf="fileItem.invalid">
51-
<div class="cds--form-requirement__title">{{fileItem.invalidTitle}}</div>
52-
<p class="cds--form-requirement__supplement">{{fileItem.invalidText}}</p>
66+
<div
67+
class="cds--form-requirement"
68+
role="alert"
69+
*ngIf="fileItem.invalid">
70+
<div class="cds--form-requirement__title">
71+
{{ fileItem.invalidTitle }}
72+
</div>
73+
<p class="cds--form-requirement__supplement">
74+
{{ fileItem.invalidText }}
75+
</p>
5376
</div>
5477
`
5578
})
@@ -65,6 +88,16 @@ export class FileComponent implements OnDestroy {
6588

6689
@Input() size: "sm" | "md" | "lg" = "lg";
6790

91+
/**
92+
* A custom template for the file name
93+
*/
94+
@Input() nameTpl: TemplateRef<unknown>;
95+
96+
/**
97+
* A custom template for the available file actions
98+
*/
99+
@Input() actionsTpl: TemplateRef<unknown>;
100+
68101
@Output() remove = new EventEmitter();
69102

70103
@HostBinding("class.cds--file__selected-file") selectedFile = true;
@@ -84,8 +117,13 @@ export class FileComponent implements OnDestroy {
84117
@HostBinding("class.cds--file__selected-file--lg") get fileSizeLarge() {
85118
return this.size === "lg";
86119
}
120+
87121
constructor(protected i18n: I18n) {}
88122

123+
public isTemplate(value: unknown): boolean {
124+
return value instanceof TemplateRef;
125+
}
126+
89127
ngOnDestroy() {
90128
this.remove.emit();
91129
}

0 commit comments

Comments
 (0)