bright-components 10.1.0 → 10.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,8 @@ import breakpoints from 'constants/breakpoints';
26
26
 
27
27
  import CalendarIcon from 'components/Icons/Calendar/';
28
28
 
29
+ import NewDayPicker from './NewDayPicker';
30
+
29
31
  const friendlyShort = 'EEE dd MMM';
30
32
  const friendlyShortWithYear = 'EEE dd MMM yyyy';
31
33
 
@@ -232,11 +234,12 @@ class DayPicker extends React.Component {
232
234
  panelAbsoluteOnDesktop,
233
235
  onSelectedDate,
234
236
  isDateSelect,
237
+ typeable,
235
238
  ...rest
236
239
  } = this.props;
237
240
 
238
241
  const { dayPickerOpen } = this.state;
239
- const { range } = this.props;
242
+ const { range, locale } = this.props;
240
243
 
241
244
  const fromDate = range.from
242
245
  ? format(range.from, this.getDisplayFormat())
@@ -250,20 +253,39 @@ class DayPicker extends React.Component {
250
253
 
251
254
  return (
252
255
  <DayPickerInputContainer {...rest}>
253
- <InputBox
254
- onClick={this.toggleDayPicker}
255
- error={error}
256
- disabled={disabled}
257
- legacyInputStyle={legacyInputStyle}
258
- data-testid="input-selector"
259
- >
260
- {datesToDisplay ? (
261
- <span>{datesToDisplay}</span>
262
- ) : (
263
- <Placeholder>{placeholder}</Placeholder>
264
- )}
265
- <OpenCalendarIcon size={22} disabled={disabled} />
266
- </InputBox>
256
+ {!typeable || dateRange ? (
257
+ <InputBox
258
+ onClick={this.toggleDayPicker}
259
+ error={error}
260
+ disabled={disabled}
261
+ legacyInputStyle={legacyInputStyle}
262
+ data-testid="input-selector"
263
+ >
264
+ {datesToDisplay ? (
265
+ <span>{datesToDisplay}</span>
266
+ ) : (
267
+ <Placeholder>{placeholder}</Placeholder>
268
+ )}
269
+ <OpenCalendarIcon size={22} disabled={disabled} />
270
+ </InputBox>
271
+ ) : (
272
+ <NewDayPicker
273
+ placeholder={placeholder}
274
+ datesToDisplay={datesToDisplay}
275
+ range={range}
276
+ allowClear={allowClear}
277
+ yearRange={yearRange}
278
+ onSelectedDate={onSelectedDate}
279
+ locale={locale}
280
+ clearValue={this.clearValue}
281
+ disabled={disabled}
282
+ error={error}
283
+ toggleDayPicker={this.toggleDayPicker}
284
+ GA={GA}
285
+ recordValue={recordValue}
286
+ dayPickerOpen={dayPickerOpen}
287
+ />
288
+ )}
267
289
 
268
290
  {dayPickerOpen && (
269
291
  <DayRangeContainer
@@ -330,7 +352,9 @@ DayPicker.propTypes = {
330
352
  legacyInputStyle: bool,
331
353
  closeButton: bool,
332
354
  panelAbsoluteOnDesktop: bool,
333
- isDateSelect: bool
355
+ isDateSelect: bool,
356
+ typeable: bool,
357
+ locale: string
334
358
  };
335
359
 
336
360
  DayPicker.defaultProps = {
@@ -356,7 +380,9 @@ DayPicker.defaultProps = {
356
380
  legacyInputStyle: false,
357
381
  closeButton: false,
358
382
  panelAbsoluteOnDesktop: true,
359
- isDateSelect: false
383
+ isDateSelect: false,
384
+ typeable: false,
385
+ locale: 'GB'
360
386
  };
361
387
 
362
388
  export { DayPicker as Unwrapped };
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
- import { render, fireEvent } from '@testing-library/react';
2
+ import { render, fireEvent, wait } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
3
4
  import ReactGA from 'react-ga';
4
5
  import DayPicker from '.';
5
6
 
@@ -406,4 +407,299 @@ describe('<DayPicker />', () => {
406
407
  expect(ReactGA.event).toHaveBeenCalledTimes(1);
407
408
  });
408
409
  });
410
+
411
+ describe('Typing date', () => {
412
+ it('should set the correct date when typed in', async () => {
413
+ wrapper.rerender(
414
+ <DayPicker
415
+ {...props}
416
+ typeable
417
+ dateRange={false}
418
+ yearRange={{ min: 2000, max: 2100 }}
419
+ GA={{
420
+ category: undefined,
421
+ actionId: undefined
422
+ }}
423
+ />
424
+ );
425
+ const inputSelect = wrapper.getByTestId('new input');
426
+
427
+ fireEvent.focus(inputSelect);
428
+ userEvent.type(inputSelect, `010203`);
429
+ await wait(() => expect(inputSelect.value).toEqual('01/02/03'));
430
+ fireEvent.blur(inputSelect);
431
+
432
+ expect(onMockSelectedDate).toHaveBeenLastCalledWith({
433
+ from: new Date('2003-02-01T00:00:00.000Z'),
434
+ to: new Date('2003-02-01T00:00:00.000Z')
435
+ });
436
+ });
437
+
438
+ it('should set the correct date when typed in - CA', () => {
439
+ wrapper.rerender(
440
+ <DayPicker
441
+ {...props}
442
+ typeable
443
+ dateRange={false}
444
+ yearRange={{ min: 2000, max: 2100 }}
445
+ locale="CA"
446
+ />
447
+ );
448
+ const inputSelect = wrapper.getByTestId('new input');
449
+
450
+ fireEvent.focus(inputSelect);
451
+ userEvent.type(inputSelect, '010203');
452
+ fireEvent.blur(inputSelect);
453
+
454
+ expect(onMockSelectedDate).toHaveBeenLastCalledWith({
455
+ from: new Date('2001-02-03T00:00:00.000Z'),
456
+ to: new Date('2001-02-03T00:00:00.000Z')
457
+ });
458
+ });
459
+
460
+ it('should show the correct date when date pre set', () => {
461
+ wrapper.rerender(
462
+ <DayPicker
463
+ {...props}
464
+ typeable
465
+ dateRange={false}
466
+ yearRange={{ min: 2000, max: 2100 }}
467
+ range={{
468
+ from: new Date('2019-08-01'),
469
+ to: undefined
470
+ }}
471
+ />
472
+ );
473
+ const inputSelect = wrapper.getByTestId('new input');
474
+
475
+ fireEvent.focus(inputSelect);
476
+
477
+ expect(inputSelect.value).toEqual('01/08/19');
478
+ });
479
+
480
+ it('should show the correct date when date pre set - CA', () => {
481
+ wrapper.rerender(
482
+ <DayPicker
483
+ {...props}
484
+ typeable
485
+ dateRange={false}
486
+ yearRange={{ min: 2000, max: 2100 }}
487
+ range={{
488
+ from: new Date('2019-08-01'),
489
+ to: undefined
490
+ }}
491
+ locale="CA"
492
+ />
493
+ );
494
+ const inputSelect = wrapper.getByTestId('new input');
495
+
496
+ fireEvent.focus(inputSelect);
497
+
498
+ expect(inputSelect.value).toEqual('19/08/01');
499
+ });
500
+
501
+ it('should allow clearing date', () => {
502
+ wrapper.rerender(
503
+ <DayPicker
504
+ {...props}
505
+ typeable
506
+ dateRange={false}
507
+ yearRange={{ min: 2000, max: 2100 }}
508
+ range={{
509
+ from: new Date('2019-08-01'),
510
+ to: undefined
511
+ }}
512
+ />
513
+ );
514
+ const inputSelect = wrapper.getByTestId('new input');
515
+
516
+ fireEvent.focus(inputSelect);
517
+ userEvent.type(inputSelect, '{selectall}{backspace}');
518
+ fireEvent.blur(inputSelect);
519
+
520
+ expect(onMockSelectedDate).toHaveBeenLastCalledWith({
521
+ from: undefined,
522
+ to: undefined
523
+ });
524
+ });
525
+
526
+ it('shouldnt save invalid dates', () => {
527
+ wrapper.rerender(
528
+ <DayPicker
529
+ {...props}
530
+ typeable
531
+ dateRange={false}
532
+ yearRange={{ min: 2000, max: 2100 }}
533
+ range={{
534
+ from: new Date('2019-08-01'),
535
+ to: undefined
536
+ }}
537
+ />
538
+ );
539
+ const inputSelect = wrapper.getByTestId('new input');
540
+
541
+ fireEvent.focus(inputSelect);
542
+ fireEvent.blur(inputSelect);
543
+ fireEvent.focus(inputSelect);
544
+ userEvent.type(inputSelect, '{selectall}{backspace}0101');
545
+ fireEvent.blur(inputSelect);
546
+
547
+ expect(onMockSelectedDate).toHaveBeenLastCalledWith('');
548
+ expect(wrapper.getByText('Date is invalid')).toBeInTheDocument();
549
+ });
550
+
551
+ it('shouldnt save dates outside the range', () => {
552
+ wrapper.rerender(
553
+ <DayPicker
554
+ {...props}
555
+ typeable
556
+ dateRange={false}
557
+ yearRange={{ min: 2018, max: 2030 }}
558
+ range={{
559
+ from: new Date('2019-08-01'),
560
+ to: undefined
561
+ }}
562
+ allowClear={false}
563
+ />
564
+ );
565
+ const inputSelect = wrapper.getByTestId('new input');
566
+
567
+ fireEvent.focus(inputSelect);
568
+ userEvent.type(inputSelect, '{selectall}{backspace}010131');
569
+ fireEvent.blur(inputSelect);
570
+
571
+ expect(onMockSelectedDate).toHaveBeenLastCalledWith({
572
+ from: new Date('2019-08-01T00:00:00.000Z'),
573
+ to: undefined
574
+ });
575
+ expect(
576
+ wrapper.getByText(
577
+ 'Year is outside of range. Must be between 2018 - 2030'
578
+ )
579
+ ).toBeInTheDocument();
580
+ });
581
+
582
+ it('should revert invalid date to empty if no initial date', () => {
583
+ onMockSelectedDate.mockClear();
584
+ wrapper.rerender(
585
+ <DayPicker
586
+ {...props}
587
+ typeable
588
+ dateRange={false}
589
+ yearRange={{ min: 2000, max: 2100 }}
590
+ range={{
591
+ from: undefined,
592
+ to: undefined
593
+ }}
594
+ allowClear={false}
595
+ />
596
+ );
597
+ const inputSelect = wrapper.getByTestId('new input');
598
+
599
+ fireEvent.focus(inputSelect);
600
+ fireEvent.blur(inputSelect);
601
+ fireEvent.focus(inputSelect);
602
+ userEvent.type(inputSelect, '{selectall}{backspace}0101');
603
+ fireEvent.blur(inputSelect);
604
+
605
+ expect(onMockSelectedDate).not.toHaveBeenCalled();
606
+ });
607
+
608
+ it('backspace / automatically', () => {
609
+ wrapper.rerender(
610
+ <DayPicker
611
+ {...props}
612
+ typeable
613
+ dateRange={false}
614
+ yearRange={{ min: 2000, max: 2100 }}
615
+ range={{
616
+ from: new Date('2019-08-01'),
617
+ to: undefined
618
+ }}
619
+ />
620
+ );
621
+ const inputSelect = wrapper.getByTestId('new input');
622
+
623
+ fireEvent.focus(inputSelect);
624
+ userEvent.type(inputSelect, `{backspace}{backspace}/`);
625
+ expect(inputSelect.value).toEqual('01/08/');
626
+ });
627
+
628
+ it('should open calendar view with the icon', () => {
629
+ wrapper.rerender(
630
+ <DayPicker
631
+ {...props}
632
+ typeable
633
+ dateRange={false}
634
+ yearRange={{ min: 2000, max: 2100 }}
635
+ range={{
636
+ from: new Date('2019-08-01'),
637
+ to: undefined
638
+ }}
639
+ />
640
+ );
641
+
642
+ fireEvent.click(wrapper.getByTestId('calendarIcon'));
643
+ expect(wrapper.getByTestId('daypicker-panel')).toBeInTheDocument();
644
+
645
+ const inputSelect = wrapper.getByTestId('new input');
646
+ fireEvent.focus(inputSelect);
647
+ fireEvent.blur(inputSelect);
648
+
649
+ expect(
650
+ wrapper.queryByTestId('daypicker-panel')
651
+ ).not.toBeInTheDocument();
652
+ });
653
+
654
+ it('should apply the error styling if error', () => {
655
+ wrapper.rerender(
656
+ <DayPicker {...props} dateRange={false} typeable error />
657
+ );
658
+
659
+ const inputSelect = wrapper.getByTestId('input-selector');
660
+ expect(inputSelect).toHaveStyleRule(
661
+ 'border-color',
662
+ '#FF5000 !important'
663
+ );
664
+ });
665
+
666
+ it('should apply the error styling if disabled', () => {
667
+ wrapper.rerender(
668
+ <DayPicker {...props} typeable dateRange={false} disabled />
669
+ );
670
+
671
+ const inputSelect = wrapper.getByTestId('new input');
672
+ expect(inputSelect).toBeDisabled();
673
+ });
674
+
675
+ it('should call the correct GA tags when the date is typed', async () => {
676
+ wrapper.rerender(
677
+ <DayPicker
678
+ {...props}
679
+ typeable
680
+ dateRange={false}
681
+ range={{
682
+ from: undefined,
683
+ to: undefined
684
+ }}
685
+ recordValue
686
+ />
687
+ );
688
+ expect(ReactGA.event).toHaveBeenCalledTimes(0);
689
+
690
+ const inputSelect = wrapper.getByTestId('new input');
691
+ fireEvent.focus(inputSelect);
692
+ userEvent.type(inputSelect, `201222`);
693
+ await wait(() => expect(inputSelect.value).toEqual('20/12/22'));
694
+ fireEvent.blur(inputSelect);
695
+
696
+ await wait(() =>
697
+ expect(ReactGA.event).toHaveBeenCalledWith({
698
+ action: 'ActionID - Set via Typing',
699
+ category: 'Test',
700
+ label: 'Tue 20 Dec 2022'
701
+ })
702
+ );
703
+ });
704
+ });
409
705
  });
@@ -178,7 +178,7 @@ class DayPickerPanel extends React.Component {
178
178
  range: { from, to }
179
179
  } = this.state;
180
180
 
181
- const actionEvent = selectedDay ? 'Set' : 'Apply';
181
+ const actionEvent = selectedDay ? 'Set via Date Picker' : 'Apply';
182
182
 
183
183
  if (category) {
184
184
  const label = dateRange
@@ -73,7 +73,7 @@ describe('Month Year and Calendar view', () => {
73
73
 
74
74
  expect(event).toHaveBeenCalledWith({
75
75
  category: 'Cat',
76
- action: 'Action ID - Set',
76
+ action: 'Action ID - Set via Date Picker',
77
77
  label: 'Tue 03 Apr 2018'
78
78
  });
79
79
  });
@@ -20,7 +20,7 @@ describe('<DurationInput />', () => {
20
20
  });
21
21
 
22
22
  it('should select a new days value when the days input is changed', () => {
23
- const daysInput = wrapper.getByRole('textbox');
23
+ const daysInput = wrapper.getByRole('spinbutton');
24
24
 
25
25
  expect(onChange).not.toHaveBeenCalled();
26
26
 
@@ -36,7 +36,7 @@ describe('<DurationInput />', () => {
36
36
  const blurProps = { ...props, onBlur: jest.fn() };
37
37
  wrapper.rerender(<DurationInput {...blurProps} />);
38
38
 
39
- const daysInput = wrapper.getByRole('textbox');
39
+ const daysInput = wrapper.getByRole('spinbutton');
40
40
  fireEvent.blur(daysInput);
41
41
 
42
42
  expect(blurProps.onBlur).toHaveBeenCalled();
@@ -54,7 +54,9 @@ describe('<DurationInput />', () => {
54
54
  });
55
55
 
56
56
  it('should select a new hours value and a new minutes value when the inputs are changed', () => {
57
- const [hoursInput, minutesInput] = wrapper.getAllByRole('textbox');
57
+ const [hoursInput, minutesInput] = wrapper.getAllByRole(
58
+ 'spinbutton'
59
+ );
58
60
 
59
61
  expect(onChange).not.toHaveBeenCalled();
60
62
 
@@ -33,7 +33,7 @@ describe('EmployeePickerFilterBar', () => {
33
33
  });
34
34
 
35
35
  const getInput = () => element.getByPlaceholderText('Enter name');
36
- const getSelect = () => element.getByRole('listbox');
36
+ const getSelect = () => element.getByRole('combobox');
37
37
 
38
38
  it('should call updateType when the user selects a filter type', () => {
39
39
  const input = getInput();
@@ -3,6 +3,7 @@ import spacing from 'constants/spacing';
3
3
 
4
4
  const ModalBody = styled.div`
5
5
  padding: ${props => (props.noPadding ? '0px' : spacing.s2)};
6
+ overflow-y: auto;
6
7
  `;
7
8
  ModalBody.displayName = 'ModalBody';
8
9
 
@@ -34,6 +34,8 @@ const Background = styled.div`
34
34
  `;
35
35
 
36
36
  const ModalContainer = styled.div`
37
+ display: flex;
38
+ flex-direction: column;
37
39
  background: white;
38
40
  position: relative;
39
41
  width: ${props => props.width};
@@ -43,7 +45,6 @@ const ModalContainer = styled.div`
43
45
  border-radius: ${vars.borderRadius};
44
46
  max-height: 90%;
45
47
  overflow-x: hidden;
46
- overflow-y: auto;
47
48
  ${props => props.version === '1' && '-webkit-overflow-scrolling: touch'};
48
49
 
49
50
  ${props =>
@@ -133,7 +133,6 @@ describe('<Modal />', () => {
133
133
  const modalContainer = wrapper.getByRole('region');
134
134
 
135
135
  expect(modalContainer).toHaveStyleRule('overflow-x', 'hidden');
136
- expect(modalContainer).toHaveStyleRule('overflow-y', 'auto');
137
136
 
138
137
  wrapper.rerender(
139
138
  <Modal close={closeFn} allowOverflow>
@@ -15,7 +15,7 @@ describe('<ResponsiveTabs />', () => {
15
15
  </ResponsiveTabs>
16
16
  );
17
17
 
18
- expect(getByRole('listbox')).toBeInTheDocument();
18
+ expect(getByRole('combobox')).toBeInTheDocument();
19
19
  expect(getAllByRole('option')).toHaveLength(2);
20
20
  });
21
21
 
@@ -38,7 +38,7 @@ describe('<ResponsiveTabs />', () => {
38
38
  expect(getByTestId('tab1')).toBeVisible();
39
39
  expect(getByTestId('tab2')).not.toBeVisible();
40
40
 
41
- const dropdown = getByRole('listbox');
41
+ const dropdown = getByRole('combobox');
42
42
  fireEvent.change(dropdown, { target: { value: 'Two' } });
43
43
 
44
44
  expect(getByTestId('tab1')).not.toBeVisible();
@@ -90,7 +90,7 @@ describe('<ResponsiveTabs />', () => {
90
90
  </ResponsiveTabs>
91
91
  );
92
92
 
93
- fireEvent.change(getByRole('listbox'), { target: { value: 'One' } });
93
+ fireEvent.change(getByRole('combobox'), { target: { value: 'One' } });
94
94
  expect(event).not.toHaveBeenCalled();
95
95
 
96
96
  fireEvent.click(getByRole('tab'));
@@ -118,6 +118,7 @@ const TimePicker = ({
118
118
  <>
119
119
  <MobileTimePicker
120
120
  type="time"
121
+ data-testid="mobileTimePicker"
121
122
  onChange={e => {
122
123
  const timeString = e.target.value;
123
124
  if (timeString.length === 5) {