diff --git a/demo-shell/src/app.config.json b/demo-shell/src/app.config.json index 06e2c6b74f..7390174bf5 100644 --- a/demo-shell/src/app.config.json +++ b/demo-shell/src/app.config.json @@ -145,7 +145,8 @@ "component": { "selector": "date-range", "settings": { - "field": "cm:created" + "field": "cm:created", + "dateFormat": "DD-MMM-YY" } } }, diff --git a/lib/content-services/i18n/en.json b/lib/content-services/i18n/en.json index 753ff4c3d3..646c62b803 100644 --- a/lib/content-services/i18n/en.json +++ b/lib/content-services/i18n/en.json @@ -188,7 +188,8 @@ "VALIDATION": { "REQUIRED-VALUE": "Required value", "NO-DAYS": "No days selected.", - "INVALID-FORMAT": "Invalid Format" + "INVALID-FORMAT": "Invalid Format", + "BEYOND-MAX-DATE": "Date is too late." } }, "ICONS": { diff --git a/lib/content-services/search/components/search-date-range/search-date-range.component.html b/lib/content-services/search/components/search-date-range/search-date-range.component.html index 688a263994..b1cb62f0e0 100644 --- a/lib/content-services/search/components/search-date-range/search-date-range.component.html +++ b/lib/content-services/search/components/search-date-range/search-date-range.component.html @@ -3,22 +3,30 @@ + [max]="maxFrom" + (focusout)="onChangedHandler($event, from)"> {{ 'SEARCH.FILTER.VALIDATION.REQUIRED-VALUE' | translate }} + + {{ 'SEARCH.FILTER.VALIDATION.BEYOND-MAX-DATE' | translate }} + + [min]="from.value" + (focusout)="onChangedHandler($event, to)"> - + + {{ 'SEARCH.FILTER.VALIDATION.REQUIRED-VALUE' | translate }} + + {{ 'SEARCH.FILTER.VALIDATION.NO-DAYS' | translate }} diff --git a/lib/content-services/search/components/search-date-range/search-date-range.component.spec.ts b/lib/content-services/search/components/search-date-range/search-date-range.component.spec.ts index 19f6fd0e97..45996a7792 100644 --- a/lib/content-services/search/components/search-date-range/search-date-range.component.spec.ts +++ b/lib/content-services/search/components/search-date-range/search-date-range.component.spec.ts @@ -15,26 +15,84 @@ * limitations under the License. */ -import { SearchDateRangeComponent } from './search-date-range.component'; -import moment from 'moment-es6'; +import { CustomMomentDateAdapter, SearchDateRangeComponent } from './search-date-range.component'; +import { Observable } from 'rxjs/Observable'; + +declare let moment: any; describe('SearchDateRangeComponent', () => { let component: SearchDateRangeComponent; let fromDate = '2016-10-16'; let toDate = '2017-10-16'; + const localeFixture = 'it'; + const dateFormatFixture = 'DD-MMM-YY'; + + const buildAdapter = (): CustomMomentDateAdapter => { + const dateAdapter = new CustomMomentDateAdapter(null); + dateAdapter.customDateFormat = null; + return dateAdapter; + }; + + const buildUserPreferences = (): any => { + const userPreferences = { + userPreferenceStatus: { LOCALE: localeFixture }, + select: (property) => { + return Observable.of(userPreferences.userPreferenceStatus[property]); + } + }; + return userPreferences; + }; + + const theDateAdapter = buildAdapter(); beforeEach(() => { - component = new SearchDateRangeComponent(); + component = new SearchDateRangeComponent(theDateAdapter, buildUserPreferences()); }); it('should setup form elements on init', () => { component.ngOnInit(); - expect(component.form).toBeDefined(); + expect(component.from).toBeDefined(); expect(component.to).toBeDefined(); expect(component.form).toBeDefined(); }); + it('should setup locale from userPreferencesService', () => { + spyOn(component, 'setLocale').and.stub(); + component.ngOnInit(); + expect(component.setLocale).toHaveBeenCalledWith(localeFixture); + }); + + it('should setup the format of the date from configuration', () => { + component.settings = { field: 'cm:created', dateFormat: dateFormatFixture }; + component.ngOnInit(); + expect(theDateAdapter.customDateFormat).toBe(dateFormatFixture); + }); + + it('should setup form control with formatted valid date on change', () => { + component.settings = { field: 'cm:created', dateFormat: dateFormatFixture }; + component.ngOnInit(); + + const inputString = '20.feb.18'; + const momentFromInput = moment(inputString, dateFormatFixture); + expect(momentFromInput.isValid()).toBeTruthy(); + + component.onChangedHandler({ srcElement: { value: inputString }}, component.from); + expect(component.from.value).toEqual(momentFromInput); + }); + + it('should NOT setup form control with invalid date on change', () => { + component.settings = { field: 'cm:created', dateFormat: dateFormatFixture }; + component.ngOnInit(); + + const inputString = '20.f.18'; + const momentFromInput = moment(inputString, dateFormatFixture); + expect(momentFromInput.isValid()).toBeFalsy(); + + component.onChangedHandler({ srcElement: { value: inputString }}, component.from); + expect(component.from.value).not.toEqual(momentFromInput); + }); + it('should reset form', () => { component.ngOnInit(); component.form.setValue({ from: fromDate, to: toDate }); diff --git a/lib/content-services/search/components/search-date-range/search-date-range.component.ts b/lib/content-services/search/components/search-date-range/search-date-range.component.ts index 52924b1f8b..13d9ed16b8 100644 --- a/lib/content-services/search/components/search-date-range/search-date-range.component.ts +++ b/lib/content-services/search/components/search-date-range/search-date-range.component.ts @@ -17,16 +17,44 @@ import { OnInit, Component, ViewEncapsulation } from '@angular/core'; import { FormControl, Validators, FormGroup } from '@angular/forms'; +import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core'; +import { MomentDateAdapter, MAT_MOMENT_DATE_FORMATS } from '@angular/material-moment-adapter'; + import { SearchWidget } from '../../search-widget.interface'; import { SearchWidgetSettings } from '../../search-widget-settings.interface'; import { SearchQueryBuilderService } from '../../search-query-builder.service'; import { LiveErrorStateMatcher } from '../../forms/live-error-state-matcher'; -import moment from 'moment-es6'; +import { Moment } from 'moment'; +import { UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; + +declare let moment: any; + +const DEFAULT_FORMAT_DATE: string = 'DD/MM/YYYY'; + +export class CustomMomentDateAdapter extends MomentDateAdapter { + customDateFormat: string; + + parse(value: any, parseFormat: any): any { + const dateFormat = this.customDateFormat ? this.customDateFormat : DEFAULT_FORMAT_DATE; + + return super.parse(value, dateFormat); + } + + format(value: Moment, displayFormat: string): string { + const dateFormat = this.customDateFormat ? this.customDateFormat : DEFAULT_FORMAT_DATE; + + return super.format(value, dateFormat); + } +} @Component({ selector: 'adf-search-date-range', templateUrl: './search-date-range.component.html', styleUrls: ['./search-date-range.component.scss'], + providers: [ + {provide: DateAdapter, useClass: CustomMomentDateAdapter, deps: [MAT_DATE_LOCALE]}, + {provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS} + ], encapsulation: ViewEncapsulation.None, host: { class: 'adf-search-date-range' } }) @@ -42,8 +70,23 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit { settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; maxFrom: any; + datePickerDateFormat = DEFAULT_FORMAT_DATE; + + constructor(private dateAdapter: DateAdapter, + private userPreferencesService: UserPreferencesService) { + } ngOnInit() { + if (this.settings) { + this.datePickerDateFormat = this.settings.dateFormat || DEFAULT_FORMAT_DATE; + } + const theCustomDateAdapter = this.dateAdapter; + theCustomDateAdapter.customDateFormat = this.datePickerDateFormat; + + this.userPreferencesService.select(UserPreferenceValues.Locale).subscribe((locale) => { + this.setLocale(locale); + }); + const validators = Validators.compose([ Validators.required ]); @@ -80,13 +123,20 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit { } } - hasSelectedDays(from: string, to: string): boolean { - if (from && to) { - const start = moment(from).startOf('day'); - const end = moment(to).endOf('day'); + onChangedHandler(event: any, formControl) { + const inputValue = event.srcElement.value; - return start.isBefore(end); + if (inputValue) { + const formatDate = moment(inputValue, this.datePickerDateFormat); + + if (formatDate.isValid()) { + formControl.setValue(formatDate); + } } - return true; + } + + setLocale(locale) { + this.dateAdapter.setLocale(locale); + moment.locale(locale); } } diff --git a/package-lock.json b/package-lock.json index bf4e01beb5..0823316939 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,6 +49,11 @@ "superagent": "3.8.2" } }, + "moment": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", + "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" + }, "pdfjs-dist": { "version": "1.5.404", "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-1.5.404.tgz", @@ -108,6 +113,11 @@ "superagent": "3.8.2" } }, + "moment": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", + "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" + }, "pdfjs-dist": { "version": "1.5.404", "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-1.5.404.tgz", @@ -168,6 +178,11 @@ "superagent": "3.8.2" } }, + "moment": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", + "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" + }, "pdfjs-dist": { "version": "1.5.404", "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-1.5.404.tgz", @@ -229,6 +244,11 @@ "superagent": "3.8.2" } }, + "moment": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", + "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" + }, "pdfjs-dist": { "version": "1.5.404", "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-1.5.404.tgz", @@ -10552,7 +10572,7 @@ }, "moment": { "version": "2.20.1", - "resolved": "http://localhost:4873/moment/-/moment-2.20.1.tgz", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.20.1.tgz", "integrity": "sha512-Yh9y73JRljxW5QxN08Fner68eFLxM5ynNOAw2LbIB1YAGeQzZT8QFSUvkAz609Zf+IHhhaUxqZK8dG3W/+HEvg==" }, "moment-es6": { diff --git a/scripts/test-e2e-bc.sh b/scripts/test-e2e-bc.sh index 6f38d7917f..2c2b69c21b 100755 --- a/scripts/test-e2e-bc.sh +++ b/scripts/test-e2e-bc.sh @@ -8,6 +8,7 @@ ADF_VERSION=$(npm view @alfresco/adf-core version) ANGULAR_VERSION="5.1.1" MATERIAL_VERSION="5.0.1" NGX_TRANSLATE_VERSION="9.1.1" +MOMENT_VERSION="2.20.1" npm install @@ -21,5 +22,6 @@ npm install --save @mat-datetimepicker/core @mat-datetimepicker/moment npm install --save-exact --save @angular/animations@${ANGULAR_VERSION} @angular/common@${ANGULAR_VERSION} @angular/compiler@${ANGULAR_VERSION} @angular/core@${ANGULAR_VERSION} @angular/platform-browser@${ANGULAR_VERSION} @angular/router@${ANGULAR_VERSION} @angular/flex-layout@2.0.0-beta.12 @angular/forms@${ANGULAR_VERSION} @angular/forms@${ANGULAR_VERSION} @angular/http@${ANGULAR_VERSION} @angular/platform-browser-dynamic@${ANGULAR_VERSION} npm install --save-exact --save @angular/cdk@${MATERIAL_VERSION} @angular/material@${MATERIAL_VERSION} npm install --save-exact --save @ngx-translate/core@${NGX_TRANSLATE_VERSION} +npm install --save-exact --save moment@${MOMENT_VERSION} npm run e2e