Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
fefa23d
Removing Material-ui datepicker
Jialecl Nov 3, 2022
625ff5a
removing material-ui packages + calendar toolbar focus and size
Jialecl Nov 3, 2022
805c425
monthpicker resized
Jialecl Nov 3, 2022
e7d974a
Adding improvements based on feedback
Jialecl Nov 4, 2022
a9be1e8
adding translation + accessibility improvements + code fixes
Jialecl Nov 8, 2022
abbdc9d
changing box-shadow to match other pop ups
Jialecl Nov 9, 2022
6f1801d
improving focus behavior
Jialecl Nov 11, 2022
b774715
adding changes based on feedback
Jialecl Nov 11, 2022
3ac7f47
Merge branch 'master' of https://github.com/dxc-technology/halstack-r…
Jialecl Nov 14, 2022
4e6e1a8
Adding key to month and year list to avoid warning
Jialecl Nov 14, 2022
1fb0838
adding back id
Jialecl Nov 15, 2022
42cebbd
separating date picker in multiple components
Jialecl Nov 16, 2022
00c3bf5
Accessibility improvements and fixing some interactions
Jialecl Nov 18, 2022
51c12fc
Storybook fix
Jialecl Nov 18, 2022
e2aa344
Changing the calendar to always show 6 rows based on new design
Jialecl Nov 30, 2022
b4eb1fb
Adding current day/year behaviour
Jialecl Dec 1, 2022
b96c6e5
Adding calendar header yearpicker trigger
Jialecl Dec 1, 2022
916c54d
Updating tests and month localization
Jialecl Dec 2, 2022
70dd817
applying existing tokens
Jialecl Dec 2, 2022
e1ea4ad
Updating visual tests
Jialecl Dec 9, 2022
6a136ce
Updating date tokens
Jialecl Dec 9, 2022
fb1bf24
Updating specs to match new calendar
Jialecl Dec 12, 2022
5a7b80e
Adding additional tests for other month and year select
Jialecl Dec 12, 2022
2b1ea16
Adding roving tabindex to yearpicker
Jialecl Dec 13, 2022
c5926eb
Adding box-sizing to add the padding to the width
Jialecl Dec 13, 2022
25a21da
Adding changes to focus, selected date and visual changes based on fe…
Jialecl Dec 14, 2022
b4e9d42
fixing build + code improvements based on feddback
Jialecl Dec 14, 2022
5cd056f
adding return
Jialecl Dec 14, 2022
df9dd49
adding ref solution
Jialecl Dec 15, 2022
69c9492
Ordering css + removed letter spacing
Dec 15, 2022
0f88c80
Changing ref solution and adding title to calendar action
Jialecl Dec 15, 2022
354fa61
changing aria values to string
Jialecl Dec 15, 2022
5290021
Ref test fix + onblur in calendar
Jialecl Dec 15, 2022
fadced4
Cahnging ref solution and improving yearpicker scroll
Jialecl Dec 16, 2022
08ed514
reverting documentation changes
Jialecl Dec 16, 2022
b2f4259
Some updates to the Date Picker based on feedback
Dec 22, 2022
895c63b
Reversing token changes
Dec 22, 2022
19f0b41
Merge branch 'master' into jialecl-calendar
GomezIvann Dec 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions lib/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@
"styled-components": "^5.0.1"
},
"dependencies": {
"@date-io/dayjs": "^1.3.9",
"@material-ui/core": "4.11.1",
"@material-ui/icons": "4.4.3",
"@material-ui/lab": "4.0.0-alpha.17",
"@material-ui/pickers": "3.2.2",
"@material-ui/styles": "4.0.2",
"@radix-ui/react-popover": "0.1.6",
"@types/styled-components": "^5.1.24",
"@types/uuid": "^8.3.4",
Expand Down
11 changes: 7 additions & 4 deletions lib/src/common/variables.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,15 +422,15 @@ export const componentTokens = {
pickerHoverDateBackgroundColor: globalTokens.hal_purple_l_90,
pickerSelectedDateColor: globalTokens.hal_white,
pickerSelectedDateBackgroundColor: globalTokens.hal_purple_s_38,
pickerActualDateFontColor: globalTokens.hal_grey_l_60,
pickerActualDateFontColor: globalTokens.hal_black,
pickerYearFontColor: globalTokens.hal_black,
pickerMonthFontColor: globalTokens.hal_black,
pickerWeekFontColor: globalTokens.hal_black,
pickerDayFontColor: globalTokens.hal_black,
pickerMonthArrowsBackgroundColor: globalTokens.transparent,
pickerFocusColor: globalTokens.hal_blue_l_50,
pickerHeight: "316px",
pickerWidth: "290px",
pickerHeight: "316px", // not referenced in the actual implementation
pickerWidth: "292px",
},
dialog: {
overlayColor: globalTokens.hal_black,
Expand Down Expand Up @@ -1505,7 +1505,7 @@ export const defaultTranslatedComponentLabels = {
},
calendar: {
days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"],
daysShort: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"],
months: [
"January",
"February",
Expand All @@ -1521,5 +1521,8 @@ export const defaultTranslatedComponentLabels = {
"December",
],
monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
previousMonthTitle: "Previous month",
nextMonthTitle: "Next month",
openCalendar: "Open calendar",
},
};
281 changes: 281 additions & 0 deletions lib/src/date-input/Calendar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
import dayjs, { Dayjs } from "dayjs";
import React, { useState, useMemo, useEffect } from "react";
import styled from "styled-components";
import { CalendarPropsType } from "./types";
import useTranslatedLabels from "../useTranslatedLabels";
import { DxcFlex } from "../main";

const getDays = (innerDate: Dayjs) => {
const monthDayCells = [];
const lastMonthNumberOfDays = innerDate.set("month", innerDate.get("month") - 1).endOf("month");
const firstDayOfMonth = innerDate.startOf("month").day() === 0 ? 6 : innerDate.startOf("month").day() - 1;
const daysInMonth = firstDayOfMonth + innerDate.daysInMonth();

for (let i = 0; i < 42; i++) {
if (i < firstDayOfMonth) {
monthDayCells.push({
day: lastMonthNumberOfDays.get("date") - firstDayOfMonth + i + 1,
month: innerDate.get("month") ? innerDate.get("month") - 1 : 11,
year: innerDate.set("month", innerDate.get("month") - 1).get("year"),
});
} else if (i < daysInMonth) {
monthDayCells.push({
day: i - firstDayOfMonth + 1,
month: innerDate.get("month"),
year: innerDate.get("year"),
});
} else {
monthDayCells.push({
day: i - daysInMonth + 1,
month: innerDate.get("month") === 11 ? 0 : innerDate.get("month") + 1,
year: innerDate.set("month", innerDate.get("month") + 1).get("year"),
});
}
}
return monthDayCells;
};

const isDaySelected = (date: { day: number; month: number; year: number }, selectedDate) =>
selectedDate?.get("month") === date.month &&
selectedDate?.get("year") === date.year &&
selectedDate?.get("date") === date.day;

const Calendar = ({ selectedDate, innerDate, onInnerDateChange, onDaySelect }: CalendarPropsType): JSX.Element => {
const [dateToFocus, setDateToFocus] = useState(
selectedDate?.get("month") === innerDate.get("month") && selectedDate?.get("year") === innerDate.get("year")
? selectedDate
: dayjs()
);
const [toFocus, setToFocus] = useState(false);
const today = dayjs();
const dayCells = useMemo(() => getDays(innerDate), [innerDate]);
const translatedLabels = useTranslatedLabels();
const weekDays = translatedLabels.calendar.daysShort;

const onDateClickHandler = (date: { day: number; month: number; year: number }) => {
const newDate = innerDate.set("month", date.month).set("date", date.day);
onDaySelect(newDate);
setDateToFocus(newDate);
};

const handleOnBlur = (event) => {
if (!event?.currentTarget.contains(event.relatedTarget)) {
updateDateToFocus();
}
};

const updateDateToFocus = () => {
if (selectedDate?.get("month") === innerDate.get("month") && selectedDate?.get("year") === innerDate.get("year")) {
setDateToFocus(selectedDate);
} else if (today.get("month") === innerDate.get("month") && today.get("year") === innerDate.get("year")) {
setDateToFocus(today);
} else {
setDateToFocus(innerDate.set("date", 1));
}
};

const focusDate = (date: Dayjs) => {
if (innerDate.get("month") !== date.get("month") || innerDate.get("year") !== date.get("year")) {
onInnerDateChange(date);
}
setDateToFocus(date);
setToFocus(true);
};

useEffect(() => {
if (toFocus) {
document.getElementById(`day_${dateToFocus.get("date")}_month${dateToFocus.get("month")}`)?.focus();
setToFocus(false);
}
}, [dateToFocus, toFocus]);

useEffect(() => {
if (dateToFocus.get("month") !== innerDate.get("month") || dateToFocus.get("year") !== innerDate.get("year")) {
updateDateToFocus();
}
}, [innerDate, dateToFocus, selectedDate]);

const handleDayKeyboardEvent = (event, date) => {
let dateToFocusTemp =
date.month === innerDate.get("month")
? innerDate.set("date", date.day)
: innerDate.set("date", date.day).set("month", date.month);

switch (event.key) {
case "PageUp":
event.preventDefault();
event.shiftKey
? (dateToFocusTemp = dateToFocusTemp.set("year", dateToFocusTemp.get("year") - 1))
: (dateToFocusTemp = dateToFocusTemp.set("month", dateToFocusTemp.get("month") - 1));
focusDate(dateToFocusTemp);
break;
case "PageDown":
event.preventDefault();
event.shiftKey
? (dateToFocusTemp = dateToFocusTemp.set("year", dateToFocusTemp.get("year") + 1))
: (dateToFocusTemp = dateToFocusTemp.set("month", dateToFocusTemp.get("month") + 1));
focusDate(dateToFocusTemp);
break;
case "ArrowLeft":
event.preventDefault();
dateToFocusTemp = dateToFocusTemp.set("date", dateToFocusTemp.get("date") - 1);
focusDate(dateToFocusTemp);
break;
case "ArrowRight":
event.preventDefault();
dateToFocusTemp = dateToFocusTemp.set("date", dateToFocusTemp.get("date") + 1);
focusDate(dateToFocusTemp);
break;
case "ArrowUp":
event.preventDefault();
dateToFocusTemp = dateToFocusTemp.set("date", dateToFocusTemp.get("date") - 7);
focusDate(dateToFocusTemp);
break;
case "ArrowDown":
event.preventDefault();
dateToFocusTemp = dateToFocusTemp.set("date", dateToFocusTemp.get("date") + 7);
focusDate(dateToFocusTemp);
break;
case "Home":
event.preventDefault();
dateToFocus.get("day") !== 0
? (dateToFocusTemp = dateToFocusTemp.day(1))
: (dateToFocusTemp = innerDate.date(date.day - 1).day(1));
focusDate(dateToFocusTemp);
break;
case "End":
event.preventDefault();
dateToFocusTemp.get("day") !== 0 && (dateToFocusTemp = dateToFocusTemp.day(7));
focusDate(dateToFocusTemp);
break;
case " ":
event.preventDefault();
onDaySelect(dateToFocusTemp);
break;
}
};

return (
<CalendarContainer>
<DxcFlex alignItems="center" justifyContent="space-between">
{weekDays.map((weekDay) => (
<WeekHeaderCell key={weekDay}>{weekDay}</WeekHeaderCell>
))}
</DxcFlex>
<DayCellsContainer onBlur={handleOnBlur}>
{dayCells.map((date, index) =>
date !== 0 ? (
<DayCell
onKeyDown={(event) => handleDayKeyboardEvent(event, date)}
aria-label={date.day}
id={`day_${date.day}_month${date.month}`}
key={`day_${index}`}
onClick={() => onDateClickHandler(date)}
selected={isDaySelected(date, selectedDate)}
actualMonth={date.month === innerDate.get("month")}
autoFocus={date.day === dateToFocus.get("date") && date.month === dateToFocus.get("month")}
aria-selected={isDaySelected(date, selectedDate)}
tabIndex={date.day === dateToFocus.get("date") && date.month === dateToFocus.get("month") ? 0 : -1}
isCurrentDay={
today.get("date") === date.day &&
today.get("month") === innerDate.get("month") &&
today.get("month") === date.month &&
today.get("year") === innerDate.get("year")
}
>
{date.day}
</DayCell>
) : (
<EmptyDayCell key={`empty_${index}`} />
)
)}
</DayCellsContainer>
</CalendarContainer>
);
};

const CalendarContainer = styled.div`
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: center;
padding: 0px 8px 8px 8px;
width: ${(props) => props.theme.dateInput.pickerWidth};
`;

const WeekHeaderCell = styled.span`
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
font-family: ${(props) => props.theme.dateInput.pickerFontFamily};
font-size: 14px;
line-height: 19px;
color: ${(props) => props.theme.dateInput.pickerWeekFontColor};
`;

const DayCellsContainer = styled.div`
box-sizing: border-box;
display: flex;
gap: 4px;
flex-wrap: wrap;
justify-content: space-between;
`;

type DayCellPropsType = {
selected?: boolean;
actualMonth: boolean;
isCurrentDay: boolean;
};

const DayCell = styled.button<DayCellPropsType>`
display: inline-flex;
justify-content: center;
align-items: center;
width: 36px;
height: 36px;
padding: 0;
font-size: 0.875rem;
font-family: ${(props) => props.theme.dateInput.pickerFontFamily};
font-weight: 400;
border: none;
border-radius: 50%;
cursor: pointer;

&:focus {
outline: ${(props) => props.theme.dateInput.pickerFocusColor} solid 2px;
}
&:hover {
background-color: ${(props) =>
props.selected
? props.theme.dateInput.pickerSelectedDateBackgroundColor
: props.theme.dateInput.pickerHoverDateBackgroundColor};
color: ${(props) =>
props.selected ? props.theme.dateInput.pickerSelectedDateColor : props.theme.dateInput.pickerHoverDateFontColor};
}
&:active {
background-color: #4b1c7d;
color: #ffffff;
}

${(props) => props.isCurrentDay && !props.selected && `border: 1px solid #cbacec;`}
background-color: ${(props) =>
props.selected ? props.theme.dateInput.pickerSelectedDateBackgroundColor : "transparent"};
color: ${(props) =>
props.selected
? props.theme.dateInput.pickerSelectedDateColor
: props.isCurrentDay
? props.theme.dateInput.pickerActualDateFontColor
: !props.actualMonth
? "#999999"
: props.theme.dateInput.pickerDayFontColor};
`;

const EmptyDayCell = styled.div`
display: inline-block;
width: 40px;
height: 36px;
`;

export default React.memo(Calendar);
30 changes: 16 additions & 14 deletions lib/src/date-input/DateInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,41 +109,43 @@ export const Chromatic = () => (

const DatePicker = () => (
<ExampleContainer expanded>
<Title title="Show date input" theme="light" level={4} />
<DxcDateInput label="Date input" defaultValue="10-06-2023" />
<Title title="Show date picker" theme="light" level={4} />
<DxcDateInput label="Date input" defaultValue="06-04-2027" />
</ExampleContainer>
);

export const ShowDatePicker = DatePicker.bind({});
ShowDatePicker.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
const dateBtn = canvas.getByRole("button");
const dateBtn = canvas.getByRole("combobox");
await userEvent.click(dateBtn);
};

const YearPicker = () => (
<ExampleContainer expanded>
<Title title="Show date input" theme="light" level={4} />
<DxcDateInput label="Date input" defaultValue="10-06-2023" />
<Title title="Show year picker" theme="light" level={4} />
<DxcDateInput label="Date input" defaultValue="06-04-2027" />
</ExampleContainer>
);

export const ShowYearPicker = YearPicker.bind({});
ShowYearPicker.play = async () => {
await fireEvent.click(screen.getByRole("button"));
await fireEvent.click(screen.getByText("2023"));
ShowYearPicker.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole("combobox"));
await fireEvent.click(screen.getByText("April 2027"));
};

const YearPickerFocus = () => (
<ExampleContainer expanded>
<Title title="Show date input" theme="light" level={4} />
<DxcDateInput label="Date input" defaultValue="10-06-2023" />
<Title title="Show year picker and focus" theme="light" level={4} />
<DxcDateInput label="Date input" defaultValue="06-04-2027" />
</ExampleContainer>
);

export const ShowYearPickerFocus = YearPickerFocus.bind({});
ShowYearPickerFocus.play = async () => {
await fireEvent.click(screen.getByRole("button"));
await fireEvent.click(screen.getByText("2023"));
await screen.getByText("2021").focus();
ShowYearPickerFocus.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole("combobox"));
await fireEvent.click(screen.getByText("April 2027"));
await userEvent.tab();
};
Loading