@ministryofjustice/frontend 3.0.3 → 3.2.0

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.
@@ -2,6 +2,7 @@
2
2
  * @jest-environment jsdom
3
3
  */
4
4
  const {
5
+ getAllByRole,
5
6
  getByText,
6
7
  getByRole,
7
8
  queryByRole,
@@ -9,6 +10,7 @@ const {
9
10
  screen,
10
11
  } = require("@testing-library/dom");
11
12
  const { userEvent } = require("@testing-library/user-event");
13
+ const dayjs = require("dayjs");
12
14
  const { configureAxe, toHaveNoViolations } = require("jest-axe");
13
15
  expect.extend(toHaveNoViolations);
14
16
 
@@ -23,19 +25,37 @@ const axe = configureAxe({
23
25
  },
24
26
  });
25
27
 
26
- const createComponent = () => {
27
- const html = `
28
- <div class="moj-datepicker" data-module="moj-date-picker">
29
- <div class="govuk-form-group">
30
- <label class="govuk-label" for="date">
31
- Date
32
- </label>
33
- <div id="date-hint" class="govuk-hint">
34
- For example, 17/5/2024.
28
+ const kebabize = (str) => {
29
+ return str.replace(
30
+ /[A-Z]+(?![a-z])|[A-Z]/g,
31
+ ($, ofset) => (ofset ? "-" : "") + $.toLowerCase(),
32
+ );
33
+ };
34
+
35
+ const configToDataAttributes = (config) => {
36
+ let attributes = "";
37
+ for (let [key, value] of Object.entries(config)) {
38
+ attributes += `data-${kebabize(key)}="${value}" `;
39
+ }
40
+ return attributes;
41
+ };
42
+
43
+ const createComponent = (config = {}, html) => {
44
+ const dataAttributes = configToDataAttributes(config);
45
+ if (typeof html === "undefined") {
46
+ html = `
47
+ <div class="moj-datepicker" data-module="moj-date-picker" ${dataAttributes}>
48
+ <div class="govuk-form-group">
49
+ <label class="govuk-label" for="date">
50
+ Date
51
+ </label>
52
+ <div id="date-hint" class="govuk-hint">
53
+ For example, 17/5/2024.
54
+ </div>
55
+ <input class="govuk-input moj-js-datepicker-input " id="date" name="date" type="text" aria-describedby="date-hint" autocomplete="off">
35
56
  </div>
36
- <input class="govuk-input moj-js-datepicker-input " id="date" name="date" type="text" aria-describedby="date-hint" autocomplete="off">
37
- </div>
38
- </div>`;
57
+ </div>`;
58
+ }
39
59
  document.body.insertAdjacentHTML("afterbegin", html);
40
60
 
41
61
  component = document.querySelector('[data-module="moj-date-picker"]');
@@ -78,6 +98,32 @@ const getLastDayOfWeek = (dateObject, lastDayOfWeekIndex) => {
78
98
  return lastDayOfWeek;
79
99
  };
80
100
 
