Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ACS-5857] Moment to Date-Fns Migration for adfMomentDate Pipe. #8924

Closed
wants to merge 19 commits into from

Conversation

SheenaMalhotra182
Copy link
Contributor

@SheenaMalhotra182 SheenaMalhotra182 commented Sep 22, 2023

Please check if the PR fulfills these requirements

  • The commit message follows our guidelines
  • Tests for the changes have been added (for bug fixes / features)
  • Docs have been added / updated (for bug fixes / features)

What kind of change does this PR introduce? (check one with "x")

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • Documentation
  • Other... Please describe:

What is the current behaviour? (You can also link to an open issue here)
Moment Library is being used in adfMomentDate Pipe.

What is the new behaviour?
This PR includes migration of 'lib/core/src/lib/pipes/moment-date.pipe.ts' into date-fns

  1. Created a new Pipe (adfDate) which uses date-fns library instead of Moment.
  2. Replaced occurrences of adfMomentDate with adfDate in 'DateWidgetComponent' and 'DateCloudWidgetComponent' and accordingly changed the ts and spec files
  3. Replaced occurrences of adfMomentDate with adfDate in 'DateTimeWidgetComponent' and accordingly changed the ts and spec files

Does this PR introduce a breaking change? (check one with "x")

  • Yes
  • No

If this PR contains a breaking change, please describe the impact and migration path for existing applications: ...

Other information:
https://alfresco.atlassian.net/browse/ACS-5857

There's one unit test case which needed the format of test data to be changed in order to validate it with the default format =>
https://github.com/Alfresco/alfresco-ng2-components/pull/8924/files#diff-b52520351ccf61f2557086298ce2a0d0e574463d39d0fa921085bcc2af1c1760R1116.
Rest all unit tests and e2es work fine without needing any changes.

let service: DateFormatTranslationService;

