mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2026-09-09 18:03:21 +00:00
AAE-44003 Allow manual input for date field (#11830)
* AAE-44003 Allow manual input for date field * copilot suggestions * code review update * code review update
This commit is contained in:
+68
-56
@@ -10,66 +10,78 @@
|
||||
>
|
||||
{{ property.label | translate }}
|
||||
</mat-label>
|
||||
<div class="adf-dateitem-editable-controls">
|
||||
<span
|
||||
class="adf-datepicker-span-button"
|
||||
[attr.data-automation-id]="'datepicker-label-toggle-' + property.key"
|
||||
(click)="showDatePicker()"
|
||||
(dblclick)="copyToClipboard(property.displayValue)"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
(keyup.enter)="showDatePicker()"
|
||||
>
|
||||
<span [attr.data-automation-id]="'card-' + property.type + '-value-' + property.key">
|
||||
@if (showProperty) {
|
||||
{{ property.displayValue }}
|
||||
} @else {
|
||||
{{ property.default | translate }}
|
||||
}
|
||||
@if (property.allowManualInput) {
|
||||
<input
|
||||
matInput
|
||||
[matDatetimepicker]="datetimePicker"
|
||||
[formControl]="cardViewDateTimeControl"
|
||||
(dateChange)="onDateChanged($event)"
|
||||
[attr.id]="'card-view-dateitem-' + property.key"
|
||||
[attr.data-automation-id]="'datepicker-manual-input-' + property.key"
|
||||
[placeholder]="property.default ? (property.default | translate) : ''"
|
||||
/>
|
||||
} @else {
|
||||
<div class="adf-dateitem-editable-controls">
|
||||
<span
|
||||
class="adf-datepicker-span-button"
|
||||
[attr.data-automation-id]="'datepicker-label-toggle-' + property.key"
|
||||
(click)="showDatePicker()"
|
||||
(dblclick)="copyToClipboard(property.displayValue)"
|
||||
tabindex="0"
|
||||
role="button"
|
||||
(keyup.enter)="showDatePicker()"
|
||||
>
|
||||
<span [attr.data-automation-id]="'card-' + property.type + '-value-' + property.key">
|
||||
@if (showProperty) {
|
||||
{{ property.displayValue }}
|
||||
} @else {
|
||||
{{ property.default | translate }}
|
||||
}
|
||||
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
matInput
|
||||
[disabled]="isReadonlyProperty || !editable"
|
||||
class="adf-invisible-date-input"
|
||||
[attr.tabIndex]="-1"
|
||||
[matDatetimepicker]="datetimePicker"
|
||||
[value]="valueDate"
|
||||
(dateChange)="onDateChanged($event)"
|
||||
[attr.id]="'card-view-dateitem-' + property.key"
|
||||
/>
|
||||
@if (showClearAction) {
|
||||
<mat-icon
|
||||
matIconSuffix
|
||||
class="adf-date-reset-icon"
|
||||
[ngClass]="{ 'cdk-visually-hidden': !editable || isReadonlyProperty}"
|
||||
(click)="onDateClear()"
|
||||
[attr.title]="'CORE.METADATA.ACTIONS.CLEAR' | translate"
|
||||
[attr.data-automation-id]="'datepicker-date-clear-' + property.key"
|
||||
adf-icon="clear"
|
||||
/>
|
||||
}
|
||||
<mat-datetimepicker-toggle
|
||||
matIconSuffix
|
||||
[attr.tabindex]="-1"
|
||||
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
|
||||
[attr.data-automation-id]="'datepickertoggle-' + property.key"
|
||||
[for]="datetimePicker"
|
||||
class="adf-dateitem-picker-toggle"
|
||||
[ngClass]="{ 'cdk-visually-hidden': !editable || isReadonlyProperty}"
|
||||
/>
|
||||
|
||||
<mat-datetimepicker
|
||||
#datetimePicker
|
||||
[type]="$any(property).type"
|
||||
[timeInterval]="5"
|
||||
</div>
|
||||
<input
|
||||
matInput
|
||||
[disabled]="isReadonlyProperty || !editable"
|
||||
[attr.data-automation-id]="'datepicker-' + property.key"
|
||||
[startAt]="valueDate"
|
||||
[ngClass]="{ 'cdk-visually-hidden': !editable || isReadonlyProperty}"
|
||||
class="adf-invisible-date-input"
|
||||
[attr.tabIndex]="-1"
|
||||
[matDatetimepicker]="datetimePicker"
|
||||
[value]="valueDate"
|
||||
(dateChange)="onDateChanged($event)"
|
||||
[attr.id]="'card-view-dateitem-' + property.key"
|
||||
/>
|
||||
}
|
||||
@if (showClearAction) {
|
||||
<mat-icon
|
||||
matIconSuffix
|
||||
class="adf-date-reset-icon"
|
||||
[ngClass]="{ 'cdk-visually-hidden': !editable || isReadonlyProperty}"
|
||||
(click)="onDateClear()"
|
||||
[attr.title]="'CORE.METADATA.ACTIONS.CLEAR' | translate"
|
||||
[attr.data-automation-id]="'datepicker-date-clear-' + property.key"
|
||||
adf-icon="clear"
|
||||
/>
|
||||
}
|
||||
<mat-datetimepicker-toggle
|
||||
matIconSuffix
|
||||
[attr.tabindex]="-1"
|
||||
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
|
||||
[attr.data-automation-id]="'datepickertoggle-' + property.key"
|
||||
[for]="datetimePicker"
|
||||
class="adf-dateitem-picker-toggle"
|
||||
[ngClass]="{ 'cdk-visually-hidden': !editable || isReadonlyProperty}"
|
||||
/>
|
||||
|
||||
<mat-datetimepicker
|
||||
#datetimePicker
|
||||
[type]="$any(property).type"
|
||||
[timeInterval]="5"
|
||||
[disabled]="isReadonlyProperty || !editable"
|
||||
[attr.data-automation-id]="'datepicker-' + property.key"
|
||||
[startAt]="valueDate"
|
||||
[ngClass]="{ 'cdk-visually-hidden': !editable || isReadonlyProperty}"
|
||||
/>
|
||||
</mat-form-field>
|
||||
} @else {
|
||||
<mat-form-field class="adf-property-field adf-dateitem-editable" [floatLabel]="property.default ? 'always' : null">
|
||||
|
||||
+591
-1
@@ -22,12 +22,15 @@ import { CardViewDateItemComponent } from './card-view-dateitem.component';
|
||||
import { ClipboardService } from '../../../clipboard/clipboard.service';
|
||||
import { CardViewDatetimeItemModel } from '../../models/card-view-datetimeitem.model';
|
||||
import { AppConfigService } from '../../../app-config/app-config.service';
|
||||
import { MatDatetimepickerInputEvent } from '@mat-datetimepicker/core';
|
||||
import { DatetimeAdapter, MatDatetimepickerInputEvent } from '@mat-datetimepicker/core';
|
||||
import { DateAdapter } from '@angular/material/core';
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { addMinutes } from 'date-fns';
|
||||
import { UnitTestingUtils } from '../../../testing/unit-testing-utils';
|
||||
import { MatFormField } from '@angular/material/form-field';
|
||||
import { AdfDateFnsAdapter } from '../../../common/utils/date-fns-adapter';
|
||||
import { AdfDateTimeFnsAdapter } from '../../../common/utils/datetime-fns-adapter';
|
||||
|
||||
describe('CardViewDateItemComponent', () => {
|
||||
let loader: HarnessLoader;
|
||||
@@ -302,6 +305,18 @@ describe('CardViewDateItemComponent', () => {
|
||||
expect(component.property.value).toBeNull();
|
||||
expect(component.property.default).toBeNull();
|
||||
});
|
||||
|
||||
it('should not touch the form control when onDateClear is called and allowManualInput is false', () => {
|
||||
component.editable = true;
|
||||
component.property.editable = true;
|
||||
component.property.value = new Date('Jul 10 2017');
|
||||
fixture.detectChanges();
|
||||
|
||||
const spy = spyOn(component.cardViewDateTimeControl, 'setValue');
|
||||
component.onDateClear();
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be possible update a date-time', async () => {
|
||||
@@ -360,6 +375,581 @@ describe('CardViewDateItemComponent', () => {
|
||||
expect(await chips[2].getText()).toBe('Jul 12, 2017, 0:01');
|
||||
});
|
||||
|
||||
describe('allowManualInput', () => {
|
||||
beforeEach(() => {
|
||||
fixture.componentRef.setInput(
|
||||
'property',
|
||||
new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
default: '',
|
||||
format: 'yyyy-MM-dd',
|
||||
editable: true,
|
||||
allowManualInput: true
|
||||
})
|
||||
);
|
||||
fixture.componentRef.setInput('editable', true);
|
||||
});
|
||||
|
||||
it('should render a visible typeable input when allowManualInput is true', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const manualInput = testingUtils.getByDataAutomationId('datepicker-manual-input-dateKey');
|
||||
expect(manualInput).not.toBeNull();
|
||||
const invisibleInput = fixture.nativeElement.querySelector('.adf-invisible-date-input');
|
||||
expect(invisibleInput).toBeNull();
|
||||
});
|
||||
|
||||
it('should NOT render the span-button when allowManualInput is true', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const spanButton = testingUtils.getByDataAutomationId('datepicker-label-toggle-dateKey');
|
||||
expect(spanButton).toBeNull();
|
||||
});
|
||||
|
||||
it('should render the invisible input and span-button when allowManualInput is false', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
default: '',
|
||||
format: '',
|
||||
editable: true,
|
||||
allowManualInput: false
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
const invisibleInput = fixture.nativeElement.querySelector('.adf-invisible-date-input');
|
||||
expect(invisibleInput).not.toBeNull();
|
||||
const spanButton = testingUtils.getByDataAutomationId('datepicker-label-toggle-dateKey');
|
||||
expect(spanButton).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should still render the datepicker toggle and picker when allowManualInput is true', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const datePickerToggle = testingUtils.getByDataAutomationId(`datepickertoggle-${component.property.key}`);
|
||||
const datePicker = testingUtils.getByDataAutomationId(`datepicker-${component.property.key}`);
|
||||
expect(datePickerToggle).not.toBeNull();
|
||||
expect(datePicker).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should still render the clear icon when allowManualInput is true and value exists', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const clearIcon = testingUtils.getByDataAutomationId(`datepicker-date-clear-${component.property.key}`);
|
||||
expect(clearIcon).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should update the property when a date is changed via the picker', () => {
|
||||
const cardViewUpdateService = TestBed.inject(CardViewUpdateService);
|
||||
const itemUpdatedSpy = spyOn(cardViewUpdateService.itemUpdated$, 'next');
|
||||
fixture.detectChanges();
|
||||
|
||||
const expectedDate = new Date('Jul 10 2018');
|
||||
component.onDateChanged({ value: addMinutes(expectedDate, expectedDate.getTimezoneOffset()) } as MatDatetimepickerInputEvent<Date>);
|
||||
|
||||
expect(itemUpdatedSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should sync the form control value on init', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.cardViewDateTimeControl.value).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should clear the form control value when onDateClear is called', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
component.onDateClear();
|
||||
expect(component.cardViewDateTimeControl.value).toBeNull();
|
||||
});
|
||||
|
||||
it('should disable the form control when property.editable is false', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
default: '',
|
||||
format: 'yyyy-MM-dd',
|
||||
editable: false,
|
||||
allowManualInput: true
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.cardViewDateTimeControl.disabled).toBeTrue();
|
||||
});
|
||||
|
||||
it('should disable the form control when component editable is false', () => {
|
||||
component.editable = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.cardViewDateTimeControl.disabled).toBeTrue();
|
||||
});
|
||||
|
||||
it('should disable the form control when editable input changes to false after init', () => {
|
||||
fixture.detectChanges();
|
||||
expect(component.cardViewDateTimeControl.disabled).toBeFalse();
|
||||
|
||||
fixture.componentRef.setInput('editable', false);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.cardViewDateTimeControl.disabled).toBeTrue();
|
||||
});
|
||||
|
||||
it('should re-enable the form control when editable input changes back to true after being disabled', () => {
|
||||
fixture.componentRef.setInput('editable', false);
|
||||
fixture.detectChanges();
|
||||
expect(component.cardViewDateTimeControl.disabled).toBeTrue();
|
||||
|
||||
fixture.componentRef.setInput('editable', true);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.cardViewDateTimeControl.disabled).toBeFalse();
|
||||
});
|
||||
|
||||
it('should disable the form control when property changes to non-editable after init', () => {
|
||||
fixture.detectChanges();
|
||||
expect(component.cardViewDateTimeControl.disabled).toBeFalse();
|
||||
|
||||
fixture.componentRef.setInput(
|
||||
'property',
|
||||
new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
default: '',
|
||||
format: 'yyyy-MM-dd',
|
||||
editable: false,
|
||||
allowManualInput: true
|
||||
})
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.cardViewDateTimeControl.disabled).toBeTrue();
|
||||
});
|
||||
|
||||
it('should have null form control value when property has no value', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: null,
|
||||
key: 'dateKey',
|
||||
default: '',
|
||||
format: 'yyyy-MM-dd',
|
||||
editable: true,
|
||||
allowManualInput: true
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.cardViewDateTimeControl.value).toBeNull();
|
||||
});
|
||||
|
||||
it('should render placeholder with default value when property has default', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: null,
|
||||
key: 'dateKey',
|
||||
default: 'Select a date',
|
||||
format: 'yyyy-MM-dd',
|
||||
editable: true,
|
||||
allowManualInput: true
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const manualInput = testingUtils.getByDataAutomationId('datepicker-manual-input-dateKey');
|
||||
expect(manualInput.nativeElement.placeholder).toContain('Select a date');
|
||||
});
|
||||
|
||||
it('should render empty placeholder when property has no default', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const manualInput = testingUtils.getByDataAutomationId('datepicker-manual-input-dateKey');
|
||||
expect(manualInput.nativeElement.placeholder).toBe('');
|
||||
});
|
||||
|
||||
it('should sync form control value with valueDate when date is changed via picker', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const expectedDate = new Date('Jul 10 2018');
|
||||
component.onDateChanged({ value: addMinutes(expectedDate, expectedDate.getTimezoneOffset()) } as MatDatetimepickerInputEvent<Date>);
|
||||
|
||||
expect(component.cardViewDateTimeControl.value).not.toBeNull();
|
||||
expect(component.cardViewDateTimeControl.value instanceof Date).toBeTrue();
|
||||
});
|
||||
|
||||
it('should re-sync form control value when property input is replaced after init', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const newDate = new Date('2020-03-15');
|
||||
fixture.componentRef.setInput(
|
||||
'property',
|
||||
new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: newDate,
|
||||
key: 'dateKey',
|
||||
format: 'yyyy-MM-dd',
|
||||
editable: true,
|
||||
allowManualInput: true
|
||||
})
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.cardViewDateTimeControl.value).not.toBeNull();
|
||||
expect(component.cardViewDateTimeControl.value.toDateString()).toBe(newDate.toDateString());
|
||||
});
|
||||
|
||||
it('should set form control to null when replacement property has no value', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentRef.setInput(
|
||||
'property',
|
||||
new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: null,
|
||||
key: 'dateKey',
|
||||
format: 'yyyy-MM-dd',
|
||||
editable: true,
|
||||
allowManualInput: true
|
||||
})
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.cardViewDateTimeControl.value).toBeNull();
|
||||
});
|
||||
|
||||
it('should re-sync form control and valueDate when updateItem$ emits for matching key', () => {
|
||||
fixture.detectChanges();
|
||||
const cardViewUpdateService = TestBed.inject(CardViewUpdateService);
|
||||
const newDate = new Date('2021-06-01');
|
||||
|
||||
cardViewUpdateService.updateItem$.next({ key: 'dateKey', value: newDate } as any);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.valueDate).not.toBeNull();
|
||||
expect(component.cardViewDateTimeControl.value).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should not re-sync form control when updateItem$ emits for a different key', () => {
|
||||
fixture.detectChanges();
|
||||
const originalControlValue = component.cardViewDateTimeControl.value;
|
||||
const cardViewUpdateService = TestBed.inject(CardViewUpdateService);
|
||||
|
||||
cardViewUpdateService.updateItem$.next({ key: 'otherKey', value: new Date('2021-06-01') } as any);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.cardViewDateTimeControl.value).toEqual(originalControlValue);
|
||||
});
|
||||
});
|
||||
|
||||
describe('format changes', () => {
|
||||
it('should re-apply format when property.format changes with allowManualInput', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
format: 'dd/MM/yyyy',
|
||||
editable: true,
|
||||
allowManualInput: true
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const datetimeAdapter = fixture.debugElement.injector.get(DatetimeAdapter) as AdfDateTimeFnsAdapter;
|
||||
expect(datetimeAdapter.displayFormat).toBe('dd/MM/yyyy');
|
||||
|
||||
component.property.format = 'yyyy-MM-dd';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(datetimeAdapter.displayFormat).toBe('yyyy-MM-dd');
|
||||
});
|
||||
|
||||
it('should not re-apply format when property.format has not changed', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
format: 'dd/MM/yyyy',
|
||||
editable: true,
|
||||
allowManualInput: true
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const datetimeAdapter = fixture.debugElement.injector.get(DatetimeAdapter) as AdfDateTimeFnsAdapter;
|
||||
const formatBefore = datetimeAdapter.displayFormat;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(datetimeAdapter.displayFormat).toBe(formatBefore);
|
||||
});
|
||||
|
||||
it('should reset adapters to defaults when custom format is removed in manual input mode', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
format: 'dd/MM/yyyy',
|
||||
editable: true,
|
||||
allowManualInput: true
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const datetimeAdapter = fixture.debugElement.injector.get(DatetimeAdapter) as AdfDateTimeFnsAdapter;
|
||||
const dateAdapter = fixture.debugElement.injector.get(DateAdapter) as AdfDateFnsAdapter;
|
||||
expect(datetimeAdapter.displayFormat).toBe('dd/MM/yyyy');
|
||||
expect(dateAdapter.displayFormat).toBe('dd/MM/yyyy');
|
||||
|
||||
component.property.format = '';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(datetimeAdapter.displayFormat).toBeNull();
|
||||
expect(dateAdapter.displayFormat).toBe('MMM dd');
|
||||
});
|
||||
|
||||
it('should reset datetime adapter when custom format is removed for datetime property in manual input mode', () => {
|
||||
component.property = new CardViewDatetimeItemModel({
|
||||
label: 'Datetime label',
|
||||
value: new Date('07/10/2017 10:15'),
|
||||
key: 'datetimeKey',
|
||||
format: 'yyyy-MM-dd HH:mm',
|
||||
editable: true,
|
||||
allowManualInput: true
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const datetimeAdapter = fixture.debugElement.injector.get(DatetimeAdapter) as AdfDateTimeFnsAdapter;
|
||||
expect(datetimeAdapter.displayFormat).toBe('yyyy-MM-dd HH:mm');
|
||||
|
||||
component.property.format = '';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(datetimeAdapter.displayFormat).toBeNull();
|
||||
});
|
||||
|
||||
it('should fallback to adapter defaults when allowManualInput is false', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
format: 'YYYY-MM-dd',
|
||||
editable: true,
|
||||
allowManualInput: false
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const dateAdapter = fixture.debugElement.injector.get(DateAdapter) as AdfDateFnsAdapter;
|
||||
const datetimeAdapter = fixture.debugElement.injector.get(DatetimeAdapter) as AdfDateTimeFnsAdapter;
|
||||
|
||||
expect(dateAdapter.displayFormat).toBe('MMM dd');
|
||||
expect(datetimeAdapter.displayFormat).toBeNull();
|
||||
});
|
||||
|
||||
it('should re-set form control value when format changes and control has value', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
format: 'dd/MM/yyyy',
|
||||
editable: true,
|
||||
allowManualInput: true
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const spy = spyOn(component.cardViewDateTimeControl, 'setValue');
|
||||
component.property.format = 'yyyy-MM-dd';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not re-set form control value when format changes but allowManualInput is false', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
format: 'dd/MM/yyyy',
|
||||
editable: true,
|
||||
allowManualInput: false
|
||||
});
|
||||
component.editable = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const spy = spyOn(component.cardViewDateTimeControl, 'setValue');
|
||||
component.property.format = 'yyyy-MM-dd';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(spy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('CardViewDateItemModel allowManualInput', () => {
|
||||
it('should default allowManualInput to false when not specified', () => {
|
||||
const model = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date(),
|
||||
key: 'dateKey'
|
||||
});
|
||||
expect(model.allowManualInput).toBeFalse();
|
||||
});
|
||||
|
||||
it('should set allowManualInput to true when specified', () => {
|
||||
const model = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date(),
|
||||
key: 'dateKey',
|
||||
allowManualInput: true
|
||||
});
|
||||
expect(model.allowManualInput).toBeTrue();
|
||||
});
|
||||
|
||||
it('should keep allowManualInput false when explicitly set to false', () => {
|
||||
const model = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date(),
|
||||
key: 'dateKey',
|
||||
allowManualInput: false
|
||||
});
|
||||
expect(model.allowManualInput).toBeFalse();
|
||||
});
|
||||
|
||||
it('should emit when format changes', () => {
|
||||
const model = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date(),
|
||||
key: 'dateKey',
|
||||
format: 'dd/MM/yyyy'
|
||||
});
|
||||
const formatChangeSpy = jasmine.createSpy('formatChangeSpy');
|
||||
model.formatChanges$.subscribe(formatChangeSpy);
|
||||
|
||||
model.format = 'yyyy-MM-dd';
|
||||
|
||||
expect(formatChangeSpy).toHaveBeenCalledOnceWith('yyyy-MM-dd');
|
||||
});
|
||||
|
||||
it('should not emit when format is assigned the same value', () => {
|
||||
const model = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date(),
|
||||
key: 'dateKey',
|
||||
format: 'dd/MM/yyyy'
|
||||
});
|
||||
const formatChangeSpy = jasmine.createSpy('formatChangeSpy');
|
||||
model.formatChanges$.subscribe(formatChangeSpy);
|
||||
|
||||
model.format = 'dd/MM/yyyy';
|
||||
|
||||
expect(formatChangeSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('property input replacement', () => {
|
||||
it('should re-sync valueDate when property input is replaced after init', () => {
|
||||
fixture.componentRef.setInput(
|
||||
'property',
|
||||
new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
format: '',
|
||||
editable: true
|
||||
})
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
const newDate = new Date('2022-11-20');
|
||||
fixture.componentRef.setInput(
|
||||
'property',
|
||||
new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: newDate,
|
||||
key: 'dateKey',
|
||||
format: '',
|
||||
editable: true
|
||||
})
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.valueDate).not.toBeNull();
|
||||
expect(component.valueDate instanceof Date).toBeTrue();
|
||||
});
|
||||
|
||||
it('should set valueDate to null when replacement property has no value', () => {
|
||||
fixture.componentRef.setInput(
|
||||
'property',
|
||||
new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: new Date('07/10/2017'),
|
||||
key: 'dateKey',
|
||||
format: '',
|
||||
editable: true
|
||||
})
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.componentRef.setInput(
|
||||
'property',
|
||||
new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: null,
|
||||
key: 'dateKey',
|
||||
format: '',
|
||||
editable: true
|
||||
})
|
||||
);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.valueDate).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateItem$ re-sync for multivalued', () => {
|
||||
it('should re-sync valueDate when updateItem$ emits for a multivalued property', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: ['2020-01-01', '2020-02-01'],
|
||||
key: 'dateKey',
|
||||
editable: true,
|
||||
multivalued: true
|
||||
});
|
||||
fixture.detectChanges();
|
||||
const cardViewUpdateService = TestBed.inject(CardViewUpdateService);
|
||||
const newDates = [new Date('2021-06-01'), new Date('2021-07-01')];
|
||||
|
||||
component.property.value = newDates;
|
||||
cardViewUpdateService.updateItem$.next({ key: 'dateKey', value: newDates } as any);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.valueDate).not.toBeNull();
|
||||
expect(component.valueDate instanceof Date).toBeTrue();
|
||||
});
|
||||
|
||||
it('should not update valueDate when updateItem$ emits for a different key on multivalued property', () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
label: 'Date label',
|
||||
value: ['2020-01-01'],
|
||||
key: 'dateKey',
|
||||
editable: true,
|
||||
multivalued: true
|
||||
});
|
||||
fixture.detectChanges();
|
||||
const originalValueDate = component.valueDate;
|
||||
const cardViewUpdateService = TestBed.inject(CardViewUpdateService);
|
||||
|
||||
cardViewUpdateService.updateItem$.next({ key: 'otherKey', value: [new Date('2021-06-01')] } as any);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.valueDate).toEqual(originalValueDate);
|
||||
});
|
||||
});
|
||||
|
||||
describe('FloatLabel behavior', () => {
|
||||
it('should set floatLabel to "always" when property has default value and is editable', async () => {
|
||||
component.property = new CardViewDateItemModel({
|
||||
|
||||
+92
-3
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, effect, Input, OnInit, ViewChild, ViewEncapsulation, inject } from '@angular/core';
|
||||
import { Component, OnChanges, effect, Input, OnInit, SimpleChanges, ViewChild, ViewEncapsulation, inject, DestroyRef } from '@angular/core';
|
||||
import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core';
|
||||
import {
|
||||
DatetimeAdapter,
|
||||
@@ -42,6 +42,10 @@ import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { IconModule } from '../../../icon/icon.module';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { BehaviorSubject, EMPTY, filter, switchMap } from 'rxjs';
|
||||
|
||||
const DEFAULT_DATE_FORMAT = 'MMM DD';
|
||||
|
||||
@Component({
|
||||
providers: [
|
||||
@@ -68,11 +72,13 @@ import { IconModule } from '../../../icon/icon.module';
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
host: { class: 'adf-card-view-dateitem' }
|
||||
})
|
||||
export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemModel> implements OnInit {
|
||||
export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemModel> implements OnInit, OnChanges {
|
||||
private readonly dateAdapter = inject<DateAdapter<Date>>(DateAdapter);
|
||||
private readonly datetimeAdapter = inject<DatetimeAdapter<Date>>(DatetimeAdapter);
|
||||
private readonly userPreferencesService = inject(UserPreferencesService);
|
||||
private readonly clipboardService = inject(ClipboardService);
|
||||
private readonly translateService = inject(TranslationService);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
@Input()
|
||||
displayEmpty = true;
|
||||
@@ -87,6 +93,8 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
||||
|
||||
cardViewDateTimeControl: FormControl<Date> = new FormControl<Date>(null);
|
||||
|
||||
private readonly property$ = new BehaviorSubject<CardViewDateItemModel | undefined>(undefined);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
// Use effect to react to locale signal changes (must be in injection context)
|
||||
@@ -95,8 +103,49 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes.property) {
|
||||
this.property$.next(this.property);
|
||||
|
||||
if (!changes.property.firstChange) {
|
||||
this.handleFormatChange();
|
||||
this.syncControlDisabledState();
|
||||
if (!this.property.multivalued) {
|
||||
this.initSingleValueProperty();
|
||||
} else {
|
||||
this.initMultivaluedProperty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.editable && !changes.editable.firstChange) {
|
||||
this.syncControlDisabledState();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
(this.dateAdapter as AdfDateFnsAdapter).displayFormat = 'MMM DD';
|
||||
this.property$.next(this.property);
|
||||
this.property$
|
||||
.pipe(
|
||||
switchMap((prop) => prop?.formatChanges$ ?? EMPTY),
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
)
|
||||
.subscribe(() => this.handleFormatChange());
|
||||
|
||||
this.cardViewUpdateService.updateItem$
|
||||
.pipe(
|
||||
filter((itemModel) => itemModel.key === this.property.key),
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
)
|
||||
.subscribe(() => {
|
||||
if (!this.property.multivalued) {
|
||||
this.initSingleValueProperty();
|
||||
} else {
|
||||
this.initMultivaluedProperty();
|
||||
}
|
||||
});
|
||||
|
||||
this.applyFormat();
|
||||
|
||||
if (this.property.multivalued) {
|
||||
this.initMultivaluedProperty();
|
||||
@@ -105,6 +154,16 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
||||
}
|
||||
}
|
||||
|
||||
private applyFormat(): void {
|
||||
if (this.property.allowManualInput && this.property.format) {
|
||||
(this.dateAdapter as AdfDateFnsAdapter).displayFormat = this.property.format;
|
||||
(this.datetimeAdapter as AdfDateTimeFnsAdapter).displayFormat = this.property.format;
|
||||
} else {
|
||||
(this.dateAdapter as AdfDateFnsAdapter).displayFormat = DEFAULT_DATE_FORMAT;
|
||||
(this.datetimeAdapter as AdfDateTimeFnsAdapter).displayFormat = null;
|
||||
}
|
||||
}
|
||||
|
||||
get showProperty(): boolean {
|
||||
return this.displayEmpty || !this.property.isEmpty();
|
||||
}
|
||||
@@ -126,6 +185,9 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
||||
this.property.value = DateFnsUtils.forceUtc(event.value);
|
||||
this.valueDate = DateFnsUtils.forceLocal(event.value);
|
||||
}
|
||||
if (this.property.allowManualInput) {
|
||||
this.cardViewDateTimeControl.setValue(this.valueDate, { emitEvent: false });
|
||||
}
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
@@ -133,6 +195,9 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
||||
|
||||
onDateClear() {
|
||||
this.valueDate = null;
|
||||
if (this.property.allowManualInput) {
|
||||
this.cardViewDateTimeControl.setValue(null, { emitEvent: false });
|
||||
}
|
||||
this.cardViewUpdateService.update({ ...this.property } as CardViewDateItemModel, null);
|
||||
this.property.value = null;
|
||||
this.property.default = null;
|
||||
@@ -169,12 +234,36 @@ export class CardViewDateItemComponent extends BaseCardView<CardViewDateItemMode
|
||||
this.cardViewUpdateService.update({ ...this.property } as CardViewDateItemModel, this.property.value);
|
||||
}
|
||||
|
||||
private syncControlDisabledState(): void {
|
||||
if (!this.property.allowManualInput) {
|
||||
return;
|
||||
}
|
||||
if (!this.isEditable) {
|
||||
this.cardViewDateTimeControl.disable({ emitEvent: false });
|
||||
} else {
|
||||
this.cardViewDateTimeControl.enable({ emitEvent: false });
|
||||
}
|
||||
}
|
||||
|
||||
private handleFormatChange(): void {
|
||||
this.applyFormat();
|
||||
if (this.property.allowManualInput && this.cardViewDateTimeControl.value !== null) {
|
||||
this.cardViewDateTimeControl.setValue(this.cardViewDateTimeControl.value, { emitEvent: false });
|
||||
}
|
||||
}
|
||||
|
||||
private initSingleValueProperty() {
|
||||
this.valueDate = null;
|
||||
if (this.property.value && !Array.isArray(this.property.value)) {
|
||||
const date = new Date(this.property.value);
|
||||
this.property.value = date;
|
||||
this.valueDate = this.property.type === 'date' ? DateFnsUtils.forceLocal(date) : date;
|
||||
}
|
||||
|
||||
if (this.property.allowManualInput) {
|
||||
this.cardViewDateTimeControl.setValue(this.valueDate ?? null);
|
||||
this.syncControlDisabledState();
|
||||
}
|
||||
}
|
||||
|
||||
private initMultivaluedProperty() {
|
||||
|
||||
@@ -20,4 +20,5 @@ import { CardViewItemProperties } from './card-view-item-properties.interface';
|
||||
export interface CardViewDateItemProperties extends CardViewItemProperties {
|
||||
format?: string;
|
||||
locale?: string;
|
||||
allowManualInput?: boolean;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DatePipe, registerLocaleData } from '@angular/common';
|
||||
import localeFr from '@angular/common/locales/fr';
|
||||
import { CardViewDateItemModel } from './card-view-dateitem.model';
|
||||
import { CardViewDateItemProperties } from '../interfaces/card-view.interfaces';
|
||||
import { DateFnsUtils } from '../../common/utils/date-fns-utils';
|
||||
|
||||
registerLocaleData(localeFr);
|
||||
|
||||
describe('CardViewDateItemModel', () => {
|
||||
let properties: CardViewDateItemProperties;
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
label: 'Date label',
|
||||
value: new Date('2025-03-07T15:30:00.000Z'),
|
||||
key: 'dateKey'
|
||||
};
|
||||
});
|
||||
|
||||
it('should initialize with date type and default allowManualInput', () => {
|
||||
const itemModel = new CardViewDateItemModel(properties);
|
||||
|
||||
expect(itemModel.type).toBe('date');
|
||||
expect(itemModel.allowManualInput).toBeFalse();
|
||||
});
|
||||
|
||||
it('should initialize format, locale and allowManualInput from properties', () => {
|
||||
const itemModel = new CardViewDateItemModel({
|
||||
...properties,
|
||||
format: 'fullDate',
|
||||
locale: 'fr',
|
||||
allowManualInput: true
|
||||
});
|
||||
|
||||
expect(itemModel.format).toBe('fullDate');
|
||||
expect(itemModel.locale).toBe('fr');
|
||||
expect(itemModel.allowManualInput).toBeTrue();
|
||||
});
|
||||
|
||||
describe('displayValue', () => {
|
||||
it('should prepare and transform a single date value', () => {
|
||||
const itemModel = new CardViewDateItemModel(properties);
|
||||
const preparedDate = new Date('2025-03-07T00:00:00.000');
|
||||
|
||||
spyOn(DateFnsUtils, 'forceLocal').and.returnValue(preparedDate);
|
||||
spyOn(itemModel, 'transformDate').and.returnValue('formatted-date');
|
||||
|
||||
expect(itemModel.displayValue).toBe('formatted-date');
|
||||
expect(DateFnsUtils.forceLocal).toHaveBeenCalledOnceWith(properties.value as Date);
|
||||
expect(itemModel.transformDate).toHaveBeenCalledOnceWith(preparedDate);
|
||||
});
|
||||
|
||||
it('should prepare and transform each value when multivalued', () => {
|
||||
const firstDate = new Date('2025-03-07T15:30:00.000Z');
|
||||
const secondDate = new Date('2025-03-08T15:30:00.000Z');
|
||||
const itemModel = new CardViewDateItemModel({
|
||||
...properties,
|
||||
multivalued: true,
|
||||
value: [firstDate, secondDate]
|
||||
});
|
||||
|
||||
spyOn(DateFnsUtils, 'forceLocal').and.callFake((date) => date as Date);
|
||||
spyOn(itemModel, 'transformDate').and.callFake((date) => `${(date as Date).toISOString()}-formatted`);
|
||||
|
||||
expect(itemModel.displayValue).toEqual([`${firstDate.toISOString()}-formatted`, `${secondDate.toISOString()}-formatted`]);
|
||||
expect(DateFnsUtils.forceLocal).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should return the default value when the single value is not present', () => {
|
||||
const itemModel = new CardViewDateItemModel({
|
||||
...properties,
|
||||
value: null,
|
||||
default: 'No date selected'
|
||||
});
|
||||
|
||||
expect(itemModel.displayValue).toBe('No date selected');
|
||||
});
|
||||
|
||||
it('should return the default value as an array when the multivalued value is not present', () => {
|
||||
const itemModel = new CardViewDateItemModel({
|
||||
...properties,
|
||||
multivalued: true,
|
||||
value: null,
|
||||
default: 'No dates selected'
|
||||
});
|
||||
|
||||
expect(itemModel.displayValue).toEqual(['No dates selected']);
|
||||
});
|
||||
|
||||
it('should return an empty array when the multivalued value and default are not present', () => {
|
||||
const itemModel = new CardViewDateItemModel({
|
||||
...properties,
|
||||
multivalued: true,
|
||||
value: null
|
||||
});
|
||||
|
||||
expect(itemModel.displayValue).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('transformDate', () => {
|
||||
it('should use the provided format and locale', () => {
|
||||
const value = new Date('2025-03-07T15:30:00.000Z');
|
||||
const itemModel = new CardViewDateItemModel({
|
||||
...properties,
|
||||
format: 'fullDate',
|
||||
locale: 'fr'
|
||||
});
|
||||
const expectedValue = new DatePipe('fr').transform(value, 'fullDate');
|
||||
|
||||
expect(itemModel.transformDate(value)).toBe(expectedValue as string);
|
||||
});
|
||||
|
||||
it('should use mediumDate and en-US when format and locale are not provided', () => {
|
||||
const value = new Date('2025-03-07T15:30:00.000Z');
|
||||
const itemModel = new CardViewDateItemModel(properties);
|
||||
const expectedValue = new DatePipe('en-US').transform(value, 'mediumDate');
|
||||
|
||||
expect(itemModel.transformDate(value)).toBe(expectedValue as string);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatChanges$', () => {
|
||||
it('should emit when format changes', () => {
|
||||
const itemModel = new CardViewDateItemModel({
|
||||
...properties,
|
||||
format: 'dd/MM/yyyy'
|
||||
});
|
||||
const formatChangeSpy = jasmine.createSpy('formatChangeSpy');
|
||||
|
||||
itemModel.formatChanges$.subscribe(formatChangeSpy);
|
||||
itemModel.format = 'yyyy-MM-dd';
|
||||
|
||||
expect(formatChangeSpy).toHaveBeenCalledOnceWith('yyyy-MM-dd');
|
||||
});
|
||||
|
||||
it('should not emit when the same format is assigned', () => {
|
||||
const itemModel = new CardViewDateItemModel({
|
||||
...properties,
|
||||
format: 'dd/MM/yyyy'
|
||||
});
|
||||
const formatChangeSpy = jasmine.createSpy('formatChangeSpy');
|
||||
|
||||
itemModel.formatChanges$.subscribe(formatChangeSpy);
|
||||
itemModel.format = 'dd/MM/yyyy';
|
||||
|
||||
expect(formatChangeSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -21,13 +21,29 @@ import { CardViewBaseItemModel } from './card-view-baseitem.model';
|
||||
import { CardViewDateItemProperties } from '../interfaces/card-view.interfaces';
|
||||
import { DatePipe } from '@angular/common';
|
||||
import { DateFnsUtils } from '../../common/utils/date-fns-utils';
|
||||
import { Subject } from 'rxjs';
|
||||
|
||||
type DateItemType = Date | Date[] | null;
|
||||
|
||||
export class CardViewDateItemModel extends CardViewBaseItemModel<DateItemType> implements CardViewItem, DynamicComponentModel {
|
||||
type = 'date';
|
||||
format: string;
|
||||
locale: string;
|
||||
allowManualInput = false;
|
||||
|
||||
private readonly formatChangesSubject = new Subject<string>();
|
||||
readonly formatChanges$ = this.formatChangesSubject.asObservable();
|
||||
private _format: string;
|
||||
|
||||
get format(): string {
|
||||
return this._format;
|
||||
}
|
||||
|
||||
set format(value: string) {
|
||||
if (this._format !== value) {
|
||||
this._format = value;
|
||||
this.formatChangesSubject.next(value);
|
||||
}
|
||||
}
|
||||
|
||||
constructor(cardViewDateItemProperties: CardViewDateItemProperties) {
|
||||
super(cardViewDateItemProperties);
|
||||
@@ -39,6 +55,10 @@ export class CardViewDateItemModel extends CardViewBaseItemModel<DateItemType> i
|
||||
if (cardViewDateItemProperties.locale) {
|
||||
this.locale = cardViewDateItemProperties.locale;
|
||||
}
|
||||
|
||||
if (cardViewDateItemProperties.allowManualInput !== undefined) {
|
||||
this.allowManualInput = cardViewDateItemProperties.allowManualInput;
|
||||
}
|
||||
}
|
||||
|
||||
get displayValue(): string | string[] {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { CardViewDatetimeItemModel } from './card-view-datetimeitem.model';
|
||||
import { CardViewDateItemProperties } from '../interfaces/card-view.interfaces';
|
||||
import { DateFnsUtils } from '../../common/utils/date-fns-utils';
|
||||
|
||||
describe('CardViewDatetimeItemModel', () => {
|
||||
let properties: CardViewDateItemProperties;
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
label: 'Datetime label',
|
||||
value: new Date('2025-03-07T15:30:00.000Z'),
|
||||
key: 'datetimeKey'
|
||||
};
|
||||
});
|
||||
|
||||
it('should initialize with datetime type and default datetime format', () => {
|
||||
const itemModel = new CardViewDatetimeItemModel(properties);
|
||||
|
||||
expect(itemModel.type).toBe('datetime');
|
||||
expect(itemModel.format).toBe('MMM d, y, H:mm');
|
||||
});
|
||||
|
||||
it('should preserve the provided format', () => {
|
||||
const itemModel = new CardViewDatetimeItemModel({
|
||||
...properties,
|
||||
format: 'yyyy-MM-dd HH:mm'
|
||||
});
|
||||
|
||||
expect(itemModel.format).toBe('yyyy-MM-dd HH:mm');
|
||||
});
|
||||
|
||||
it('should transform the original date value without forcing it to local midnight', () => {
|
||||
const itemModel = new CardViewDatetimeItemModel(properties);
|
||||
|
||||
spyOn(DateFnsUtils, 'forceLocal');
|
||||
spyOn(itemModel, 'transformDate').and.returnValue('formatted-datetime');
|
||||
|
||||
expect(itemModel.displayValue).toBe('formatted-datetime');
|
||||
expect(DateFnsUtils.forceLocal).not.toHaveBeenCalled();
|
||||
expect(itemModel.transformDate).toHaveBeenCalledOnceWith(properties.value as Date);
|
||||
});
|
||||
});
|
||||
@@ -22,13 +22,11 @@ import { CardViewDateItemProperties } from '../interfaces/card-view.interfaces';
|
||||
|
||||
export class CardViewDatetimeItemModel extends CardViewDateItemModel implements CardViewItem, DynamicComponentModel {
|
||||
type = 'datetime';
|
||||
format = 'MMM d, y, H:mm';
|
||||
|
||||
constructor(cardViewDateItemProperties: CardViewDateItemProperties) {
|
||||
super(cardViewDateItemProperties);
|
||||
|
||||
if (cardViewDateItemProperties.format) {
|
||||
this.format = cardViewDateItemProperties.format;
|
||||
if (!cardViewDateItemProperties.format) {
|
||||
this.format = 'MMM d, y, H:mm';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ export class AdfDateTimeFnsAdapter extends DatetimeAdapter<Date> {
|
||||
override format(date: Date, displayFormat: any): string {
|
||||
displayFormat = DateFnsUtils.convertMomentToDateFnsFormat(displayFormat);
|
||||
|
||||
if (this.displayFormat && displayFormat === this.formats?.display?.datetimeInput) {
|
||||
if (this.displayFormat && (displayFormat === this.formats?.display?.datetimeInput || displayFormat === this.formats?.display?.dateInput)) {
|
||||
return this._delegate.format(date, this.displayFormat || displayFormat);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user