aboutsummaryrefslogtreecommitdiffstats
path: root/src/angular/form-elements/dropdown
diff options
context:
space:
mode:
Diffstat (limited to 'src/angular/form-elements/dropdown')
-rw-r--r--src/angular/form-elements/dropdown/dropdown-models.ts18
-rw-r--r--src/angular/form-elements/dropdown/dropdown-trigger.directive.ts17
-rw-r--r--src/angular/form-elements/dropdown/dropdown.component.html.ts59
-rw-r--r--src/angular/form-elements/dropdown/dropdown.component.spec.ts71
-rw-r--r--src/angular/form-elements/dropdown/dropdown.component.ts149
5 files changed, 314 insertions, 0 deletions
diff --git a/src/angular/form-elements/dropdown/dropdown-models.ts b/src/angular/form-elements/dropdown/dropdown-models.ts
new file mode 100644
index 0000000..fa8dc23
--- /dev/null
+++ b/src/angular/form-elements/dropdown/dropdown-models.ts
@@ -0,0 +1,18 @@
+export enum DropDownTypes {
+ Regular,
+ Headless,
+ Auto
+}
+
+export enum DropDownOptionType {
+ Simple, // default
+ Header,
+ Disable,
+ HorizontalLine
+}
+
+export interface IDropDownOption {
+ value: any;
+ label: string;
+ type?: DropDownOptionType;
+}
diff --git a/src/angular/form-elements/dropdown/dropdown-trigger.directive.ts b/src/angular/form-elements/dropdown/dropdown-trigger.directive.ts
new file mode 100644
index 0000000..94ab3bc
--- /dev/null
+++ b/src/angular/form-elements/dropdown/dropdown-trigger.directive.ts
@@ -0,0 +1,17 @@
+import { Directive, Input, HostBinding, HostListener } from "@angular/core";
+import { DropDownComponent } from "./dropdown.component";
+
+@Directive({
+ selector: '[SdcDropdownTrigger]'
+})
+
+export class DropDownTriggerDirective {
+
+ @HostBinding('class.js-sdc-dropdown--toggle-hook') true;
+ @Input() dropDown: DropDownComponent;
+
+ @HostListener('click', ['$event']) onClick = (event) => {
+ this.dropDown.toggleDropdown(event);
+ }
+
+}
diff --git a/src/angular/form-elements/dropdown/dropdown.component.html.ts b/src/angular/form-elements/dropdown/dropdown.component.html.ts
new file mode 100644
index 0000000..a4247a4
--- /dev/null
+++ b/src/angular/form-elements/dropdown/dropdown.component.html.ts
@@ -0,0 +1,59 @@
+export default `
+<div class="sdc-dropdown" #dropDownWrapper
+ [ngClass]="{
+ 'headless': type === cIDropDownTypes.Headless,
+ 'sdc-dropdown__error': !valid,
+ 'open-bottom': show && bottomVisible,
+ 'open-top':show && !bottomVisible}">
+ <label *ngIf="label" class="sdc-dropdown__label" [ngClass]="{'required':required}">{{label}}</label>
+ <div class="sdc-dropdown__component-container">
+
+ <!--[DROP-DOWN AUTO HEADER START]-->
+ <div *ngIf="type===cIDropDownTypes.Auto" class="sdc-dropdown-auto__wrapper">
+ <input class="sdc-dropdown__header js-sdc-dropdown--toggle-hook"
+ [(ngModel)]="this.filterValue"
+ (ngModelChange)="filterOptions(this.filterValue)"
+ placeholder="{{this.selectedOption?.label || this.selectedOption?.value || placeHolder}}">
+ <svg-icon name="caret1-down-o" mode="secondary" size="small" (click)="toggleDropdown($event)"></svg-icon>
+ </div>
+ <!--[DROP-DOWN AUTO HEADER END]-->
+
+ <!--[DROP-DOWN REGULAR HEADER START]-->
+ <button *ngIf="type===cIDropDownTypes.Regular"
+ class="sdc-dropdown__header js-sdc-dropdown--toggle-hook"
+ (click)="toggleDropdown($event)"
+ [ngClass]="{'disabled': disabled, 'placeholder':!this.selectedOption}">
+ {{ this.selectedOption?.label || this.selectedOption?.value || placeHolder}}
+ <svg-icon name="caret1-down-o" mode="secondary" size="small"></svg-icon>
+ </button>
+ <!--[DROP-DOWN HEADER END]-->
+
+ <!--[DROP-DOWN OPTIONS START]-->
+ <div class="sdc-dropdown__options-wrapper--frame" [ngClass]="{
+ 'sdc-dropdown__options-wrapper--top':!bottomVisible,
+ 'sdc-dropdown__options-wrapper--uncollapsed':show
+ }">
+ <ul #optionsContainerElement *ngIf="options" class="sdc-dropdown__options-list" [ngClass]="{
+ 'sdc-dropdown__options-list--headless': headless,
+ 'sdc-dropdown__options-list--animation-init':animation_init
+ }">
+ <ng-container *ngFor="let option of options; let i = index">
+ <!--[Drop down item list or string list start]-->
+ <li *ngIf="option" class="sdc-dropdown__option"
+ [ngClass]="{
+ 'selected': option == selectedOption,
+ 'sdc-dropdown__option--group':isGroupDesign,
+ 'sdc-dropdown__option--header': option.type && option.type === cIDropDownOptionType.Header,
+ 'sdc-dropdown__option--disabled': option.type && option.type === cIDropDownOptionType.Disable,
+ 'sdc-dropdown__option--hr': option.type && option.type === cIDropDownOptionType.HorizontalLine
+ }"
+ (click)="selectOption(option.value, $event)">{{option.label || String(option.value)}}</li>
+ <!--[Drop down item list or string list end]-->
+ </ng-container>
+ </ul>
+ </div>
+ <!--[DROP-DOWN OPTIONS END]-->
+
+ </div>
+</div>
+`;
diff --git a/src/angular/form-elements/dropdown/dropdown.component.spec.ts b/src/angular/form-elements/dropdown/dropdown.component.spec.ts
new file mode 100644
index 0000000..1c0cb4d
--- /dev/null
+++ b/src/angular/form-elements/dropdown/dropdown.component.spec.ts
@@ -0,0 +1,71 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { DropDownComponent } from './dropdown.component';
+import { IDropDownOption, DropDownTypes } from "./dropdown-models";
+import { FormsModule } from "@angular/forms";
+import {SvgIconModule} from "../../svg-icon/svg-icon.module";
+
+
+const label:string = "DropDown example";
+const placeHolder:string = "Please choose option";
+const options:IDropDownOption[] = [
+ {
+ label:'First Option',
+ value: 'First Option'
+ },
+ {
+ label:'Second Option',
+ value: 'Second Option'
+ },
+ {
+ label:'Third Option',
+ value: 'Third Option'
+ }
+];
+
+describe('DropDown component', () => {
+ let fixture: ComponentFixture<DropDownComponent>;
+ let component: DropDownComponent;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ DropDownComponent ],
+ imports:[
+ FormsModule,
+ SvgIconModule
+ ]
+ }).compileComponents();
+ fixture = TestBed.createComponent(DropDownComponent);
+ component = fixture.componentInstance;
+
+ }));
+
+ beforeEach(()=>{
+ component.label = label;
+ component.placeHolder = placeHolder;
+ component.options = options;
+ component.type = DropDownTypes.Regular;
+ console.log('herer we got component', component)
+ fixture.detectChanges();
+ });
+
+ it('component should be created', () => {
+ expect(component).toBeTruthy();
+ });
+
+ it('component should export the selected value', () => {
+ const option = options[1];
+ component.selectOption(option);
+ fixture.detectChanges();
+ expect(component.selectedOption).toEqual(option);
+ });
+
+ it('component should have autocomplite', () => {
+ expect(component.options.length).toEqual(3);
+ component.type = DropDownTypes.Auto;
+ component.filterValue = 'testERrorotesttresadfadfasdfasf';
+ fixture.detectChanges();
+ component.filterOptions(component.filterValue);
+ expect(component.options.length).toEqual(0);
+ });
+
+});
diff --git a/src/angular/form-elements/dropdown/dropdown.component.ts b/src/angular/form-elements/dropdown/dropdown.component.ts
new file mode 100644
index 0000000..a23072f
--- /dev/null
+++ b/src/angular/form-elements/dropdown/dropdown.component.ts
@@ -0,0 +1,149 @@
+import { Component, EventEmitter, Input, Output, forwardRef, OnChanges, SimpleChanges, OnInit, ElementRef, ViewChild, AfterViewInit, HostListener, Renderer } from '@angular/core';
+import { IDropDownOption, DropDownOptionType, DropDownTypes } from "./dropdown-models";
+import { ValidatableComponent } from './../validation/validatable.component';
+import template from './dropdown.component.html';
+
+@Component({
+ selector: 'sdc-dropdown',
+ template: template
+})
+export class DropDownComponent extends ValidatableComponent implements OnChanges, OnInit {
+
+ @Output('changed') changeEmitter:EventEmitter<IDropDownOption> = new EventEmitter<IDropDownOption>();
+ @Input() label: string;
+ @Input() options: IDropDownOption[];
+ @Input() disabled: boolean;
+ @Input() placeHolder: string;
+ @Input() required: boolean;
+ @Input() maxHeight: number;
+ @Input() selectedOption: IDropDownOption;
+ @Input() type: DropDownTypes = DropDownTypes.Regular;
+ @ViewChild('dropDownWrapper') dropDownWrapper: ElementRef;
+ @ViewChild('optionsContainerElement') optionsContainerElement: ElementRef;
+ @HostListener('document:click', ['$event']) onClick(e) {
+ this.onClickDocument(e);
+ }
+
+ private bottomVisible = true;
+ private myRenderer: Renderer;
+
+ // Drop-down show/hide flag. default is false (closed)
+ public show = false;
+
+ // Export DropDownOptionType enum so we can use it on the template
+ public cIDropDownOptionType = DropDownOptionType;
+ public cIDropDownTypes = DropDownTypes;
+
+ // Configure unselectable option types
+ private unselectableOptions = [
+ DropDownOptionType.Disable,
+ DropDownOptionType.Header,
+ DropDownOptionType.HorizontalLine
+ ];
+
+ // Set or unset Group style on drop-down
+ public isGroupDesign = false;
+ public animation_init = false;
+ public allOptions: IDropDownOption[];
+ public filterValue: string;
+
+ constructor(public renderer: Renderer) {
+ super();
+ this.myRenderer = renderer;
+ this.maxHeight = 244;
+ this.filterValue = '';
+ }
+
+ ngOnInit(): void {
+ if (this.options) {
+ this.allOptions = this.options;
+ if (this.options.find(option => option.type === DropDownOptionType.Header)) {
+ this.isGroupDesign = true;
+ }
+ }
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ console.log("ngOnChanges");
+ if (changes.selectedOption && changes.selectedOption.currentValue !== changes.selectedOption.previousValue) {
+ if (typeof changes.selectedOption.currentValue === 'string' && this.isSelectable(changes.selectedOption.currentValue)) {
+ this.setSelected(changes.selectedOption.currentValue);
+ } else if (this.isSelectable(changes.selectedOption.currentValue.value)) {
+ this.setSelected(changes.selectedOption.currentValue.value);
+ } else {
+ this.setSelected(undefined);
+ }
+ }
+ }
+
+ public getValue(): any {
+ return this.selectedOption && this.selectedOption.value;
+ }
+
+ public selectOption = (option: IDropDownOption | string, event?): void => {
+ if (event) { event.stopPropagation(); }
+ if (this.type === DropDownTypes.Headless) {
+ // Hide the options when in headless mode and user select option.
+ this.myRenderer.setElementStyle(this.dropDownWrapper.nativeElement, 'display', 'none');
+ }
+ if (typeof option === 'string' && this.isSelectable(option)) {
+ this.setSelected(option);
+ } else if (this.isSelectable((option as IDropDownOption).value)) {
+ this.setSelected((option as IDropDownOption).value);
+ }
+ }
+
+ public toggleDropdown = (event?): void => {
+ if (event) { event.stopPropagation(); }
+ if (this.type === DropDownTypes.Headless) {
+ // Show the options when in headless mode.
+ this.myRenderer.setElementStyle(this.dropDownWrapper.nativeElement, 'display', 'block');
+ }
+ if (this.disabled) { return; }
+ this.animation_init = true;
+ this.bottomVisible = this.isBottomVisible();
+ this.show = !this.show;
+ }
+
+ public filterOptions = (filterValue): void => {
+ if (filterValue.length >= 1 && !this.show) { this.toggleDropdown(); }
+ if (this.selectedOption) { this.selectedOption = null; }
+ this.options = this.allOptions.filter((option) => {
+ return option.value.toLowerCase().indexOf(filterValue.toLowerCase()) > -1;
+ });
+ }
+
+ private isSelectable = (value: string): boolean => {
+ const option: IDropDownOption = this.options.find(o => o.value === value);
+ if (!option) { return false; }
+ if (!option.type) { return true; }
+ return !this.unselectableOptions.find(optionType => optionType === option.type);
+ }
+
+ private setSelected = (value: string): void => {
+ this.selectedOption = this.options.find(o => o.value === value);
+ if (this.type === DropDownTypes.Auto) { this.filterValue = value; }
+ this.show = false;
+ this.changeEmitter.next(this.selectedOption);
+ }
+
+ private isBottomVisible = (): boolean => {
+ const windowPos = window.innerHeight + window.pageYOffset;
+ const boundingRect = this.dropDownWrapper.nativeElement.getBoundingClientRect();
+ const dropDownPos = boundingRect.top + boundingRect.height + this.maxHeight;
+ return windowPos > dropDownPos;
+ }
+
+ private onClickDocument = (event): void => {
+ if (this.type === DropDownTypes.Headless) {
+ if (!this.optionsContainerElement.nativeElement.contains(event.target)) {
+ this.show = false;
+ }
+ } else {
+ if (!this.dropDownWrapper.nativeElement.contains(event.target)) {
+ this.show = false;
+ }
+ }
+ }
+
+}