-
Notifications
You must be signed in to change notification settings - Fork 1
refactor: TTA-49 change date picker #886
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
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| <form (ngSubmit)="onSubmit()" class="date-range-form"> | ||
| <label class="col-form-label my-1">Select your Date Range:</label> | ||
| <mat-form-field appearance="fill"> | ||
| <mat-label>Enter a date range</mat-label> | ||
| <mat-date-range-input [formGroup]="range" [rangePicker]="picker"> | ||
| <input matStartDate formControlName="start" placeholder="Start date" /> | ||
| <input matEndDate formControlName="end" placeholder="End date" /> | ||
| </mat-date-range-input> | ||
| <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle> | ||
| <mat-date-range-picker | ||
| #picker | ||
| [calendarHeaderComponent]="TimeRangeHeader" | ||
| ></mat-date-range-picker> | ||
| </mat-form-field> | ||
| <div class="col-12 col-md-2 my-1"> | ||
| <button type="submit" class="btn btn-primary">Search</button> | ||
| </div> | ||
| </form> | ||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| :host { | ||
| display: flex; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| justify-content: center; | ||
| height: 100%; | ||
| } | ||
|
|
||
| .date-range-form{ | ||
| display: flex; | ||
| justify-content: space-between; | ||
| width: 100%; | ||
| } | ||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| import { SimpleChange } from '@angular/core'; | ||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | ||
| import { FormsModule, ReactiveFormsModule } from '@angular/forms'; | ||
| import { MockStore, provideMockStore } from '@ngrx/store/testing'; | ||
| import * as moment from 'moment'; | ||
| import { ToastrService } from 'ngx-toastr'; | ||
| import { EntryState } from 'src/app/modules/time-clock/store/entry.reducer'; | ||
| import * as entryActions from '../../../time-clock/store/entry.actions'; | ||
|
|
||
|
||
|
|
||
| import { TimeRangeCustomComponent } from './time-range-custom.component'; | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add one aditional whitespace after imports |
||
| describe('TimeRangeCustomComponent', () => { | ||
| let component: TimeRangeCustomComponent; | ||
| let fixture: ComponentFixture<TimeRangeCustomComponent>; | ||
| let store: MockStore<EntryState>; | ||
| const toastrServiceStub = { | ||
| error: () => {} | ||
| }; | ||
|
|
||
| const timeEntry = { | ||
| id: '1', | ||
| start_date: new Date(), | ||
| end_date: new Date(), | ||
| activity_id: '1', | ||
| technologies: ['react', 'redux'], | ||
| comments: 'any comment', | ||
| uri: 'TT-123', | ||
| project_id: '1' | ||
| }; | ||
|
|
||
| const state = { | ||
| active: timeEntry, | ||
| entryList: [timeEntry], | ||
| isLoading: false, | ||
| message: 'test', | ||
| createError: false, | ||
| updateError: false, | ||
| timeEntriesSummary: null, | ||
| entriesForReport: [timeEntry], | ||
| }; | ||
|
|
||
| beforeEach(async () => { | ||
| await TestBed.configureTestingModule({ | ||
| imports: [FormsModule, ReactiveFormsModule], | ||
| declarations: [ TimeRangeCustomComponent ], | ||
| providers: [ | ||
| provideMockStore({ initialState: state }), | ||
| { provide: ToastrService, useValue: toastrServiceStub } | ||
| ], | ||
| }) | ||
| .compileComponents(); | ||
| store = TestBed.inject(MockStore); | ||
| }); | ||
|
|
||
| beforeEach(() => { | ||
| fixture = TestBed.createComponent(TimeRangeCustomComponent); | ||
| component = fixture.componentInstance; | ||
| fixture.detectChanges(); | ||
| }); | ||
|
|
||
| it('should create', () => { | ||
| expect(component).toBeTruthy(); | ||
| }); | ||
|
|
||
| it('setInitialDataOnScreen on ngOnInit', () => { | ||
| spyOn(component, 'setInitialDataOnScreen'); | ||
|
|
||
| component.ngOnInit(); | ||
|
|
||
| expect(component.setInitialDataOnScreen).toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('LoadEntriesByTimeRange action is triggered when start date is before end date', () => { | ||
| const end = moment(new Date()).subtract(1, 'days'); | ||
| const start = moment(new Date()); | ||
| spyOn(store, 'dispatch'); | ||
| component.range.controls.start.setValue(end); | ||
| component.range.controls.end.setValue(start); | ||
|
|
||
| component.onSubmit(); | ||
|
|
||
| expect(store.dispatch).toHaveBeenCalledWith(new entryActions.LoadEntriesByTimeRange({ | ||
| start_date: end.startOf('day'), | ||
| end_date: start.endOf('day') | ||
| })); | ||
| }); | ||
|
|
||
| it('shows an error when the end date is before the start date', () => { | ||
| spyOn(toastrServiceStub, 'error'); | ||
| const yesterday = moment(new Date()).subtract(2, 'days'); | ||
| const today = moment(new Date()); | ||
| spyOn(store, 'dispatch'); | ||
| component.range.controls.start.setValue(today); | ||
| component.range.controls.end.setValue(yesterday); | ||
|
|
||
| component.onSubmit(); | ||
|
|
||
| expect(toastrServiceStub.error).toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('setInitialDataOnScreen sets dates in form', () => { | ||
| spyOn(component.range.controls.start, 'setValue'); | ||
| spyOn(component.range.controls.end, 'setValue'); | ||
|
|
||
| component.setInitialDataOnScreen(); | ||
|
|
||
| expect(component.range.controls.start.setValue).toHaveBeenCalled(); | ||
| expect(component.range.controls.end.setValue).toHaveBeenCalled(); | ||
|
|
||
| }); | ||
|
|
||
| it('triggers onSubmit to set initial data', () => { | ||
| spyOn(component, 'onSubmit'); | ||
|
|
||
| component.setInitialDataOnScreen(); | ||
|
|
||
| expect(component.onSubmit).toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('When the ngOnChanges method is called, the onSubmit method is called', () => { | ||
| const userIdCalled = 'test-user-1'; | ||
| spyOn(component, 'onSubmit'); | ||
|
|
||
| component.ngOnChanges({userId: new SimpleChange(null, userIdCalled, false)}); | ||
|
|
||
| expect(component.onSubmit).toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| it('When the ngOnChanges method is the first change, the onSubmit method is not called', () => { | ||
| const userIdNotCalled = 'test-user-2'; | ||
| spyOn(component, 'onSubmit'); | ||
|
|
||
| component.ngOnChanges({userId: new SimpleChange(null, userIdNotCalled, true)}); | ||
|
|
||
| expect(component.onSubmit).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a white space at the end of the file |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| import { OnChanges, SimpleChanges, Component, OnInit, Input } from '@angular/core'; | ||
| import { FormControl, FormGroup } from '@angular/forms'; | ||
| import { TimeRangeHeaderComponent } from './time-range-header/time-range-header.component'; | ||
| import { DATE_FORMAT } from 'src/environments/environment'; | ||
| import { formatDate } from '@angular/common'; | ||
| import {Store} from '@ngrx/store'; | ||
| import {EntryState} from '../../../time-clock/store/entry.reducer'; | ||
| import { ToastrService } from 'ngx-toastr'; | ||
|
|
||
| import * as entryActions from '../../../time-clock/store/entry.actions'; | ||
| import * as moment from 'moment'; | ||
|
|
||
|
|
||
| @Component({ | ||
| selector: 'app-time-range-custom', | ||
| templateUrl: './time-range-custom.component.html', | ||
| styleUrls: ['./time-range-custom.component.scss'] | ||
| }) | ||
| export class TimeRangeCustomComponent implements OnInit, OnChanges { | ||
| @Input() userId: string; | ||
|
|
||
| readonly TimeRangeHeader = TimeRangeHeaderComponent; | ||
|
|
||
| range = new FormGroup({ | ||
| start: new FormControl(), | ||
| end: new FormControl(), | ||
| }); | ||
|
|
||
| constructor(private store: Store<EntryState>, private toastrService: ToastrService) { | ||
| } | ||
| ngOnInit(): void { | ||
| this.setInitialDataOnScreen(); | ||
| } | ||
|
|
||
| ngOnChanges(changes: SimpleChanges){ | ||
| if (!changes.userId.firstChange){ | ||
| this.onSubmit(); | ||
| } | ||
| } | ||
|
|
||
| setInitialDataOnScreen() { | ||
| this.range.setValue({ | ||
| start: formatDate(moment().startOf('isoWeek').format('l'), DATE_FORMAT, 'en'), | ||
| end: formatDate(moment().format('l'), DATE_FORMAT, 'en') | ||
| }); | ||
| this.onSubmit(); | ||
| } | ||
|
|
||
| onSubmit() { | ||
| const startDate = moment(this.range.getRawValue().start).startOf('day'); | ||
| const endDate = moment(this.range.getRawValue().end).endOf('day'); | ||
| if (endDate.isBefore(startDate)) { | ||
| this.toastrService.error('The end date should be after the start date'); | ||
| } else { | ||
| this.store.dispatch(new entryActions.LoadEntriesByTimeRange({ | ||
| start_date: moment(this.range.getRawValue().start).startOf('day'), | ||
| end_date: moment(this.range.getRawValue().end).endOf('day'), | ||
| }, this.userId)); | ||
| } | ||
| } | ||
|
|
||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| <app-time-range-panel></app-time-range-panel> | ||
|
|
||
| <div class="time-range-header"> | ||
| <button | ||
| mat-icon-button | ||
| class="time-range-double-arrow" | ||
| (click)="previousClicked('year')" | ||
| > | ||
| <mat-icon>keyboard_arrow_left</mat-icon> | ||
| <mat-icon>keyboard_arrow_left</mat-icon> | ||
| </button> | ||
| <button mat-icon-button class="time-range-month" (click)="previousClicked('month')"> | ||
| <mat-icon>keyboard_arrow_left</mat-icon> | ||
| </button> | ||
|
|
||
| <span class="time-range-header-label">{{ periodLabel }}</span> | ||
|
|
||
| <button mat-icon-button class="time-range-month-next" (click)="nextClicked('month')"> | ||
| <mat-icon>keyboard_arrow_right</mat-icon> | ||
| </button> | ||
| <button | ||
| mat-icon-button | ||
| class="time-range-double-arrow time-range-double-arrow-next" | ||
| (click)="nextClicked('year')" | ||
| > | ||
| <mat-icon>keyboard_arrow_right</mat-icon> | ||
| <mat-icon>keyboard_arrow_right</mat-icon> | ||
| </button> | ||
| </div> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| .time-range-header { | ||
| display: flex; | ||
| align-items: center; | ||
| padding: 0.5em; | ||
| } | ||
|
|
||
| .time-range-header-label { | ||
| flex: 1; | ||
| height: 1em; | ||
| font-weight: 500; | ||
| text-align: center; | ||
| } | ||
|
|
||
| .time-range-double-arrow .mat-icon { | ||
| margin: -22%; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leave only one white space at the end of the file