@ministryofjustice/frontend 3.0.2 → 3.1.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;
@@ -559,13 +605,541 @@ describe("Date picker with defaults", () => {
559
605
  expect(await axe(document.body)).toHaveNoViolations();
560
606
  });
561
607
  });
608
+ });
609
+
610
+ describe("button menu JS API", () => {
611
+ let component;
612
+
613
+ beforeEach(() => {
614
+ component = createComponent();
615
+ });
616
+
617
+ afterEach(() => {
618
+ document.body.innerHTML = "";
619
+ });
620
+
621
+ describe("config", () => {
622
+ test("default config values", () => {
623
+ const datePicker = new MOJFrontend.DatePicker(component, {});
624
+ datePicker.init();
625
+
626
+ expect(datePicker.config).toStrictEqual({
627
+ leadingZeros: false,
628
+ weekStartDay: "monday",
629
+ });
630
+ });
631
+
632
+ test("leadingZeros", () => {
633
+ const config = { leadingZeros: true };
634
+ const datePicker = new MOJFrontend.DatePicker(component, config);
635
+ datePicker.init();
636
+
637
+ expect(datePicker.config.leadingZeros).toBe(true);
638
+ });
639
+
640
+ test("weekStartDay can be set to sunday", () => {
641
+ const config = { weekStartDay: "Sunday" };
642
+ const datePicker = new MOJFrontend.DatePicker(component, config);
643
+ datePicker.init();
644
+
645
+ expect(datePicker.config.weekStartDay).toBe("sunday");
646
+ expect(datePicker.dayLabels[0]).toBe("Sunday");
647
+ });
648
+
649
+ test("weekStartDay can't be set to other days", () => {
650
+ const config = { weekStartDay: "friday" };
651
+ const datePicker = new MOJFrontend.DatePicker(component, config);
652
+ datePicker.init();
653
+
654
+ expect(datePicker.config.weekStartDay).toBe("monday");
655
+ });
656
+
657
+ test("minDate", () => {
658
+ const minDate = dayjs().subtract("1", "week").startOf("day");
659
+ const config = { minDate: minDate.format("D/M/YYYY") };
660
+ const datePicker = new MOJFrontend.DatePicker(component, config);
661
+ datePicker.init();
662
+
663
+ expect(datePicker.minDate).toStrictEqual(minDate.toDate());
664
+ });
665
+
666
+ test("future minDate sets currentDate to minDate", () => {
667
+ const minDate = dayjs().add("1", "week").startOf("day");
668
+ const config = { minDate: minDate.format("D/M/YYYY") };
669
+ const datePicker = new MOJFrontend.DatePicker(component, config);
670
+ datePicker.init();
671
+
672
+ expect(datePicker.minDate).toStrictEqual(minDate.toDate());
673
+ expect(datePicker.currentDate).toStrictEqual(minDate.toDate());
674
+ });
675
+
676
+ test("maxDate", () => {
677
+ const maxDate = dayjs().add("1", "week").startOf("day");
678
+ const config = { maxDate: maxDate.format("D/M/YYYY") };
679
+ const datePicker = new MOJFrontend.DatePicker(component, config);
680
+ datePicker.init();
681
+
682
+ expect(datePicker.maxDate).toStrictEqual(maxDate.toDate());
683
+ });
684
+
685
+ test("past maxDate sets currentDate to maxDate", () => {
686
+ const maxDate = dayjs().subtract("1", "week").startOf("day");
687
+ const config = { maxDate: maxDate.format("D/M/YYYY") };
688
+ const datePicker = new MOJFrontend.DatePicker(component, config);
689
+ datePicker.init();
690
+
691
+ expect(datePicker.maxDate).toStrictEqual(maxDate.toDate());
692
+ expect(datePicker.currentDate).toStrictEqual(maxDate.toDate());
693
+ });
694
+
695
+ test("excludedDays", () => {
696
+ const config = { excludedDays: "sunday thursday" };
697
+ const datePicker = new MOJFrontend.DatePicker(component, config);
698
+ datePicker.init();
699
+
700
+ expect(datePicker.excludedDays).toEqual([0, 4]);
701
+ });
702
+
703
+ describe("excludedDates", () => {
704
+ test("excluding a day", () => {
705
+ const dateToExclude = dayjs()
706
+ .date(getDateInCurrentMonth())
707
+ .startOf("day");
708
+ config = { excludedDates: dateToExclude.format("D/M/YYYY") };
709
+ const datePicker = new MOJFrontend.DatePicker(component, config);
710
+ datePicker.init();
711
+
712
+ expect(datePicker.excludedDates).toStrictEqual([
713
+ dateToExclude.toDate(),
714
+ ]);
715
+ });
716
+
717
+ test("excluding multiple dates", () => {
718
+ const firstDateToExclude = dayjs()
719
+ .date(getDateInCurrentMonth())
720
+ .startOf("day");
721
+ const secondDateToExclude = dayjs()
722
+ .date(getDateInCurrentMonth([firstDateToExclude.date()]))
723
+ .startOf("day");
724
+ config = {
725
+ excludedDates: `${firstDateToExclude.format("D/M/YYYY")} ${secondDateToExclude.format("D/M/YYYY")}`,
726
+ };
727
+ const datePicker = new MOJFrontend.DatePicker(component, config);
728
+ datePicker.init();
729
+
730
+ expect(datePicker.excludedDates.length).toEqual(2);
731
+ expect(datePicker.excludedDates).toStrictEqual([
732
+ firstDateToExclude.toDate(),
733
+ secondDateToExclude.toDate(),
734
+ ]);
735
+ });
736
+
737
+ test("excluding a range of days", () => {
738
+ let datesToExclude;
739
+ if (dayjs().date() < 15) {
740
+ datesToExclude = getDateRangeInCurrentMonth(18, 20);
741
+ } else {
742
+ datesToExclude = getDateRangeInCurrentMonth(3, 5);
743
+ }
744
+ datesToExclude = datesToExclude.map((date) => date.startOf("day"));
745
+ config = {
746
+ excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
747
+ };
748
+ const datePicker = new MOJFrontend.DatePicker(component, config);
749
+ datePicker.init();
750
+
751
+ expect(datePicker.excludedDates.length).toEqual(3);
752
+ expect(datePicker.excludedDates).toStrictEqual(
753
+ datesToExclude.map((date) => date.toDate()),
754
+ );
755
+ });
756
+
757
+ test("excluding individual dates and a range of days", () => {
758
+ let datesToExclude;
759
+ if (dayjs().date() < 15) {
760
+ datesToExclude = getDateRangeInCurrentMonth(18, 20);
761
+ datesToExclude.push(dayjs().date(22));
762
+ datesToExclude.push(dayjs().date(25));
763
+ } else {
764
+ datesToExclude = getDateRangeInCurrentMonth(3, 5);
765
+ datesToExclude.push(dayjs().date(7));
766
+ datesToExclude.push(dayjs().date(11));
767
+ }
768
+ datesToExclude = datesToExclude.map((date) => date.startOf("day"));
769
+ config = {
770
+ 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")} `,
771
+ };
772
+ const datePicker = new MOJFrontend.DatePicker(component, config);
773
+ datePicker.init();
774
+
775
+ expect(datePicker.excludedDates.length).toEqual(5);
776
+ expect(datePicker.excludedDates).toStrictEqual(
777
+ datesToExclude.map((date) => date.toDate()),
778
+ );
779
+ });
780
+ });
781
+ });
562
782
 
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
783
+ describe("UI", () => {
784
+ let calendarButton;
785
+ let input;
786
+
787
+ test("with leadingZeros false", async () => {
788
+ input = screen.getByLabelText("Date");
789
+
790
+ const config = { leadingZeros: false };
791
+ new MOJFrontend.DatePicker(component, config).init();
792
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
793
+ const dateToSelect = screen.queryByText("9")?.closest("button");
794
+ const selectedDate = dayjs().date(9);
795
+
796
+ await user.click(calendarButton);
797
+ await user.click(dateToSelect);
798
+
799
+ expect(input).toHaveValue(selectedDate.format("D/M/YYYY"));
800
+ });
801
+
802
+ test("with leadingZeros true", async () => {
803
+ input = screen.getByLabelText("Date");
804
+
805
+ const config = { leadingZeros: true };
806
+ new MOJFrontend.DatePicker(component, config).init();
807
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
808
+ const dateToSelect = screen.queryByText("9")?.closest("button");
809
+ const selectedDate = dayjs().date(9);
810
+
811
+ await user.click(calendarButton);
812
+ await user.click(dateToSelect);
813
+
814
+ expect(input).toHaveValue(selectedDate.format("DD/MM/YYYY"));
815
+ });
816
+
817
+ test.skip.failing("minDate", async () => {
818
+ const minDay = 3;
819
+ const lastDayinMonth = dayjs().endOf("month").date();
820
+ const minDate = dayjs().date(minDay);
821
+ const config = { minDate: minDate.format("DD/MM/YYYY") };
822
+
823
+ new MOJFrontend.DatePicker(component, config).init();
824
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
825
+ await user.click(calendarButton);
826
+
827
+ for (let i = 1; i <= lastDayinMonth; i++) {
828
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
829
+ const dayButton = screen.getByTestId(testId);
830
+
831
+ if (i <= minDay) {
832
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
833
+ } else {
834
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
835
+ }
836
+ }
837
+ });
838
+
839
+ test("maxDate", async () => {
840
+ const maxDay = 21;
841
+ const lastDayinMonth = dayjs().endOf("month").date();
842
+ const maxDate = dayjs().date(maxDay);
843
+ const config = { maxDate: maxDate.format("DD/MM/YYYY") };
844
+
845
+ new MOJFrontend.DatePicker(component, config).init();
846
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
847
+ await user.click(calendarButton);
848
+
849
+ for (let i = 1; i <= lastDayinMonth; i++) {
850
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
851
+ const dayButton = screen.getByTestId(testId);
852
+
853
+ if (i > maxDay) {
854
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
855
+ } else {
856
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
857
+ }
858
+ }
859
+ });
860
+
861
+ describe("excludedDates", () => {
862
+ test("excluding a day", async () => {
863
+ const dateToExclude = dayjs()
864
+ .date(getDateInCurrentMonth())
865
+ .startOf("day");
866
+ const excludedDay = dateToExclude.date();
867
+ const config = { excludedDates: dateToExclude.format("D/M/YYYY") };
868
+
869
+ const lastDayinMonth = dayjs().endOf("month").date();
870
+
871
+ new MOJFrontend.DatePicker(component, config).init();
872
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
873
+ await user.click(calendarButton);
874
+
875
+ for (let i = 1; i <= lastDayinMonth; i++) {
876
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
877
+ const dayButton = screen.getByTestId(testId);
878
+
879
+ if (i == excludedDay) {
880
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
881
+ } else {
882
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
883
+ }
884
+ }
885
+ });
886
+
887
+ test("excluding a range of days", async () => {
888
+ let datesToExclude;
889
+ if (dayjs().date() < 15) {
890
+ datesToExclude = getDateRangeInCurrentMonth(18, 20);
891
+ } else {
892
+ datesToExclude = getDateRangeInCurrentMonth(3, 5);
893
+ }
894
+ datesToExclude = datesToExclude.map((date) => date.startOf("day"));
895
+ let daysToExclude = datesToExclude.map((date) => date.date());
896
+ const lastDayinMonth = dayjs().endOf("month").date();
897
+ config = {
898
+ excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
899
+ };
900
+
901
+ datePicker = new MOJFrontend.DatePicker(component, config).init();
902
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
903
+ await user.click(calendarButton);
904
+
905
+ for (let i = 1; i <= lastDayinMonth; i++) {
906
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
907
+ const dayButton = screen.getByTestId(testId);
908
+
909
+ if (daysToExclude.includes(i)) {
910
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
911
+ } else {
912
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
913
+ }
914
+ }
915
+ });
916
+ });
917
+
918
+ test("excludedDays", async () => {
919
+ const config = { excludedDays: "sunday" };
920
+ const lastDayinMonth = dayjs().endOf("month").date();
921
+ let excludedDays = [];
922
+ for (let i = 1; i <= lastDayinMonth; i++) {
923
+ if (dayjs().date(i).day() === 0) {
924
+ excludedDays.push(i);
925
+ }
926
+ }
927
+ new MOJFrontend.DatePicker(component, config).init();
928
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
929
+ await user.click(calendarButton);
930
+
931
+ for (let i = 1; i <= lastDayinMonth; i++) {
932
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
933
+ const dayButton = screen.getByTestId(testId);
934
+
935
+ if (excludedDays.includes(i)) {
936
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
937
+ } else {
938
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
939
+ }
940
+ }
941
+ });
942
+
943
+ test("default weekStartDay", async () => {
944
+ new MOJFrontend.DatePicker(component, {}).init();
945
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
946
+ await user.click(calendarButton);
947
+ const headers = getAllByRole(component, "columnheader");
948
+
949
+ expect(headers[0]).toHaveAccessibleName("Monday");
950
+ });
951
+
952
+ test("weekStartDay Sunday", async () => {
953
+ new MOJFrontend.DatePicker(component, { weekStartDay: "sunday" }).init();
954
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
955
+ await user.click(calendarButton);
956
+ const headers = getAllByRole(component, "columnheader");
957
+
958
+ expect(headers[0]).toHaveAccessibleName("Sunday");
959
+ });
960
+ });
961
+ });
962
+
963
+ describe("Datepicker data-attributes API", () => {
964
+ let component;
965
+ let calendarButton;
966
+ let input;
967
+
968
+ beforeEach(() => {});
969
+
970
+ afterEach(() => {
971
+ document.body.innerHTML = "";
972
+ });
973
+
974
+ test("with leadingZeros false", async () => {
975
+ component = createComponent({ leadingZeros: "false" });
976
+ new MOJFrontend.DatePicker(component).init();
977
+
978
+ input = screen.getByLabelText("Date");
979
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
980
+ const dateToSelect = screen.queryByText("9")?.closest("button");
981
+ const selectedDate = dayjs().date(9);
982
+
983
+ await user.click(calendarButton);
984
+ await user.click(dateToSelect);
985
+
986
+ expect(input).toHaveValue(selectedDate.format("D/M/YYYY"));
987
+ });
988
+
989
+ test("with leadingZeros true", async () => {
990
+ const component = createComponent({ leadingZeros: "true" });
991
+ new MOJFrontend.DatePicker(component).init();
992
+
993
+ input = screen.getByLabelText("Date");
994
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
995
+ const dateToSelect = screen.queryByText("9")?.closest("button");
996
+ const selectedDate = dayjs().date(9);
997
+
998
+ await user.click(calendarButton);
999
+ await user.click(dateToSelect);
1000
+
1001
+ expect(input).toHaveValue(selectedDate.format("DD/MM/YYYY"));
1002
+ });
1003
+
1004
+ test.skip.failing("minDate", async () => {
1005
+ const minDay = 3;
1006
+ const lastDayinMonth = dayjs().endOf("month").date();
1007
+ const minDate = dayjs().date(minDay);
1008
+ const component = createComponent({
1009
+ minDate: minDate.format("DD/MM/YYYY"),
1010
+ });
1011
+ new MOJFrontend.DatePicker(component).init();
1012
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1013
+
1014
+ await user.click(calendarButton);
1015
+
1016
+ for (let i = 1; i <= lastDayinMonth; i++) {
1017
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1018
+ const dayButton = screen.getByTestId(testId);
1019
+
1020
+ if (i <= minDay) {
1021
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
1022
+ } else {
1023
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
1024
+ }
1025
+ }
1026
+ });
1027
+
1028
+ test("maxDate", async () => {
1029
+ const maxDay = 21;
1030
+ const lastDayinMonth = dayjs().endOf("month").date();
1031
+ const maxDate = dayjs().date(maxDay);
1032
+ const component = createComponent({
1033
+ maxDate: maxDate.format("DD/MM/YYYY"),
1034
+ });
1035
+ new MOJFrontend.DatePicker(component).init();
1036
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1037
+
1038
+ await user.click(calendarButton);
1039
+
1040
+ for (let i = 1; i <= lastDayinMonth; i++) {
1041
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1042
+ const dayButton = screen.getByTestId(testId);
1043
+
1044
+ if (i > maxDay) {
1045
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
1046
+ } else {
1047
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
1048
+ }
1049
+ }
1050
+ });
1051
+
1052
+ describe("excludedDates", () => {
1053
+ test("excluding a day", async () => {
1054
+ const dateToExclude = dayjs()
1055
+ .date(getDateInCurrentMonth())
1056
+ .startOf("day");
1057
+ const excludedDay = dateToExclude.date();
1058
+ const lastDayinMonth = dayjs().endOf("month").date();
1059
+ const component = createComponent({
1060
+ excludedDates: dateToExclude.format("D/M/YYYY"),
1061
+ });
1062
+ new MOJFrontend.DatePicker(component).init();
1063
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1064
+
1065
+ await user.click(calendarButton);
1066
+
1067
+ for (let i = 1; i <= lastDayinMonth; i++) {
1068
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1069
+ const dayButton = screen.getByTestId(testId);
1070
+
1071
+ if (i == excludedDay) {
1072
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
1073
+ } else {
1074
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
1075
+ }
1076
+ }
1077
+ });
1078
+
1079
+ test("excluding a range of days", async () => {
1080
+ let datesToExclude;
1081
+ if (dayjs().date() < 15) {
1082
+ datesToExclude = getDateRangeInCurrentMonth(18, 20);
1083
+ } else {
1084
+ datesToExclude = getDateRangeInCurrentMonth(3, 5);
1085
+ }
1086
+ datesToExclude = datesToExclude.map((date) => date.startOf("day"));
1087
+ let daysToExclude = datesToExclude.map((date) => date.date());
1088
+ const lastDayinMonth = dayjs().endOf("month").date();
1089
+ component = createComponent({
1090
+ excludedDates: `${datesToExclude[0].format("D/M/YYYY")}-${datesToExclude[datesToExclude.length-1].format("D/M/YYYY")}`,
1091
+ });
1092
+ datePicker = new MOJFrontend.DatePicker(component).init();
1093
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1094
+
1095
+ await user.click(calendarButton);
1096
+
1097
+ for (let i = 1; i <= lastDayinMonth; i++) {
1098
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1099
+ const dayButton = screen.getByTestId(testId);
1100
+
1101
+ if (daysToExclude.includes(i)) {
1102
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
1103
+ } else {
1104
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
1105
+ }
1106
+ }
1107
+ });
1108
+ });
1109
+
1110
+ test("excludedDays", async () => {
1111
+ const component = createComponent({ excludedDays: "sunday" });
1112
+ const lastDayinMonth = dayjs().endOf("month").date();
1113
+ let excludedDays = [];
1114
+ for (let i = 1; i <= lastDayinMonth; i++) {
1115
+ if (dayjs().date(i).day() === 0) {
1116
+ excludedDays.push(i);
1117
+ }
1118
+ }
1119
+ new MOJFrontend.DatePicker(component).init();
1120
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1121
+
1122
+ await user.click(calendarButton);
1123
+
1124
+ for (let i = 1; i <= lastDayinMonth; i++) {
1125
+ const testId = dayjs().date(i).startOf("day").format("D/M/YYYY");
1126
+ const dayButton = screen.getByTestId(testId);
1127
+
1128
+ if (excludedDays.includes(i)) {
1129
+ expect(dayButton).toHaveAttribute("aria-disabled", "true");
1130
+ } else {
1131
+ expect(dayButton).not.toHaveAttribute("aria-disabled");
1132
+ }
1133
+ }
1134
+ });
1135
+
1136
+ test("weekStartDay", async () => {
1137
+ component = createComponent({ weekStartDay: "sunday" });
1138
+ new MOJFrontend.DatePicker(component).init();
1139
+ calendarButton = screen.getByRole("button", { name: "Choose date" });
1140
+ await user.click(calendarButton);
1141
+ const headers = getAllByRole(component, "columnheader");
1142
+
1143
+ expect(headers[0]).toHaveAccessibleName("Sunday");
1144
+ });
571
1145
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ministryofjustice/frontend",
3
3
  "description": "The MOJ Frontend contains the code you need to start building user interfaces for UK Ministry of Justice government services.",
4
- "version": "3.0.2",
4
+ "version": "3.1.0",
5
5
  "main": "moj/all.js",
6
6
  "sass": "moj/all.scss",
7
7
  "engines": {