101
+ const getDateInCurrentMonth = (excluding = []) => {
102
+ const today = dayjs().date();
103
+ excluding.push(today);
104
+ const lastDayOfMonth = dayjs().endOf("month").date();
105
+ const days = range(1, lastDayOfMonth).filter((x) => !excluding.includes(x));
106
+
107
+ return days[Math.floor(Math.random() * days.length)];
108
+ };
109
+
110
+ const getDateRangeInCurrentMonth = (startDay, endDay) => {
111
+ let date = dayjs().date(startDay); // Convert the start date to a Day.js object
112
+ const endDate = dayjs().date(endDay + 1);
113
+ const dates = [];
114
+
115
+ while (date.isBefore(endDate)) {
116
+ dates.push(date);
117
+ date = date.add(1, "day");
118
+ }
119
+
120
+ return dates;
121
+ };
122
+
123
+ const range = (start, end) => {
124
+ return [...Array(end - start + 1).keys()].map((x) => x + start);
125
+ };
126
+
81
127
  describe("Date picker with defaults", () => {
82
128
  let component;
83
129
  let calendarButton;
@@ -153,18 +199,13 @@ describe("Date picker with defaults", () => {
153
199
  });
154
200
 
155
201
  test("can navigate back in time", async () => {
156
- const today = new Date();
157
- const currentMonthName = today.toLocaleString("default", { month: "long" });
158
- const currentYear = today.getFullYear();
159
- const currentMonth = today.getMonth();
160
- const previousMonthName = new Date(
161
- today.setMonth(currentMonth - 1),
162
- ).toLocaleString("default", { month: "long" });
163
- const previousYear = currentYear - 1;
202
+ const today = dayjs();
203
+ const previousMonth = dayjs().subtract(1, 'month')
204
+ const previousYear = previousMonth.subtract(1, 'year')
164
205
 
165
- const currentTitle = `${currentMonthName} ${currentYear}`;
166
- const previousMonthTitle = `${previousMonthName} ${currentYear}`;
167
- const previousYearTitle = `${previousMonthName} ${previousYear}`;
206
+ const currentTitle = `${today.format('MMMM YYYY')}`;
207
+ const previousMonthTitle = `${previousMonth.format('MMMM YYYY')}`;
208
+ const previousYearTitle = `${previousYear.format('MMMM YYYY')}`;
168
209
 
169
210
  await user.click(calendarButton);
170
211
  let prevMonthButton = getByText(dialog, "Previous month");
@@ -178,18 +219,13 @@ describe("Date picker with defaults", () => {
178
219
  });
179
220
 
180
221
  test("can navigate forward in time", async () => {
181
- const today = new Date();
182
- const currentMonthName = today.toLocaleString("default", { month: "long" });
183
- const currentYear = today.getFullYear();
184
- const currentMonth = today.getMonth();
185
- const nextMonthName = new Date(
186
- today.setMonth(currentMonth + 1),
187
- ).toLocaleString("default", { month: "long" });
188
- const nextYear = currentYear + 1;
222
+ const today = dayjs();
223
+ const nextMonth = dayjs().add(1, 'month')
224
+ const nextYear = nextMonth.add(1, 'year')
189
225
 
190
- const currentTitle = `${currentMonthName} ${currentYear}`;
191
- const nextMonthTitle = `${nextMonthName} ${currentYear}`;
192
- const nextYearTitle = `${nextMonthName} ${nextYear}`;
226
+ const currentTitle = `${today.format('MMMM YYYY')}`;
227
+ const nextMonthTitle = `${nextMonth.format('MMMM YYYY')}`;
228
+ const nextYearTitle = `${nextYear.format('MMMM YYYY')}`;
193
229
 
194
230
  await user.click(calendarButton);
195
231
  let nextMonthButton = getByText(dialog, "Next month");
@@ -559,13 +595,541 @@ describe("Date picker with defaults", () => {
559
595
  expect(await axe(document.body)).toHaveNoViolations();
560
596
  });
561
597
  });
598
+ });
599
+
600
+ describe("button menu JS API", () => {
601
+ let component;
602
+
603
+ beforeEach(() => {
604
+ component = createComponent();
605
+ });
606
+
607
+ afterEach(() => {
608
+ document.body.innerHTML = "";
609
+ });
610
+
611
+ describe("config", () => {
612
+ test("default config values", () => {
613
+ const datePicker = new MOJFrontend.DatePicker(component, {});
614
+ datePicker.init();
615
+
616
+ expect(datePicker.config).toStrictEqual({
617
+ leadingZeros: false,
618
+ weekStartDay: "monday",
619
+ });
620
+ });
621
+
622
+ test("leadingZeros", () => {
623
+ const config = { leadingZeros: true };
624
+ const datePicker = new MOJFrontend.DatePicker(component, config);
625
+ datePicker.init();
626
+
627
+ expect(datePicker.config.leadingZeros).toBe(true);
628
+ });
629
+
630
+ test("weekStartDay can be set to sunday", () => {
631
+ const config = { weekStartDay: "Sunday" };
632
+ const datePicker = new MOJFrontend.DatePicker(component, config);
633
+ datePicker.init();
634
+
635
+ expect(datePicker.config.weekStartDay).toBe("sunday");
636
+ expect(datePicker.dayLabels[0]).toBe("Sunday");
637
+ });
638
+
639
+ test("weekStartDay can't be set to other days", () => {
640
+ const config = { weekStartDay: "friday" };
641
+ const datePicker = new MOJFrontend.DatePicker(component, config);
642
+ datePicker.init();
643
+
644
+ expect(datePicker.config.weekStartDay).toBe("monday");
645
+ });
646
+
647
+ test("minDate", () => {
648
+ const minDate = dayjs().subtract("1", "week").startOf("day");
649
+ const config = { minDate: minDate.format("D/M/YYYY") };
650
+ const datePicker = new MOJFrontend.DatePicker(component, config);
651
+ datePicker.init();
652
+
653
+ expect(datePicker.minDate).toStrictEqual(minDate.toDate());
654
+ });
655
+
656
+ test("future minDate sets currentDate to minDate", () => {
657
+ const minDate = dayjs().add("1", "week").startOf("day");
658
+ const config = { minDate: minDate.format("D/M/YYYY") };
659
+ const datePicker = new MOJFrontend.DatePicker(component, config);
660
+ datePicker.init();
562
661
 
563
- //test component API - JS and data-attribute
564
- //open with date in input
565
- //min date
566
- //max date
567
- //excluded dates
568
- //excluded days
569
- //leadingZeros - test home and end keys
570
- //weekStartDay
662
+ expect(datePicker.minDate).toStrictEqual(minDate.toDate());
663
+ expect(datePicker.currentDate).toStrictEqual(minDate.toDate());
664
+ });
665
+
666
+ test("maxDate", () => {
667
+ const maxDate = dayjs().add("1", "week").startOf("day");
668
+ const config = { maxDate: maxDate.format("D/M/YYYY") };
669
+ const datePicker = new MOJFrontend.DatePicker(component, config);
670
+ datePicker.init();
671
+
672
+ expect(datePicker.maxDate).toStrictEqual(maxDate.toDate());
673
+ });
674
+
675
+ test("past maxDate sets currentDate to maxDate", () => {
676
+ const maxDate = dayjs().subtract("1", "week").startOf("day");
677
+ const config = { maxDate: maxDate.format("D/M/YYYY") };
678
+ const datePicker = new MOJFrontend.DatePicker(component, config);
679
+ datePicker.init();
680
+
681
+ expect(datePicker.maxDate).toStrictEqual(maxDate.toDate());
682
+ expect(datePicker.currentDate).toStrictEqual(maxDate.toDate());
683
+ });
684
+
685
+ test("excludedDays", () => {
686
+ const config = { excludedDays: "sunday thursday" };
687
+ const datePicker = new MOJFrontend.DatePicker(component, config);
688
+ datePicker.init();
689
+
690
+ expect(datePicker.excludedDays).toEqual([0, 4]);
691
+ });
692
+
693
+ describe("excludedDates", () => {
694
+ test("excluding a day", () => {
695
+ const dateToExclude = dayjs()
696
+ .date(getDateInCurrentMonth())
697
+ .startOf("day");
698
+ config = { excludedDates: dateToExclude.format("D/M/YYYY") };
699
+ const datePicker = new MOJFrontend.DatePicker(component, config);
700
+ datePicker.init();
701
+
702
+ expect(datePicker.excludedDates).toStrictEqual([
703
+ dateToExclude.toDate(),
704
+ ]);
705
+ });
706
+
707
+ test("excluding multiple dates", () => {
708
+ const firstDateToExclude = dayjs()
709
+ .date(getDateInCurrentMonth())
710
+ .startOf("day");
711
+ const secondDateToExclude = dayjs()
712
+ .date(getDateInCurrentMonth([firstDateToExclude.date()]))
713
+ .startOf("day");
714
+ config = {
715
+ excludedDates: `${firstDateToExclude.format("D/M/YYYY")} ${secondDateToExclude.format("D/M/YYYY")}`,
716
+ };
717
+ const datePicker = new MOJFrontend.DatePicker(component, config);
718
+ datePicker.init();
719
+
720
+ expect(datePicker.excludedDates.length).toEqual(2);
721
+ expect(datePicker.excludedDates).toStrictEqual([
722
+ firstDateToExclude.toDate(),
723
+ secondDateToExclude.toDate(),
724
+ ]);
725
+ });
726
+
727
+ test("excluding a range of days", () => {
728
+ let datesToExclude;
729
+ if (dayjs().date() < 15) {
730
+ datesToExclude = getDateRangeInCurrentMonth(18, 20);
731
+ } else {
732
+ datesToExclude = getDateRangeInCurrentMonth(3, 5);
733
+ }
734
+ datesToExclude = datesToExclude.map((date) => date.startOf("day"));
735
+ config = {
736
+ excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
737
+ };
738
+ const datePicker = new MOJFrontend.DatePicker(component, config);
739
+ datePicker.init();
740
+
741
+ expect(datePicker.excludedDates.length).toEqual(3);
742
+ expect(datePicker.excludedDates).toStrictEqual(
743
+ datesToExclude.map((date) => date.toDate()),
744
+ );
745
+ });
746
+
747
+ test("excluding individual dates and a range of days", () => {
748
+ let datesToExclude;
749
+ if (dayjs().date() < 15) {
750
+ datesToExclude = getDateRangeInCurrentMonth(18, 20);
751
+ datesToExclude.push(dayjs().date(22));
752
+ datesToExclude.push(dayjs().date(25));
753
+ } else {
754
+ datesToExclude = getDateRangeInCurrentMonth(3, 5);
755
+ datesToExclude.push(dayjs().date(7));
756
+ datesToExclude.push(dayjs().date(11));
757
+ }
758
+ datesToExclude = datesToExclude.map((date) => date.startOf("day"));
759
+ config = {
760
+ excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[2].format("D/M/YYYY")} ${datesToExclude[3].format("D/M/YYYY")} ${datesToExclude[4].format("D/M/YYYY")} `,
761
+ };
762
+ const datePicker = new MOJFrontend.DatePicker(component, config);
763
+ datePicker.init();
764
+
765
+ expect(datePicker.excludedDates.length).toEqual(5);
766
+ expect(datePicker.excludedDates).toStrictEqual(
767
+ datesToExclude.map((date) => date.toDate()),
768
+ );
769
+ });
770
+ });
771
+ });
772
+
773
+ describe("UI", () => {
774
+ let calendarButton;
775
+ let input;
776
+
777
+ test("with leadingZeros false", async () => {
778
+ input = screen.getByLabelText("Date");
779
+
780
+ const config = { leadingZeros: false };
781
+ new MOJFrontend.DatePicker(component, config).init();
782
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
783
+ const dateToSelect = screen.queryByText("9")?.closest("button");
784
+ const selectedDate = dayjs().date(9);
785
+
786
+ await user.click(calendarButton);
787
+ await user.click(dateToSelect);
788
+
789
+ expect(input).toHaveValue(selectedDate.format("D/M/YYYY"));
790
+ });
791
+
792
+ test("with leadingZeros true", async () => {
793
+ input = screen.getByLabelText("Date");
794
+
795
+ const config = { leadingZeros: true };
796
+ new MOJFrontend.DatePicker(component, config).init();
797
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
798
+ const dateToSelect = screen.queryByText("9")?.closest("button");
799
+ const selectedDate = dayjs().date(9);
800
+
801
+ await user.click(calendarButton);
802
+ await user.click(dateToSelect);
803
+
804
+ expect(input).toHaveValue(selectedDate.format("DD/MM/YYYY"));
805
+ });
806
+
807
+ test.skip.failing("minDate", async () => {
808
+ const minDay = 3;
809
+ const lastDayinMonth = dayjs().endOf("month").date();
810
+ const minDate = dayjs().date(minDay);
811
+ const config = { minDate: minDate.format("DD/MM/YYYY") };
812
+
813
+ new MOJFrontend.DatePicker(component, config).init();
814
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
815
+ await user.click(calendarButton);
816
+
817
+ for (let i = 1; i <= lastDayinMonth; i++) {
818
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
819
+ const dayButton = screen.getByTestId(testId);
820
+
821
+ if (i <= minDay) {
822
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
823
+ } else {
824
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
825
+ }
826
+ }
827
+ });
828
+
829
+ test("maxDate", async () => {
830
+ const maxDay = 21;
831
+ const lastDayinMonth = dayjs().endOf("month").date();
832
+ const maxDate = dayjs().date(maxDay);
833
+ const config = { maxDate: maxDate.format("DD/MM/YYYY") };
834
+
835
+ new MOJFrontend.DatePicker(component, config).init();
836
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
837
+ await user.click(calendarButton);
838
+
839
+ for (let i = 1; i <= lastDayinMonth; i++) {
840
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
841
+ const dayButton = screen.getByTestId(testId);
842
+
843
+ if (i > maxDay) {
844
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
845
+ } else {
846
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
847
+ }
848
+ }
849
+ });
850
+
851
+ describe("excludedDates", () => {
852
+ test("excluding a day", async () => {
853
+ const dateToExclude = dayjs()
854
+ .date(getDateInCurrentMonth())
855
+ .startOf("day");
856
+ const excludedDay = dateToExclude.date();
857
+ const config = { excludedDates: dateToExclude.format("D/M/YYYY") };
858
+
859
+ const lastDayinMonth = dayjs().endOf("month").date();
860
+
861
+ new MOJFrontend.DatePicker(component, config).init();
862
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
863
+ await user.click(calendarButton);
864
+
865
+ for (let i = 1; i <= lastDayinMonth; i++) {
866
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
867
+ const dayButton = screen.getByTestId(testId);
868
+
869
+ if (i == excludedDay) {
870
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
871
+ } else {
872
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
873
+ }
874
+ }
875
+ });
876
+
877
+ test("excluding a range of days", async () => {
878
+ let datesToExclude;
879
+ if (dayjs().date() < 15) {
880
+ datesToExclude = getDateRangeInCurrentMonth(18, 20);
881
+ } else {
882
+ datesToExclude = getDateRangeInCurrentMonth(3, 5);
883
+ }
884
+ datesToExclude = datesToExclude.map((date) => date.startOf("day"));
885
+ let daysToExclude = datesToExclude.map((date) => date.date());
886
+ const lastDayinMonth = dayjs().endOf("month").date();
887
+ config = {
888
+ excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
889
+ };
890
+
891
+ datePicker = new MOJFrontend.DatePicker(component, config).init();
892
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
893
+ await user.click(calendarButton);
894
+
895
+ for (let i = 1; i <= lastDayinMonth; i++) {
896
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
897
+ const dayButton = screen.getByTestId(testId);
898
+
899
+ if (daysToExclude.includes(i)) {
900
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
901
+ } else {
902
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
903
+ }
904
+ }
905
+ });
906
+ });
907
+
908
+ test("excludedDays", async () => {
909
+ const config = { excludedDays: "sunday" };
910
+ const lastDayinMonth = dayjs().endOf("month").date();
911
+ let excludedDays = [];
912
+ for (let i = 1; i <= lastDayinMonth; i++) {
913
+ if (dayjs().date(i).day() === 0) {
914
+ excludedDays.push(i);
915
+ }
916
+ }
917
+ new MOJFrontend.DatePicker(component, config).init();
918
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
919
+ await user.click(calendarButton);
920
+
921
+ for (let i = 1; i <= lastDayinMonth; i++) {
922
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
923
+ const dayButton = screen.getByTestId(testId);
924
+
925
+ if (excludedDays.includes(i)) {
926
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
927
+ } else {
928
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
929
+ }
930
+ }
931
+ });
932
+
933
+ test("default weekStartDay", async () => {
934
+ new MOJFrontend.DatePicker(component, {}).init();
935
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
936
+ await user.click(calendarButton);
937
+ const headers = getAllByRole(component, "columnheader");
938
+
939
+ expect(headers[0]).toHaveAccessibleName("Monday");
940
+ });
941
+
942
+ test("weekStartDay Sunday", async () => {
943
+ new MOJFrontend.DatePicker(component, { weekStartDay: "sunday" }).init();
944
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
945
+ await user.click(calendarButton);
946
+ const headers = getAllByRole(component, "columnheader");
947
+
948
+ expect(headers[0]).toHaveAccessibleName("Sunday");
949
+ });
950
+ });
951
+ });
952
+
953
+ describe("Datepicker data-attributes API", () => {
954
+ let component;
955
+ let calendarButton;
956
+ let input;
957
+
958
+ beforeEach(() => {});
959
+
960
+ afterEach(() => {
961
+ document.body.innerHTML = "";
962
+ });
963
+
964
+ test("with leadingZeros false", async () => {
965
+ component = createComponent({ leadingZeros: "false" });
966
+ new MOJFrontend.DatePicker(component).init();
967
+
968
+ input = screen.getByLabelText("Date");
969
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
970
+ const dateToSelect = screen.queryByText("9")?.closest("button");
971
+ const selectedDate = dayjs().date(9);
972
+
973
+ await user.click(calendarButton);
974
+ await user.click(dateToSelect);
975
+
976
+ expect(input).toHaveValue(selectedDate.format("D/M/YYYY"));
977
+ });
978
+
979
+ test("with leadingZeros true", async () => {
980
+ const component = createComponent({ leadingZeros: "true" });
981
+ new MOJFrontend.DatePicker(component).init();
982
+
983
+ input = screen.getByLabelText("Date");
984
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
985
+ const dateToSelect = screen.queryByText("9")?.closest("button");
986
+ const selectedDate = dayjs().date(9);
987
+
988
+ await user.click(calendarButton);
989
+ await user.click(dateToSelect);
990
+
991
+ expect(input).toHaveValue(selectedDate.format("DD/MM/YYYY"));
992
+ });
993
+
994
+ test.skip.failing("minDate", async () => {
995
+ const minDay = 3;
996
+ const lastDayinMonth = dayjs().endOf("month").date();
997
+ const minDate = dayjs().date(minDay);
998
+ const component = createComponent({
999
+ minDate: minDate.format("DD/MM/YYYY"),
1000
+ });
1001
+ new MOJFrontend.DatePicker(component).init();
1002
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1003
+
1004
+ await user.click(calendarButton);
1005
+
1006
+ for (let i = 1; i <= lastDayinMonth; i++) {
1007
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1008
+ const dayButton = screen.getByTestId(testId);
1009
+
1010
+ if (i <= minDay) {
1011
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
1012
+ } else {
1013
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
1014
+ }
1015
+ }
1016
+ });
1017
+
1018
+ test("maxDate", async () => {
1019
+ const maxDay = 21;
1020
+ const lastDayinMonth = dayjs().endOf("month").date();
1021
+ const maxDate = dayjs().date(maxDay);
1022
+ const component = createComponent({
1023
+ maxDate: maxDate.format("DD/MM/YYYY"),
1024
+ });
1025
+ new MOJFrontend.DatePicker(component).init();
1026
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1027
+
1028
+ await user.click(calendarButton);
1029
+
1030
+ for (let i = 1; i <= lastDayinMonth; i++) {
1031
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1032
+ const dayButton = screen.getByTestId(testId);
1033
+
1034
+ if (i > maxDay) {
1035
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
1036
+ } else {
1037
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
1038
+ }
1039
+ }
1040
+ });
1041
+
1042
+ describe("excludedDates", () => {
1043
+ test("excluding a day", async () => {
1044
+ const dateToExclude = dayjs()
1045
+ .date(getDateInCurrentMonth())
1046
+ .startOf("day");
1047
+ const excludedDay = dateToExclude.date();
1048
+ const lastDayinMonth = dayjs().endOf("month").date();
1049
+ const component = createComponent({
1050
+ excludedDates: dateToExclude.format("D/M/YYYY"),
1051
+ });
1052
+ new MOJFrontend.DatePicker(component).init();
1053
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1054
+
1055
+ await user.click(calendarButton);
1056
+
1057
+ for (let i = 1; i <= lastDayinMonth; i++) {
1058
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1059
+ const dayButton = screen.getByTestId(testId);
1060
+
1061
+ if (i == excludedDay) {
1062
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
1063
+ } else {
1064
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
1065
+ }
1066
+ }
1067
+ });
1068
+
1069
+ test("excluding a range of days", async () => {
1070
+ let datesToExclude;
1071
+ if (dayjs().date() < 15) {
1072
+ datesToExclude = getDateRangeInCurrentMonth(18, 20);
1073
+ } else {
1074
+ datesToExclude = getDateRangeInCurrentMonth(3, 5);
1075
+ }
1076
+ datesToExclude = datesToExclude.map((date) => date.startOf("day"));
1077
+ let daysToExclude = datesToExclude.map((date) => date.date());
1078
+ const lastDayinMonth = dayjs().endOf("month").date();
1079
+ component = createComponent({
1080
+ excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
1081
+ });
1082
+ datePicker = new MOJFrontend.DatePicker(component).init();
1083
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1084
+
1085
+ await user.click(calendarButton);
1086
+
1087
+ for (let i = 1; i <= lastDayinMonth; i++) {
1088
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1089
+ const dayButton = screen.getByTestId(testId);
1090
+
1091
+ if (daysToExclude.includes(i)) {
1092
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
1093
+ } else {
1094
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
1095
+ }
1096
+ }
1097
+ });
1098
+ });
1099
+
1100
+ test("excludedDays", async () => {
1101
+ const component = createComponent({ excludedDays: "sunday" });
1102
+ const lastDayinMonth = dayjs().endOf("month").date();
1103
+ let excludedDays = [];
1104
+ for (let i = 1; i <= lastDayinMonth; i++) {
1105
+ if (dayjs().date(i).day() === 0) {
1106
+ excludedDays.push(i);
1107
+ }
1108
+ }
1109
+ new MOJFrontend.DatePicker(component).init();
1110
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1111
+
1112
+ await user.click(calendarButton);
1113
+
1114
+ for (let i = 1; i <= lastDayinMonth; i++) {
1115
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1116
+ const dayButton = screen.getByTestId(testId);
1117
+
1118
+ if (excludedDays.includes(i)) {
1119
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
1120
+ } else {
1121
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
1122
+ }
1123
+ }
1124
+ });
1125
+
1126
+ test("weekStartDay", async () => {
1127
+ component = createComponent({ weekStartDay: "sunday" });
1128
+ new MOJFrontend.DatePicker(component).init();
1129
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1130
+ await user.click(calendarButton);
1131
+ const headers = getAllByRole(component, "columnheader");
1132
+
1133
+ expect(headers[0]).toHaveAccessibleName("Sunday");
1134
+ });
571
1135
  });