beforeEach(() => {
TestBed.configureTestingModule({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need this block, the service does not use Translation or Root, and it has providedIn: root, so you don't need setting the "providers" section too

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

a: 'A'
};

convertMomentToDateFnsFormat(dateDisplayFormat: string): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provide code comments for the public methods so that it gets to the generated documentation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added comments

import { DateFormatTranslationService } from '../form/services/date-format-translation.service';

@Pipe({ name: 'adfDate' })
export class DatePipe implements PipeTransform {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the naming of class is going to clash with Angular DatePipe: https://angular.io/api/common/DatePipe
please use AdfDatePipe as the pipe name suggests

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed

@@ -19,9 +19,16 @@ import moment from 'moment';
import { FormFieldTypes } from './form-field-types';
import { FormFieldModel } from './form-field.model';
import { FormModel } from './form.model';
import { DateFormatTranslationService } from '../../../public-api';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incorrect import path

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed import path

return format(date, this.convertMomentToDateFnsFormat(dateFormat));
}

parse(value: string, dateFormat: string, date = new Date()): Date {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this function is confusing, why it takes date parameter and why it sets it to new date() ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if there are no cases when you pass the date here, please use "new Date()" inside the "parse" call instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated to call new Date() inside parse itself.

const date = moment(newDateValue, this.field.dateDisplayFormat, true);
if (date.isValid()) {
this.field.value = date.format(this.field.dateDisplayFormat);
const date = this.dateFormatTranslationService.parse(newDateValue, this.field.dateDisplayFormat, new Date());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you pass new date there as third param? what is the reason for that?

@@ -1,6 +1,6 @@
<div class="{{field.className}}" id="data-widget" [class.adf-invalid]="!field.isValid && isTouched()">
<mat-form-field class="adf-date-widget" [hideRequiredMarker]="true">
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }} ({{field.dateDisplayFormat}})<span class="adf-asterisk"
<label class="adf-label" [attr.for]="field.id">{{field.name | translate }} ({{dateFormatTranslationService.convertDateFnsToMomentFormat(field.dateDisplayFormat)}})<span class="adf-asterisk"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please extract to a separate function so that template does not deal with services conversion directly:

formatLabel(field: FormField): string {
  const displayName = this.translationService.instant(field.name);
  const displayFormat = this.dateFormatTranslationService.convertDateFnsToMomentFormat(field.dateDisplayFormat);

  return `${displayName} (${displayFormat})`; 
}

then in HTML:

<label>{formatLabel(field)}</label>

also provide unit tests for the formatLabel function as it becomes easier

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

implemented


beforeEach(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), CoreTestingModule],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check if you need those imports, probably not

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed unnecessary imports

import { CoreTestingModule } from '../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { DatePipe } from './date.pipe';
import { DateFormatTranslationService } from '../form/public-api';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incorrect import path

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -1,11 +1,11 @@
<div class="{{field.className}}" id="data-widget" [class.adf-invalid]="!field.isValid && isTouched()" [class.adf-left-label-input-container]="field.leftLabels">
<div *ngIf="field.leftLabels">
<label class="adf-label adf-left-label" [attr.for]="field.id">{{field.name | translate }} ({{field.dateDisplayFormat}})<span class="adf-asterisk"
<label class="adf-label adf-left-label" [attr.for]="field.id">{{field.name | translate }} ({{dateFormatTranslationService.convertDateFnsToMomentFormat(field.dateDisplayFormat)}})<span class="adf-asterisk"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be extracted to a function, UI templates should not deal with services directly, see https://github.com/Alfresco/alfresco-ng2-components/pull/8924/files#r1335592062

*ngIf="isRequired()">*</span></label>
</div>
<div>
<mat-form-field class="adf-date-widget" [class.adf-left-label-input-datepicker]="field.leftLabels" [hideRequiredMarker]="true">
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">{{field.name | translate }} ({{field.dateDisplayFormat}})<span class="adf-asterisk"
<label class="adf-label" *ngIf="!field.leftLabels" [attr.for]="field.id">{{field.name | translate }} ({{dateFormatTranslationService.convertDateFnsToMomentFormat(field.dateDisplayFormat)}})<span class="adf-asterisk"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be extracted to a function, UI templates should not deal with services directly, see https://github.com/Alfresco/alfresco-ng2-components/pull/8924/files#r1335592062

}
} else {
if (this.field.minValue) {
this.minDate = moment(this.field.minValue, DATE_FORMAT_CLOUD);
this.minDate = this.dateFormatTranslationService.format(new Date(this.field.minValue), DATE_FORMAT_CLOUD);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you need translation if you already have new Date() and export const DATE_FORMAT_CLOUD = 'yyyy-MM-dd'; ? you can probably use datefns directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

after dome changes now, DATE_FORMAT_CLOUD is 'YYYY-MM-DD' like before.
It also helps to maintain uniformity across all places if we use format or parse methods from DateFnsUtils.


const momentDateAdapter = this.dateAdapter as MomentDateAdapter;
momentDateAdapter.overrideDisplayFormat = this.field.dateDisplayFormat;
this.dateFormatConfig.display.dateInput = this.field.dateDisplayFormat;

if (this.field) {
if (this.field.dynamicDateRangeSelection) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review this block as you probably don't need format translation service as today is a Date object

it('should return the input value when given an invalid date object', () => {
const inputDate = new Date('invalid');
const dateFormat = 'DD-MM-YYYY';
const expectedOutput = inputDate.toString();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

your expected output should not be based on .toString() conversion, as whatever happens there the test will be just green. Add exact value it expects instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

@@ -583,3 +584,39 @@ export const FORM_FIELD_VALIDATORS = [
new MinDateTimeFieldValidator(),
new MaxDateTimeFieldValidator()
];

export const momentToDateFnsMap = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are using this maps in 2 places, second is translation service; extract to a separate class and use same import in both cases

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved all logic to DateFnsUtils

a: 'A'
};

export function convertMomentToDateFnsFormat(dateDisplayFormat: string): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provide documentation comments for public functions

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added documentation

providedIn: 'root'
})
export class DateFormatTranslationService {
private momentToDateFnsMap = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are using this maps in 2 places, extract to a separate class and use same import in both cases

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you already have DateFnsUtils which can be a good place

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved all logic to DateFnsUtils

import { DatePipe } from './date.pipe';
import { DateFormatTranslationService } from '../form/public-api';

describe('DatePipe', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please provide tests with different variations of the date format, i.e.

  • YYYY-MM-DD
  • YYYY-DD-MM
  • MM-DD-YY

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added steps to check wth different date formats

@@ -36,6 +36,7 @@ import { MomentDateTimePipe } from './moment-datetime.pipe';
import { FilterStringPipe } from './filter-string.pipe';
import { FilterOutArrayObjectsByPropPipe } from './filter-out-every-object-by-prop.pipe';
import { DateTimePipe } from './date-time.pipe';
import { DatePipe } from './date.pipe';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be AdfDatePipe not to clash with Angular DatePipe

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed

return dateDisplayFormat;
}

export function convertDateFnsToMomentFormat(dateDisplayFormat: string): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move all these functions to DateFnsUtils class to have a single place for all date/time code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved all logic to DateFnsUtils

return dateDisplayFormat;
}

export function formatUpdate(date: number|Date, dateFormat: string): string {
Copy link
Contributor

@DenysVuika DenysVuika Sep 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to DateFnsUtils class and use better naming, like formatDate, it is already implied that some update is going to happen

return format(date, convertMomentToDateFnsFormat(dateFormat));
}

export function parseUpdate(value: string, dateFormat: string, date = new Date()): Date {
Copy link
Contributor

@DenysVuika DenysVuika Sep 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to DateFnsUtils class and use better naming, like parseDate, it is already implied that some update is going to happen

@Injectable({
providedIn: 'root'
})
export class DateFormatTranslationService {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This service has a lot of duplicated code with Field Validator funcitons.
you probably don't need this service at all, if you move all the field validator functions to DateFnsUtils, you can use those utils without this service

@@ -170,23 +171,23 @@ describe('DateWidgetComponent', () => {
readOnly: 'false'
});
widget.field.isVisible = true;
widget.field.dateDisplayFormat = 'MM-DD-YYYY';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified test data


fixture.detectChanges();

let dateElement: any = element.querySelector('#date-field-id');
expect(dateElement.value).toContain('12-30-9999');

widget.field.value = '5-6-2019 00:00';
widget.field.dateDisplayFormat = 'D-M-YYYY HH:mm';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is data coming from the backend


private onDestroy$ = new Subject<boolean>();

constructor(public formService: FormService,
private dateAdapter: DateAdapter<Moment>,
private userPreferencesService: UserPreferencesService) {
private dateAdapter: DateAdapter<DateFnsAdapter>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incorrect parameter, should be the value type, not another adapter,
valid cases are DateAdapter<Moment | Date | string> etc

@sonarqubecloud
Copy link

sonarqubecloud bot commented Oct 9, 2023

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
4.7% 4.7% Duplication

idea Catch issues before they fail your Quality Gate with our IDE extension sonarlint SonarLint

@DenysVuika
Copy link
Contributor

closing as too many issues

@DenysVuika DenysVuika closed this Oct 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants