Skip to content
This repository has been archived by the owner on Dec 8, 2022. It is now read-only.

URL validation module #1334

Merged
merged 2 commits into from
Dec 1, 2017
Merged
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
23 changes: 23 additions & 0 deletions src/app/components/demo-components.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,29 @@ export class SkyDemoComponentsService {
];
}
},
{
name: 'URL validation',
icon: 'check',
// tslint:disable-next-line
summary: `The URL validation module allows users to validate URL format.`,
url: '/components/url-validation',
getCodeFiles: function () {
return [
{
name: 'url-validation-demo.component.html',
fileContents: require
('!!raw-loader!./url-validation/url-validation-demo.component.html')
},
{
name: 'url-validation.component.ts',
fileContents: require
('!!raw-loader!./url-validation/url-validation-demo.component.ts'),
componentName: 'SkyUrlValidationDemoComponent',
bootstrapSelector: 'sky-url-validation-demo'
}
];
}
},
{
name: 'Vertical tabs',
icon: 'folder-open-o',
Expand Down
22 changes: 22 additions & 0 deletions src/app/components/url-validation/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<sky-demo-page
pageTitle="URL validation">
<sky-demo-page-summary>
The URL validation module allows the user to validate URL format.
</sky-demo-page-summary>

<sky-demo-page-properties
sectionHeading="Directive properties">
<sky-demo-page-property
propertyName="skyUrlValidation">
When this attribute is placed on an input element, the input with validation properties is created. The error message will display if the input is not in the correct format. This directive will use <stache-code>NgModel</stache-code> for binding data.
</sky-demo-page-property>
</sky-demo-page-properties>

<sky-demo-page-example>
<sky-url-validation-demo>
</sky-url-validation-demo>
<sky-demo-page-code
demoName="URL validation">
</sky-demo-page-code>
</sky-demo-page-example>
</sky-demo-page>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<input
skyUrlValidation
class="sky-form-control"
[(ngModel)]="url"
#input="ngModel">

<div
class="sky-error-label"
*ngIf="input.errors && input.errors.skyUrl && (input.dirty && input.touched)">
<span
[hidden]="!input.errors.skyUrl.invalid">
Please enter valid url with format http(s)://site.domain
</span>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Component } from '@angular/core';

@Component({
selector: 'sky-url-validation-demo',
templateUrl: './url-validation-demo.component.html'
})
export class SkyUrlValidationDemoComponent {
public url: string;
}
4 changes: 4 additions & 0 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { SkyTextHighlightModule } from './modules/text-highlight';
import { SkyToolbarModule } from './modules/toolbar';
import { SkyTilesModule } from './modules/tiles';
import { SkyTimepickerModule } from './modules/timepicker';
import { SkyUrlValidationModule } from './modules/url-validation';
import { SkyVerticalTabsetModule } from './modules/vertical-tabset';
import { SkyWaitModule } from './modules/wait';

Expand Down Expand Up @@ -113,6 +114,7 @@ import { SkyWaitModule } from './modules/wait';
SkyTilesModule,
SkyTimepickerModule,
SkyToolbarModule,
SkyUrlValidationModule,
SkyVerticalTabsetModule,
SkyWaitModule,
SkyDatepickerModule
Expand Down Expand Up @@ -506,6 +508,8 @@ export {
SkyToolbarSectionComponent
} from './modules/toolbar';

export * from './modules/url-validation';

