Skip to content
Permalink
Browse files
feat(resource): new date picker (DEV-112) (#532)
* refactor(app): sort imports

* feat(resource): init new date picker

* style(date): display a date period in a different way

* style(date): overwrite material style

* style(date): clean up style in date picker

* refactor(date): make lint happy

* chore(date): no console error in case of missing date

* feat(date): real date picker with islamic calendar support

* feat(date): get current day in all calendars

* style(date): better date formatting

* feat(date): another date picker

* feat(date): using new date picker

* refactor(date): remove previous date picker

* fix(app): add date picker

* feat(date): update value

* feat(date): create new value

* refactor(date): clean up code

* refactor(date): make tslint happy

* feat(date): display date

* test(date): fix date pipe test

* test(date): fix tests of new date picker

* test(date): fix tests of new date picker

* refactor(resource): remove unnecessary console.log

* style(date): display date in a nice way

* fix(date): fix date picker close process

* feat(date): new date value handler for periods

* feat(date): handle period date values

* fix(date): date picker for single date and date range

* test(date): fix test in new date value comp

* test(date): fix knora date pipe

* test(date): add tests and fix issues

* fix(date): fix issue in date-picker

* feat(date): use new date picker in res instance form

* test(date): more tests in date value handler

* style(date): clean up date picker design

* fix(date): disable period toggle button if start date is not valid

* chore(date): do not predefine date in end date

* feat(search): use date picker in advanced search

* refactor(value): clean up code

* fix(date): resolve ngAfterViewChecked issue
  • Loading branch information
kilchenmann committed Oct 14, 2021
1 parent ec59ee5 commit 04e4b32be6b9e836eade4ee9b0666ead5582b47d
Showing with 2,653 additions and 70 deletions.
  1. +8 −2 src/app/app.module.ts
  2. +4 −4 src/app/main/pipes/formatting/knoradate.pipe.spec.ts
  3. +22 −5 src/app/main/pipes/formatting/knoradate.pipe.ts
  4. +1 −1 src/app/workspace/resource/operations/add-value/add-value.component.html
  5. +2 −1 src/app/workspace/resource/operations/display-edit/display-edit.component.html
  6. +1 −0 src/app/workspace/resource/operations/display-edit/display-edit.component.ts
  7. +0 −2 src/app/workspace/resource/resource-instance-form/resource-instance-form.component.spec.ts
  8. +1 −1 ...ource/resource-instance-form/select-properties/switch-properties/switch-properties.component.html
  9. +3 −3 src/app/workspace/resource/values/date-value/date-value.component.html
  10. +126 −0 src/app/workspace/resource/values/yet-another-date-value/date-picker/date-picker.component.html
  11. +109 −0 src/app/workspace/resource/values/yet-another-date-value/date-picker/date-picker.component.scss
  12. +44 −0 src/app/workspace/resource/values/yet-another-date-value/date-picker/date-picker.component.spec.ts
  13. +531 −0 src/app/workspace/resource/values/yet-another-date-value/date-picker/date-picker.component.ts
  14. +33 −0 ...space/resource/values/yet-another-date-value/date-value-handler/date-value-handler.component.html
  15. +16 −0 ...space/resource/values/yet-another-date-value/date-value-handler/date-value-handler.component.scss
  16. +384 −0 ...ce/resource/values/yet-another-date-value/date-value-handler/date-value-handler.component.spec.ts
  17. +313 −0 ...rkspace/resource/values/yet-another-date-value/date-value-handler/date-value-handler.component.ts
  18. +42 −0 src/app/workspace/resource/values/yet-another-date-value/yet-another-date-value.component.html
  19. 0 src/app/workspace/resource/values/yet-another-date-value/yet-another-date-value.component.scss
  20. +653 −0 src/app/workspace/resource/values/yet-another-date-value/yet-another-date-value.component.spec.ts
  21. +188 −0 src/app/workspace/resource/values/yet-another-date-value/yet-another-date-value.component.ts
  22. +5 −2 .../search-select-property/specify-property-value/search-date-value/search-date-value.component.html
  23. +9 −0 .../search-select-property/specify-property-value/search-date-value/search-date-value.component.scss
  24. +97 −34 ...arch-select-property/specify-property-value/search-date-value/search-date-value.component.spec.ts
  25. +6 −9 ...on/search-select-property/specify-property-value/search-date-value/search-date-value.component.ts
  26. +1 −1 src/assets/style/_viewer.scss
  27. +54 −5 src/assets/style/main.scss
@@ -138,6 +138,9 @@ import { TextValueAsXMLComponent } from './workspace/resource/values/text-value/
import { TimeInputComponent } from './workspace/resource/values/time-value/time-input/time-input.component';
import { TimeValueComponent } from './workspace/resource/values/time-value/time-value.component';
import { UriValueComponent } from './workspace/resource/values/uri-value/uri-value.component';
import { DatePickerComponent } from './workspace/resource/values/yet-another-date-value/date-picker/date-picker.component';
import { DateValueHandlerComponent } from './workspace/resource/values/yet-another-date-value/date-value-handler/date-value-handler.component';
import { YetAnotherDateValueComponent } from './workspace/resource/values/yet-another-date-value/yet-another-date-value.component';
import { ListViewComponent } from './workspace/results/list-view/list-view.component';
import { ResourceGridComponent } from './workspace/results/list-view/resource-grid/resource-grid.component';
import { ResourceListComponent } from './workspace/results/list-view/resource-list/resource-list.component';
@@ -152,6 +155,7 @@ import { SearchIntValueComponent } from './workspace/search/advanced-search/reso
import { SearchLinkValueComponent } from './workspace/search/advanced-search/resource-and-property-selection/search-select-property/specify-property-value/search-link-value/search-link-value.component';
import { SearchDisplayListComponent } from './workspace/search/advanced-search/resource-and-property-selection/search-select-property/specify-property-value/search-list-value/search-display-list/search-display-list.component';
import { SearchListValueComponent } from './workspace/search/advanced-search/resource-and-property-selection/search-select-property/specify-property-value/search-list-value/search-list-value.component';
import { SearchResourceComponent } from './workspace/search/advanced-search/resource-and-property-selection/search-select-property/specify-property-value/search-resource/search-resource.component';
import { SearchTextValueComponent } from './workspace/search/advanced-search/resource-and-property-selection/search-select-property/specify-property-value/search-text-value/search-text-value.component';
import { SearchUriValueComponent } from './workspace/search/advanced-search/resource-and-property-selection/search-select-property/specify-property-value/search-uri-value/search-uri-value.component';
import { SpecifyPropertyValueComponent } from './workspace/search/advanced-search/resource-and-property-selection/search-select-property/specify-property-value/specify-property-value.component';
@@ -160,7 +164,6 @@ import { SearchSelectOntologyComponent } from './workspace/search/advanced-searc
import { ExpertSearchComponent } from './workspace/search/expert-search/expert-search.component';
import { FulltextSearchComponent } from './workspace/search/fulltext-search/fulltext-search.component';
import { SearchPanelComponent } from './workspace/search/search-panel/search-panel.component';
import { SearchResourceComponent } from './workspace/search/advanced-search/resource-and-property-selection/search-select-property/specify-property-value/search-resource/search-resource.component';

// translate: AoT requires an exported function for factories
export function httpLoaderFactory(httpClient: HttpClient) {
@@ -197,7 +200,9 @@ export function httpLoaderFactory(httpClient: HttpClient) {
DateEditComponent,
DateInputComponent,
DateInputTextComponent,
DatePickerComponent,
DateValueComponent,
DateValueHandlerComponent,
DecimalValueComponent,
DialogComponent,
DialogHeaderComponent,
@@ -273,6 +278,7 @@ export function httpLoaderFactory(httpClient: HttpClient) {
SearchLinkValueComponent,
SearchListValueComponent,
SearchPanelComponent,
SearchResourceComponent,
SearchSelectOntologyComponent,
SearchSelectPropertyComponent,
SearchSelectResourceClassComponent,
@@ -311,7 +317,7 @@ export function httpLoaderFactory(httpClient: HttpClient) {
UsersComponent,
UsersListComponent,
VisualizerComponent,
SearchResourceComponent,
YetAnotherDateValueComponent,
],
imports: [
AngularSplitModule.forRoot(),
@@ -47,11 +47,11 @@ describe('KnoradatePipe', () => {

dateWithDisplayOptions = pipe.transform(date, 'dd.MM.YYYY', 'calendar');

expect(dateWithDisplayOptions).toEqual('04.07.1776 GREGORIAN');
expect(dateWithDisplayOptions).toEqual('04.07.1776 Gregorian');

dateWithDisplayOptions = pipe.transform(date, 'dd.MM.YYYY', 'all');

expect(dateWithDisplayOptions).toEqual('04.07.1776 AD GREGORIAN');
expect(dateWithDisplayOptions).toEqual('04.07.1776 Gregorian');
});

it ('should return a string with the desired display options for a date without era', () => {
@@ -63,11 +63,11 @@ describe('KnoradatePipe', () => {

dateWithDisplayOptions = pipe.transform(date, 'dd.MM.YYYY', 'calendar');

expect(dateWithDisplayOptions).toEqual('04.07.1441 ISLAMIC');
expect(dateWithDisplayOptions).toEqual('04.07.1441 Islamic');

dateWithDisplayOptions = pipe.transform(date, 'dd.MM.YYYY', 'all');

expect(dateWithDisplayOptions).toEqual('04.07.1441 ISLAMIC');
expect(dateWithDisplayOptions).toEqual('04.07.1441 Islamic');
});

it ('should return a string with only the month and the year', () => {
@@ -6,9 +6,9 @@ import { KnoraDate } from '@dasch-swiss/dsp-js';
})
export class KnoraDatePipe implements PipeTransform {

transform(date: KnoraDate, format?: string, displayOptions?: 'era' | 'calendar' | 'all'): string {
transform(date: KnoraDate, format?: string, displayOptions?: 'era' | 'calendar' | 'calendarOnly' | 'all'): string {
if (!(date instanceof KnoraDate)) {
console.error('Non-KnoraDate provided. Expected a valid KnoraDate');
// console.error('Non-KnoraDate provided. Expected a valid KnoraDate');
return '';
}

@@ -34,11 +34,17 @@ export class KnoraDatePipe implements PipeTransform {
addDisplayOptions(date: KnoraDate, value: string, options: string): string {
switch (options) {
case 'era':
return value + (date.era !== 'noEra' ? ' ' + date.era : '');
// displays date with era; era only in case of BCE
return value + (date.era === 'noEra' ? '' : ((date.era === 'BCE' || date.era === 'AD') ? ' ' + date.era : ''));
case 'calendar':
return value + ' ' + date.calendar;
// displays date without era but with calendar type
return value + ' ' + this._titleCase(date.calendar);
case 'calendarOnly':
// displays only the selected calendar type without any data
return this._titleCase(date.calendar);
case 'all':
return value + (date.era !== 'noEra' ? ' ' + date.era : '') + ' ' + date.calendar;
// displays date with era (only as BCE) and selected calendar type
return value + (date.era === 'noEra' ? '' : (date.era === 'BCE' ? ' ' + date.era : '')) + ' ' + this._titleCase(date.calendar);
}
}

@@ -79,4 +85,15 @@ export class KnoraDatePipe implements PipeTransform {
}
}

/**
* returns a string in Title Case format
* It's needed to transform a calendar name e.g. 'GREGORIAN' into 'Gregorian'
*
* @param str
* @returns string
*/
private _titleCase(str: string): string {
return str.split(' ').map(w => w[0].toUpperCase() + w.substr(1).toLowerCase()).join(' ');
}

}
@@ -14,7 +14,7 @@
<app-geoname-value #createVal *ngSwitchCase="constants.GeonameValue" [mode]="mode"></app-geoname-value>
<app-link-value #createVal *ngSwitchCase="constants.LinkValue" [mode]="mode"
[parentResource]="parentResource" [propIri]="resourcePropertyDefinition.id"></app-link-value>
<app-date-value #createVal *ngSwitchCase="constants.DateValue" [mode]="mode"></app-date-value>
<app-yet-another-date-value #createVal *ngSwitchCase="constants.DateValue" [mode]="mode"></app-yet-another-date-value>
<app-list-value #createVal *ngSwitchCase="constants.ListValue" [mode]="mode"
[propertyDef]="resourcePropertyDefinition"></app-list-value>
</span>
@@ -19,7 +19,8 @@
<app-geoname-value class="parent-value-component" #displayVal *ngSwitchCase="constants.GeonameValue" [mode]="mode" [displayValue]="$any(displayValue)"></app-geoname-value>
<app-link-value class="parent-value-component" #displayVal *ngSwitchCase="constants.LinkValue" [mode]="mode" [displayValue]="$any(displayValue)"
[parentResource]="parentResource" [propIri]="displayValue.property" (referredResourceClicked)="referredResourceClicked.emit($event)" (referredResourceHovered)="referredResourceHovered.emit($event)"></app-link-value>
<app-date-value class="parent-value-component" #displayVal *ngSwitchCase="constants.DateValue" [mode]="mode" [displayValue]="$any(displayValue)" [displayOptions]="dateDisplayOptions" [labels]="showDateLabels" [ontologyDateFormat]="dateFormat"></app-date-value>
<!-- <app-date-value class="parent-value-component" #displayVal *ngSwitchCase="constants.DateValue" [mode]="mode" [displayValue]="$any(displayValue)" [displayOptions]="dateDisplayOptions" [labels]="showDateLabels" [ontologyDateFormat]="dateFormat"></app-date-value> -->
<app-yet-another-date-value class="parent-value-component" #displayVal *ngSwitchCase="constants.DateValue" [mode]="mode" [displayValue]="$any(displayValue)" [ontologyDateFormat]="dateFormat"></app-yet-another-date-value>
<app-list-value class="parent-value-component" #displayVal *ngSwitchCase="constants.ListValue" [mode]="mode" [displayValue]="$any(displayValue)"
[propertyDef]="$any(parentResource.entityInfo.properties[displayValue.property])"></app-list-value>
<span *ngSwitchDefault>{{displayValue.strval}}</span>
@@ -239,6 +239,7 @@ export class DisplayEditComponent implements OnInit {
mergeMap((res: WriteValueResponse) => this._dspApiConnection.v2.values.getValue(this.parentResource.id, res.uuid))
).subscribe(
(res2: ReadResource) => {

this._valueOperationEventService.emit(
new EmitEvent(Events.ValueUpdated, new UpdatedEventValues(
this.displayValue, res2.getValues(this.displayValue.property)[0])));
@@ -444,8 +444,6 @@ describe('ResourceInstanceFormComponent', () => {

const selectOntoComp = resourceInstanceFormComponentDe.query(By.directive(MockSelectOntologyComponent));

console.log('ontos: ', (selectOntoComp.componentInstance as MockSelectOntologyComponent).ontologiesMetadata.ontologies);

expect((selectOntoComp.componentInstance as MockSelectOntologyComponent).ontologiesMetadata.ontologies.length).toEqual(11);

expect(dspConnSpy.v2.onto.getOntologiesByProjectIri).toHaveBeenCalledTimes(1);
@@ -11,7 +11,7 @@
<app-geoname-value #createVal *ngSwitchCase="constants.GeonameValue" [mode]="mode" [valueRequiredValidator]="isRequiredProp" [parentForm]="parentForm" [formName]="formName"></app-geoname-value>
<app-link-value #createVal *ngSwitchCase="constants.LinkValue" [mode]="mode" [valueRequiredValidator]="isRequiredProp" [parentForm]="parentForm" [formName]="formName"
[parentResource]="parentResource" [propIri]="property.id"></app-link-value>
<app-date-value #createVal *ngSwitchCase="constants.DateValue" [mode]="mode" [valueRequiredValidator]="isRequiredProp" [parentForm]="parentForm" [formName]="formName"></app-date-value>
<app-yet-another-date-value #createVal *ngSwitchCase="constants.DateValue" [mode]="mode" [valueRequiredValidator]="isRequiredProp" [parentForm]="parentForm" [formName]="formName"></app-yet-another-date-value>
<app-list-value #createVal *ngSwitchCase="constants.ListValue" [mode]="mode" [valueRequiredValidator]="isRequiredProp" [parentForm]="parentForm" [formName]="formName"
[propertyDef]="property"></app-list-value>
<span *ngSwitchDefault>
@@ -3,19 +3,19 @@
<!-- Date period -->
<span *ngIf="valueFormControl.value?.end; else date">
<span class="rm-value date-start">
<span *ngIf="labels">Period Start: </span>
<!-- <span *ngIf="labels">Period Start: </span> -->
{{valueFormControl.value?.start | knoraDate:ontologyDateFormat:displayOptions}}
</span>
<span class="rm-value date-end">
<span *ngIf="labels">Period End: </span>
<span *ngIf="labels"> &mdash; </span>
{{valueFormControl.value?.end | knoraDate:ontologyDateFormat:displayOptions}}
</span>
</span>

<!-- Only one date -->
<ng-template #date>
<span class="rm-value">
<span *ngIf="labels">Date: </span>
<!-- <span *ngIf="labels">Date: </span> -->
{{valueFormControl.value | knoraDate:ontologyDateFormat:displayOptions}}
</span>
</ng-template>
@@ -0,0 +1,126 @@
<div [formGroup]="dateForm" class="date-picker-container">
<mat-form-field class="hidden">
<input matInput [formControlName]="'knoraDate'" [errorStateMatcher]="matcher" readonly/>
</mat-form-field>
<mat-form-field [floatLabel]="'always'" class="child-input-component date-picker-value" [matMenuTriggerFor]="datePicker">
<mat-label>{{ calendar | titlecase }}</mat-label>
<input matInput
class="date-picker-input date"
[formControlName]="'date'"
placeholder="Click to select a date"
autocomplete="off"
readonly />
<mat-icon matSuffix class="link">event</mat-icon>
<!-- <mat-error *ngIf="dateForm.controls.date.hasError('required')">
Date is <strong>required</strong>
</mat-error> -->
</mat-form-field>
</div>


<!-- <mat-form-field class="date" [matMenuTriggerFor]="datePicker">
<mat-label>{{calendar}} {{era}}</mat-label>
<input matInput class="link" autocomplete="off" readonly placeholder="dd/mm/yyyy or mm/yyyy or yyyy" [value]="date | knoraDate">
<mat-icon matSuffix class="link">event</mat-icon>
</mat-form-field> -->
<!-- <button mat-button [matMenuTriggerFor]="menu">Menu</button> -->
<mat-menu #datePicker="matMenu" [formGroup]="form" class="date-picker">

<div class="date-picker-content" (click)="$event.stopPropagation()" (keydown)="$event.stopPropagation()">


<!-- select calendar and button to close date picker -->
<div class="calendar-selector panel">
<mat-form-field class="calendar" floatLabel="never">
<mat-label>Calendar</mat-label>
<mat-select formControlName="calendar" class="center">
<mat-option *ngFor="let cal of calendars; let i = index" [value]="cal">
{{cal | titlecase}}
</mat-option>
</mat-select>
</mat-form-field>
<span class="fill-remaining-space"></span>
<div class="action">
<!-- go to the current date -->
<button mat-button (click)=setToday()>Today</button>
<!-- submit button (which is needed if date doesn't have precision on an exact day) -->
<button mat-icon-button color="primary" [disabled]="!form.valid" (click)="setDate(day); closeDatePicker()">
<mat-icon>done</mat-icon>
</button>
</div>
</div>

<!-- panel with month (incl. navigation), year and era -->
<div class="month-year-selector panel">
<!-- select month -->
<mat-form-field class="month" floatLabel="never" [class.larger]="calendar === 'ISLAMIC'">
<mat-label>Month</mat-label>
<mat-select formControlName="month" class="center">
<mat-option>-- None --</mat-option>
<mat-option *ngFor="let month of months; let i = index" [value]="i + 1">
<span *ngIf="calendar !== 'ISLAMIC'">{{month[0]}}</span>
<span *ngIf="calendar === 'ISLAMIC'">{{month[1]}}</span>
</mat-option>
</mat-select>
</mat-form-field>
<!-- set year incl. era -->
<mat-form-field class="year" floatLabel="never" [class.withButtonToggle]="calendar !== 'ISLAMIC'">
<mat-label>Year *</mat-label>
<input matInput type="number" formControlName="year" min="1" class="center">
<mat-error *ngIf="formErrors.year">
{{ formErrors.year }}
</mat-error>
<!-- <button mat-flat-button matSuffix (click)="switchEra()" [attr.aria-label]="'Switch era'"
[attr.aria-pressed]="ce">
{{ce ? 'CE' : 'BCE'}}
</button> -->
<mat-button-toggle-group *ngIf="calendar !== 'ISLAMIC'" class="era suffix-toggle-group" matSuffix
formControlName="era" aria-label="Define era">
<mat-button-toggle value="CE">CE</mat-button-toggle>
<mat-button-toggle value="BCE">BCE</mat-button-toggle>
</mat-button-toggle-group>
</mat-form-field>

<!-- hidden slide toggle to know about era changed -->
<!-- <mat-slide-toggle formControlName="era">CE</mat-slide-toggle> -->
</div>

<!-- select day -->
<div class="day-selector panel" [class.disabled]="disableDaySelector">

<div>
<p *ngIf="era === 'CE'" class="week-days">
<span *ngFor="let d of weekDays" class="day">{{d}}</span>
</p>
<p *ngIf="era === 'BCE'" class="mat-caption center">
<!-- <mat-icon>warn</mat-icon> -->
Attention: Date <strong>before common era</strong>.
</p>
</div>
<div *ngFor="let week of weeks; let last = last" class="week">
<span *ngFor="let d of week" class="day" [class.selected]="d === day">
<!-- the following span is only a placeholder to set the position of the first day to the correct column-->
<span *ngIf="d === 0"></span>
<span *ngIf="d > 0" class="selectable link" [class.disabled]="disableDaySelector"
(click)="!disableDaySelector && setDate(d); closeDatePicker()">{{d}}</span>
</span>
<!-- add not interested button at the end, if you want precision on year and month only -->
<span *ngIf="(last && week.length < 7)" class="day">
<span class="selectable link" [class.disabled]="disableDaySelector" (click)="!disableDaySelector && setDate(); closeDatePicker()">
<mat-icon>not_interested</mat-icon>
</span>
</span>
</div>
<!-- add not interested button at the end, if you want precision on year and month only;
if the month ends on a Sunday, we have to add an additional line to get the not-interested option -->
<div class="week" *ngIf="weeks[weeks.length - 1].length === 7">
<span class="day">
<span class="selectable link" [class.disabled]="disableDaySelector" (click)="!disableDaySelector && setDate(); closeDatePicker()">
<mat-icon>not_interested</mat-icon>
</span>
</span>
</div>
</div>
</div>

</mat-menu>
Loading

0 comments on commit 04e4b32

Please sign in to comment.