export {
SkyVerticalTabsetComponent,
SkyVerticalTabsetGroupComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { CommonModule } from '@angular/common';

import { SkyUrlValidationModule } from '../url-validation.module';
import { UrlValidationTestComponent } from './url-validation.component.fixture';

@NgModule({
declarations: [
UrlValidationTestComponent
],
imports: [
FormsModule,
CommonModule,
SkyUrlValidationModule
],
exports: [
UrlValidationTestComponent
]
})
export class SkyUrlValidationFixturesModule { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<div>
<input
skyUrlValidation
[(ngModel)]="urlValidator"
#url="ngModel">
<div
class="sky-error-label"
*ngIf="url.errors && url.errors.skyUrl && (url.dirty || url.touched)">
<div [hidden]="!url.errors.skyUrl.invalid">
Please enter valid url with format http(s)://site.domain
</div>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Component } from '@angular/core';

@Component({
selector: 'sky-test-cmp',
templateUrl: './url-validation.component.fixture.html'
})
export class UrlValidationTestComponent {
public urlValidator: string;
}
2 changes: 2 additions & 0 deletions src/modules/url-validation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './url-validation.directive';
export * from './url-validation.module';
93 changes: 93 additions & 0 deletions src/modules/url-validation/url-validation.directive.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { TestBed, ComponentFixture, fakeAsync, tick } from '@angular/core/testing';
import { FormsModule, NgModel } from '@angular/forms';
import { By } from '@angular/platform-browser';

import { SkyUrlValidationFixturesModule } from './fixtures/url-validation-fixtures.module';
import { UrlValidationTestComponent } from './fixtures/url-validation.component.fixture';

describe('Url validation', () => {
function setInput(
element: HTMLElement,
text: string,
compFixture: ComponentFixture<any>) {
let inputEvent = document.createEvent('Event');
let params = {
bubbles: false,
cancelable: false
};

inputEvent.initEvent('input', params.bubbles, params.cancelable);

let changeEvent = document.createEvent('Event');
changeEvent.initEvent('change', params.bubbles, params.cancelable);
let inputEl = element.querySelector('input');
inputEl.value = text;

inputEl.dispatchEvent(inputEvent);
compFixture.detectChanges();

inputEl.dispatchEvent(changeEvent);
compFixture.detectChanges();
tick();

}

let component: UrlValidationTestComponent;
let fixture: ComponentFixture<UrlValidationTestComponent>;
let ngModel: NgModel;
let nativeElement: HTMLElement;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [
SkyUrlValidationFixturesModule,
FormsModule
]
});
fixture = TestBed.createComponent(UrlValidationTestComponent);
nativeElement = fixture.nativeElement as HTMLElement;
let input = fixture.debugElement.query(By.css('input'));
ngModel = <NgModel>input.injector.get(NgModel);
component = fixture.componentInstance;
});

it('should validate correct input', fakeAsync(() => {
fixture.detectChanges();
tick();
setInput(nativeElement, 'https://blackbaud.com', fixture);
fixture.detectChanges();

expect(nativeElement.querySelector('input').value).toBe('https://blackbaud.com');

expect(ngModel.control.valid).toBe(true);
expect(ngModel.control.pristine).toBe(false);
expect(ngModel.control.touched).toBe(false);

}));
it('should validate incorrect input', fakeAsync(() => {
fixture.detectChanges();
tick();
setInput(nativeElement, '[]awefhawenfc0293ejwf]', fixture);
fixture.detectChanges();

expect(nativeElement.querySelector('input').value).toBe('[]awefhawenfc0293ejwf]');

expect(ngModel.control.valid).toBe(false);
expect(ngModel.control.pristine).toBe(false);
expect(ngModel.control.touched).toBe(false);
}));

it('should handle invalid and then valid input', fakeAsync(() => {
fixture.detectChanges();
tick();
setInput(nativeElement, '[]awefhawenfc0293ejwf]', fixture);
setInput(nativeElement, 'blackbaud.com', fixture);

expect(nativeElement.querySelector('input').value).toBe('blackbaud.com');
expect(component.urlValidator).toEqual('blackbaud.com');

expect(ngModel.control.valid).toBe(true);
expect(ngModel.control.pristine).toBe(false);
expect(ngModel.control.touched).toBe(false);
}));
});
37 changes: 37 additions & 0 deletions src/modules/url-validation/url-validation.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Directive, forwardRef } from '@angular/core';
import { Validator, NG_VALIDATORS, AbstractControl } from '@angular/forms';

// tslint:disable:no-forward-ref no-use-before-declare
const SKY_URL_VALIDATION_VALIDATOR = {
provide: NG_VALIDATORS,
useExisting: forwardRef(() => SkyUrlValidationDirective),
multi: true
};
// tslint:enable
@Directive({
selector: '[skyUrlValidation]',
providers: [SKY_URL_VALIDATION_VALIDATOR]
})

export class SkyUrlValidationDirective implements Validator {
public validate(control: AbstractControl): {[key: string]: any} {
const value = control.value;

if (!value) {
return;
}

if (!this.urlIsValid(value)) {
return {
'skyUrl': {
invalid: control.value
}
};
}
}

public urlIsValid(url: string): boolean {
let regex = /^((http|https):\/\/)?([\w\-]+\.)+[\w\-]+/i;
return regex.test(url);
}
}
17 changes: 17 additions & 0 deletions src/modules/url-validation/url-validation.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { SkyUrlValidationDirective } from './url-validation.directive';

@NgModule({
declarations: [
SkyUrlValidationDirective
],
imports: [
FormsModule
],
exports: [
SkyUrlValidationDirective
]
})
export class SkyUrlValidationModule { }