diff --git a/integration/specs/Calendar/calendar-11.spec.js b/integration/specs/Calendar/calendar-11.spec.js new file mode 100644 index 000000000..6485e63ce --- /dev/null +++ b/integration/specs/Calendar/calendar-11.spec.js @@ -0,0 +1,41 @@ +const PageCalendar = require('../../../src/components/Calendar/pageObject'); + +const CALENDAR = '#calendar-11'; + +describe('Calendar', () => { + beforeAll(async () => { + await browser.url('/#!/Calendar/11'); + }); + beforeEach(async () => { + await browser.refresh(); + const component = await $(CALENDAR); + await component.waitForExist(); + }); + it('should set range initial date and end date button elements as selected', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthDay(2); + await calendar.clickRightMonthDay(16); + await expect(await calendar.isLeftMonthDaySelected(2)).toBe(true); + await expect(await calendar.isRightMonthDaySelected(16)).toBe(true); + }); + it('should set range initial and end dates when using keyboard navigation', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthSelectYear(); + await browser.keys('Escape'); + await browser.keys('Tab'); + await browser.keys('ArrowUp'); + await browser.keys('ArrowLeft'); + await browser.keys('Enter'); + await browser.keys('PageDown'); + await browser.keys('ArrowDown'); + await browser.keys('Enter'); + await expect(await calendar.isLeftMonthDaySelected(15)).toBe(true); + await expect(await calendar.isRightMonthDaySelected(22)).toBe(true); + }); + it('should disable all days beyond the first disabled day when selecting range', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickRightMonthDay(14); + await expect(await calendar.isRightMonthDayEnabled(23)).toBe(false); + await expect(await calendar.isRightMonthDayEnabled(24)).toBe(false); + }); +}); diff --git a/integration/specs/Calendar/calendar-5.spec.js b/integration/specs/Calendar/calendar-5.spec.js index 7ce99d806..37eca5a93 100644 --- a/integration/specs/Calendar/calendar-5.spec.js +++ b/integration/specs/Calendar/calendar-5.spec.js @@ -11,24 +11,9 @@ describe('Calendar', () => { const component = await $(CALENDAR); await component.waitForExist(); }); - it('should set range initial date and end date button elements as selected', async () => { + it('should disable the dates passed in `disabledDays`', async () => { const calendar = await PageCalendar(CALENDAR); - await calendar.clickDay(2); - await calendar.clickDay(16); - await expect(await calendar.isDaySelected(2)).toBe(true); - await expect(await calendar.isDaySelected(16)).toBe(true); - }); - it('should set range initial and end dates when using keyboard navigation', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickSelectYear(); - await browser.keys('Escape'); - await browser.keys('Tab'); - await browser.keys('ArrowLeft'); - await browser.keys('Enter'); - await browser.keys('ArrowDown'); - await browser.keys('ArrowDown'); - await browser.keys('Enter'); - await expect(await calendar.isDaySelected(2)).toBe(true); - await expect(await calendar.isDaySelected(16)).toBe(true); + await expect(await calendar.isDayEnabled(20)).toBe(false); + await expect(await calendar.isDayEnabled(21)).toBe(true); }); }); diff --git a/integration/specs/Calendar/calendar-7.spec.js b/integration/specs/Calendar/calendar-7.spec.js index 84d419753..b4ad2f285 100644 --- a/integration/specs/Calendar/calendar-7.spec.js +++ b/integration/specs/Calendar/calendar-7.spec.js @@ -11,199 +11,24 @@ describe('Calendar', () => { const component = await $(CALENDAR); await component.waitForExist(); }); - it('should change months when clicked on left and right arrows', async () => { + it('should set range initial date and end date button elements as selected', async () => { const calendar = await PageCalendar(CALENDAR); - await expect(await calendar.getLeftSelectedMonth()).toBe('December'); - await expect(await calendar.getRightSelectedMonth()).toBe('January'); - await calendar.clickPrevMonthButton(); - await expect(await calendar.getLeftSelectedMonth()).toBe('November'); - await expect(await calendar.getRightSelectedMonth()).toBe('December'); - await calendar.clickNextMonthButton(); - await calendar.clickNextMonthButton(); - await expect(await calendar.getLeftSelectedMonth()).toBe('January'); - await expect(await calendar.getRightSelectedMonth()).toBe('February'); + await calendar.clickDay(2); + await calendar.clickDay(16); + await expect(await calendar.isDaySelected(2)).toBe(true); + await expect(await calendar.isDaySelected(16)).toBe(true); }); - it('should select the right day button element', async () => { + it('should set range initial and end dates when using keyboard navigation', async () => { const calendar = await PageCalendar(CALENDAR); - await expect(await calendar.getLeftMonthSelectedDay()).toBe('11'); - await expect(await calendar.getRightMonthSelectedDay()).toBe(undefined); - await calendar.clickLeftMonthDay(5); - await expect(await calendar.getLeftMonthSelectedDay()).toBe('5'); - await expect(await calendar.getRightMonthSelectedDay()).toBe(undefined); - await calendar.clickRightMonthDay(6); - await expect(await calendar.getLeftMonthSelectedDay()).toBe(undefined); - await expect(await calendar.getRightMonthSelectedDay()).toBe('6'); - }); - it('should focus the correct day in left month when an arrow key is pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthDay(11); - await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); - await browser.keys('ArrowUp'); - await expect(await calendar.isLeftMonthDayFocused(4)).toBe(true); - await browser.keys('ArrowDown'); - await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); - await browser.keys('ArrowLeft'); - await expect(await calendar.isLeftMonthDayFocused(10)).toBe(true); - await browser.keys('ArrowRight'); - await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); - }); - it('should focus the correct day in right month when an arrow key is pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickRightMonthDay(11); - await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); - await browser.keys('ArrowUp'); - await expect(await calendar.isRightMonthDayFocused(4)).toBe(true); - await browser.keys('ArrowDown'); - await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); - await browser.keys('ArrowLeft'); - await expect(await calendar.isRightMonthDayFocused(10)).toBe(true); - await browser.keys('ArrowRight'); - await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); - }); - it('should focus the first day of the week in left month when HOME key is pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthDay(11); - await browser.keys('Home'); - await expect(await calendar.isLeftMonthDayFocused(8)).toBe(true); - }); - it('should focus the first day of the week in right month when HOME key is pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickRightMonthDay(11); - await browser.keys('Home'); - await expect(await calendar.isRightMonthDayFocused(5)).toBe(true); - }); - it('should focus the last day of the week in left month when END key is pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthDay(11); - await browser.keys('End'); - await expect(await calendar.isLeftMonthDayFocused(14)).toBe(true); - }); - it('should focus the last day of the week in right month when END key is pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickRightMonthDay(8); - await browser.keys('End'); - await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); - }); - it('should change to the previous month when is in the first day of a month and press LEFT OR UP arrow keys', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthDay(1); - await browser.keys('ArrowLeft'); - await expect(await calendar.getLeftSelectedMonth()).toBe('November'); - await expect(await calendar.isLeftMonthDayFocused(30)).toBe(true); - await calendar.clickRightMonthDay(1); - await expect(await calendar.isLeftMonthDayFocused(30)).toBe(true); - }); - it('should change to next month when is in the last day of a month and press RIGHT OR DOWN arrow keys', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthDay(31); - await browser.keys('ArrowRight'); - await expect(await calendar.isRightMonthDayFocused(1)).toBe(true); - await calendar.clickRightMonthDay(31); - await browser.keys('ArrowRight'); - await expect(await calendar.getLeftSelectedMonth()).toBe('January'); - await expect(await calendar.getRightSelectedMonth()).toBe('February'); - await expect(await calendar.isRightMonthDayFocused(1)).toBe(true); - }); - it('should change to the previous month when PAGEUP key is pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickRightMonthDay(11); - await browser.keys('PageUp'); - await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); - await browser.keys('PageUp'); - await expect(await calendar.getLeftSelectedMonth()).toBe('November'); - await expect(await calendar.getRightSelectedMonth()).toBe('December'); - await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); - }); - it('should change to next month when PAGEDOWN key is pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthDay(11); - await browser.keys('PageDown'); - await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); - await browser.keys('PageDown'); - await expect(await calendar.getLeftSelectedMonth()).toBe('January'); - await expect(await calendar.getRightSelectedMonth()).toBe('February'); - await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); - }); - it('should change to the previous year when ALT + PAGEUP keys are pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthDay(11); - await browser.keys(['Alt', 'PageUp']); - await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); - await expect(await calendar.getLeftSelectedMonth()).toBe('December'); - await expect(await calendar.getLeftMonthSelectedYear()).toBe('2018'); - await expect(await calendar.getRightSelectedMonth()).toBe('January'); - await expect(await calendar.getRightMonthSelectedYear()).toBe('2019'); - }); - it('should select a day when press ENTER key while is focused', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthDay(11); - await browser.keys('ArrowLeft'); - await browser.keys('Enter'); - await expect(await calendar.isLeftMonthDaySelected(10)).toBe(true); - await calendar.clickRightMonthDay(11); - await browser.keys('ArrowLeft'); - await browser.keys('Enter'); - await expect(await calendar.isRightMonthDaySelected(10)).toBe(true); - }); - it('should focus the selected day when tab reach days table', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthSelectYear(); - await expect(await calendar.isLeftMonthDayFocused(11)).toBe(false); - await browser.keys('Escape'); - await browser.keys('Tab'); - await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); - await calendar.clickRightMonthDay(11); - await calendar.clickRightMonthSelectYear(); - await expect(await calendar.isRightMonthDayFocused(11)).toBe(false); + await calendar.clickSelectYear(); await browser.keys('Escape'); await browser.keys('Tab'); - await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); - }); - it('should change to next year when ALT + PAGEDOWN keys are pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthDay(10); - await browser.keys(['Alt', 'PageDown']); - await expect(await calendar.isLeftMonthDayFocused(10)).toBe(true); - await expect(await calendar.getLeftSelectedMonth()).toBe('December'); - await expect(await calendar.getLeftMonthSelectedYear()).toBe('2020'); - await expect(await calendar.getRightSelectedMonth()).toBe('January'); - await expect(await calendar.getRightMonthSelectedYear()).toBe('2021'); - }); - it('should navigate on the header when using arrow keys', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickPrevMonthButton(); - await expect(await calendar.isPrevMonthButtonFocused()).toBe(true); - await expect(await calendar.isLeftYearSelectFocused()).toBe(false); - await expect(await calendar.isRightYearSelectFocused()).toBe(false); - await expect(await calendar.isNextMonthButtonFocused()).toBe(false); await browser.keys('ArrowLeft'); - await expect(await calendar.isPrevMonthButtonFocused()).toBe(true); - await expect(await calendar.isLeftYearSelectFocused()).toBe(false); - await expect(await calendar.isRightYearSelectFocused()).toBe(false); - await expect(await calendar.isNextMonthButtonFocused()).toBe(false); - await browser.keys('ArrowRight'); - await expect(await calendar.isPrevMonthButtonFocused()).toBe(false); - await expect(await calendar.isLeftYearSelectFocused()).toBe(true); - await expect(await calendar.isRightYearSelectFocused()).toBe(false); - await expect(await calendar.isNextMonthButtonFocused()).toBe(false); - await browser.keys('ArrowRight'); - await expect(await calendar.isPrevMonthButtonFocused()).toBe(false); - await expect(await calendar.isLeftYearSelectFocused()).toBe(false); - await expect(await calendar.isRightYearSelectFocused()).toBe(true); - await expect(await calendar.isNextMonthButtonFocused()).toBe(false); - await browser.keys('ArrowRight'); - await expect(await calendar.isPrevMonthButtonFocused()).toBe(false); - await expect(await calendar.isLeftYearSelectFocused()).toBe(false); - await expect(await calendar.isRightYearSelectFocused()).toBe(false); - await expect(await calendar.isNextMonthButtonFocused()).toBe(true); - }); - it('should move from calendar controls to days when TAB key is pressed', async () => { - const calendar = await PageCalendar(CALENDAR); - await calendar.clickPrevMonthButton(); - await expect(await calendar.isLeftMonthDayFocused(1)).toBe(false); - await browser.keys('Tab'); - await expect(await calendar.isLeftMonthDayFocused(1)).toBe(true); - await browser.keys('Tab'); - await expect(await calendar.isLeftMonthDayFocused(1)).toBe(false); + await browser.keys('Enter'); + await browser.keys('ArrowDown'); + await browser.keys('ArrowDown'); + await browser.keys('Enter'); + await expect(await calendar.isDaySelected(2)).toBe(true); + await expect(await calendar.isDaySelected(16)).toBe(true); }); }); diff --git a/integration/specs/Calendar/calendar-9.spec.js b/integration/specs/Calendar/calendar-9.spec.js index 3027b0b87..1a7ddbfbc 100644 --- a/integration/specs/Calendar/calendar-9.spec.js +++ b/integration/specs/Calendar/calendar-9.spec.js @@ -11,24 +11,199 @@ describe('Calendar', () => { const component = await $(CALENDAR); await component.waitForExist(); }); - it('should set range initial date and end date button elements as selected', async () => { + it('should change months when clicked on left and right arrows', async () => { const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthDay(2); - await calendar.clickRightMonthDay(16); - await expect(await calendar.isLeftMonthDaySelected(2)).toBe(true); - await expect(await calendar.isRightMonthDaySelected(16)).toBe(true); + await expect(await calendar.getLeftSelectedMonth()).toBe('December'); + await expect(await calendar.getRightSelectedMonth()).toBe('January'); + await calendar.clickPrevMonthButton(); + await expect(await calendar.getLeftSelectedMonth()).toBe('November'); + await expect(await calendar.getRightSelectedMonth()).toBe('December'); + await calendar.clickNextMonthButton(); + await calendar.clickNextMonthButton(); + await expect(await calendar.getLeftSelectedMonth()).toBe('January'); + await expect(await calendar.getRightSelectedMonth()).toBe('February'); }); - it('should set range initial and end dates when using keyboard navigation', async () => { + it('should select the right day button element', async () => { const calendar = await PageCalendar(CALENDAR); - await calendar.clickLeftMonthSelectYear(); - await browser.keys('Escape'); - await browser.keys('Tab'); + await expect(await calendar.getLeftMonthSelectedDay()).toBe('11'); + await expect(await calendar.getRightMonthSelectedDay()).toBe(undefined); + await calendar.clickLeftMonthDay(5); + await expect(await calendar.getLeftMonthSelectedDay()).toBe('5'); + await expect(await calendar.getRightMonthSelectedDay()).toBe(undefined); + await calendar.clickRightMonthDay(6); + await expect(await calendar.getLeftMonthSelectedDay()).toBe(undefined); + await expect(await calendar.getRightMonthSelectedDay()).toBe('6'); + }); + it('should focus the correct day in left month when an arrow key is pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthDay(11); + await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); await browser.keys('ArrowUp'); - await browser.keys('Enter'); - await browser.keys('PageDown'); + await expect(await calendar.isLeftMonthDayFocused(4)).toBe(true); await browser.keys('ArrowDown'); + await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); + await browser.keys('ArrowLeft'); + await expect(await calendar.isLeftMonthDayFocused(10)).toBe(true); + await browser.keys('ArrowRight'); + await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); + }); + it('should focus the correct day in right month when an arrow key is pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickRightMonthDay(11); + await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); + await browser.keys('ArrowUp'); + await expect(await calendar.isRightMonthDayFocused(4)).toBe(true); + await browser.keys('ArrowDown'); + await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); + await browser.keys('ArrowLeft'); + await expect(await calendar.isRightMonthDayFocused(10)).toBe(true); + await browser.keys('ArrowRight'); + await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); + }); + it('should focus the first day of the week in left month when HOME key is pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthDay(11); + await browser.keys('Home'); + await expect(await calendar.isLeftMonthDayFocused(8)).toBe(true); + }); + it('should focus the first day of the week in right month when HOME key is pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickRightMonthDay(11); + await browser.keys('Home'); + await expect(await calendar.isRightMonthDayFocused(5)).toBe(true); + }); + it('should focus the last day of the week in left month when END key is pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthDay(11); + await browser.keys('End'); + await expect(await calendar.isLeftMonthDayFocused(14)).toBe(true); + }); + it('should focus the last day of the week in right month when END key is pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickRightMonthDay(8); + await browser.keys('End'); + await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); + }); + it('should change to the previous month when is in the first day of a month and press LEFT OR UP arrow keys', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthDay(1); + await browser.keys('ArrowLeft'); + await expect(await calendar.getLeftSelectedMonth()).toBe('November'); + await expect(await calendar.isLeftMonthDayFocused(30)).toBe(true); + await calendar.clickRightMonthDay(1); + await expect(await calendar.isLeftMonthDayFocused(30)).toBe(true); + }); + it('should change to next month when is in the last day of a month and press RIGHT OR DOWN arrow keys', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthDay(31); + await browser.keys('ArrowRight'); + await expect(await calendar.isRightMonthDayFocused(1)).toBe(true); + await calendar.clickRightMonthDay(31); + await browser.keys('ArrowRight'); + await expect(await calendar.getLeftSelectedMonth()).toBe('January'); + await expect(await calendar.getRightSelectedMonth()).toBe('February'); + await expect(await calendar.isRightMonthDayFocused(1)).toBe(true); + }); + it('should change to the previous month when PAGEUP key is pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickRightMonthDay(11); + await browser.keys('PageUp'); + await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); + await browser.keys('PageUp'); + await expect(await calendar.getLeftSelectedMonth()).toBe('November'); + await expect(await calendar.getRightSelectedMonth()).toBe('December'); + await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); + }); + it('should change to next month when PAGEDOWN key is pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthDay(11); + await browser.keys('PageDown'); + await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); + await browser.keys('PageDown'); + await expect(await calendar.getLeftSelectedMonth()).toBe('January'); + await expect(await calendar.getRightSelectedMonth()).toBe('February'); + await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); + }); + it('should change to the previous year when ALT + PAGEUP keys are pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthDay(11); + await browser.keys(['Alt', 'PageUp']); + await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); + await expect(await calendar.getLeftSelectedMonth()).toBe('December'); + await expect(await calendar.getLeftMonthSelectedYear()).toBe('2018'); + await expect(await calendar.getRightSelectedMonth()).toBe('January'); + await expect(await calendar.getRightMonthSelectedYear()).toBe('2019'); + }); + it('should select a day when press ENTER key while is focused', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthDay(11); + await browser.keys('ArrowLeft'); + await browser.keys('Enter'); + await expect(await calendar.isLeftMonthDaySelected(10)).toBe(true); + await calendar.clickRightMonthDay(11); + await browser.keys('ArrowLeft'); await browser.keys('Enter'); - await expect(await calendar.isLeftMonthDaySelected(16)).toBe(true); - await expect(await calendar.isRightMonthDaySelected(23)).toBe(true); + await expect(await calendar.isRightMonthDaySelected(10)).toBe(true); + }); + it('should focus the selected day when tab reach days table', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthSelectYear(); + await expect(await calendar.isLeftMonthDayFocused(11)).toBe(false); + await browser.keys('Escape'); + await browser.keys('Tab'); + await expect(await calendar.isLeftMonthDayFocused(11)).toBe(true); + await calendar.clickRightMonthDay(11); + await calendar.clickRightMonthSelectYear(); + await expect(await calendar.isRightMonthDayFocused(11)).toBe(false); + await browser.keys('Escape'); + await browser.keys('Tab'); + await expect(await calendar.isRightMonthDayFocused(11)).toBe(true); + }); + it('should change to next year when ALT + PAGEDOWN keys are pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickLeftMonthDay(10); + await browser.keys(['Alt', 'PageDown']); + await expect(await calendar.isLeftMonthDayFocused(10)).toBe(true); + await expect(await calendar.getLeftSelectedMonth()).toBe('December'); + await expect(await calendar.getLeftMonthSelectedYear()).toBe('2020'); + await expect(await calendar.getRightSelectedMonth()).toBe('January'); + await expect(await calendar.getRightMonthSelectedYear()).toBe('2021'); + }); + it('should navigate on the header when using arrow keys', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickPrevMonthButton(); + await expect(await calendar.isPrevMonthButtonFocused()).toBe(true); + await expect(await calendar.isLeftYearSelectFocused()).toBe(false); + await expect(await calendar.isRightYearSelectFocused()).toBe(false); + await expect(await calendar.isNextMonthButtonFocused()).toBe(false); + await browser.keys('ArrowLeft'); + await expect(await calendar.isPrevMonthButtonFocused()).toBe(true); + await expect(await calendar.isLeftYearSelectFocused()).toBe(false); + await expect(await calendar.isRightYearSelectFocused()).toBe(false); + await expect(await calendar.isNextMonthButtonFocused()).toBe(false); + await browser.keys('ArrowRight'); + await expect(await calendar.isPrevMonthButtonFocused()).toBe(false); + await expect(await calendar.isLeftYearSelectFocused()).toBe(true); + await expect(await calendar.isRightYearSelectFocused()).toBe(false); + await expect(await calendar.isNextMonthButtonFocused()).toBe(false); + await browser.keys('ArrowRight'); + await expect(await calendar.isPrevMonthButtonFocused()).toBe(false); + await expect(await calendar.isLeftYearSelectFocused()).toBe(false); + await expect(await calendar.isRightYearSelectFocused()).toBe(true); + await expect(await calendar.isNextMonthButtonFocused()).toBe(false); + await browser.keys('ArrowRight'); + await expect(await calendar.isPrevMonthButtonFocused()).toBe(false); + await expect(await calendar.isLeftYearSelectFocused()).toBe(false); + await expect(await calendar.isRightYearSelectFocused()).toBe(false); + await expect(await calendar.isNextMonthButtonFocused()).toBe(true); + }); + it('should move from calendar controls to days when TAB key is pressed', async () => { + const calendar = await PageCalendar(CALENDAR); + await calendar.clickPrevMonthButton(); + await expect(await calendar.isLeftMonthDayFocused(1)).toBe(false); + await browser.keys('Tab'); + await expect(await calendar.isLeftMonthDayFocused(1)).toBe(true); + await browser.keys('Tab'); + await expect(await calendar.isLeftMonthDayFocused(1)).toBe(false); }); }); diff --git a/src/components/Calendar/day.js b/src/components/Calendar/day.js index 8e2d0d3ff..b9ac4058a 100644 --- a/src/components/Calendar/day.js +++ b/src/components/Calendar/day.js @@ -8,6 +8,7 @@ import StyledDayButton from './styled/dayButton'; import StyledRangeHighlight from './styled/rangeHighlight'; import { isSameDay, compareDates } from './helpers'; import { useRangeStartDate, useRangeEndDate } from './hooks'; +import isInArray from './helpers/isInArray'; function DayComponent(props) { const { @@ -16,6 +17,7 @@ function DayComponent(props) { isSelected, minDate, maxDate, + maxRangeEnd, onChange, isWithinRange, isFirstDayOfWeek, @@ -23,14 +25,20 @@ function DayComponent(props) { useAutoFocus, focusedDate, currentRange, + disabledDays, privateKeyDown, privateOnFocus, privateOnBlur, privateOnHover, } = props; + const day = date.getDate(); const isAdjacentDate = date.getMonth() !== firstDayMonth.getMonth(); - const isDisabled = compareDates(date, maxDate) > 0 || compareDates(date, minDate) < 0; + const isDisabled = + compareDates(date, maxDate) > 0 || + compareDates(date, minDate) < 0 || + (maxRangeEnd && compareDates(date, maxRangeEnd) > 0) || + isInArray(date, disabledDays); const tabIndex = isSameDay(focusedDate, date) ? 0 : -1; const buttonRef = useRef(); const isRangeStartDate = useRangeStartDate(date, currentRange); diff --git a/src/components/Calendar/doubleCalendar/index.js b/src/components/Calendar/doubleCalendar/index.js index 6db522df6..a5f770954 100644 --- a/src/components/Calendar/doubleCalendar/index.js +++ b/src/components/Calendar/doubleCalendar/index.js @@ -12,6 +12,8 @@ import { getNextFocusedDate, isEmptyRange, isDateBelowLimit, + isDateBeyondLimit, + normalizeDates, } from '../helpers'; import { useNormalizedValue, @@ -45,12 +47,14 @@ export default function DoubleCalendar(props) { onChange, selectedRange, selectionType, + disabledDays, } = props; const currentValue = useNormalizedValue(value); const [focusedDate, setFocusedDate] = useState(currentValue); const [currentMonth, setCurrentMonth] = useState(getFirstDayMonth(currentValue)); const [currentRange, setCurrentRange] = useState(selectedRange); const [enableNavKeys, setEnableNavKeys] = useState(false); + const [maxRangeEnd, setMaxRangeEnd] = useState(undefined); const rightCalendarMonth = addMonths(currentMonth, 1); const currentYear = currentMonth.getFullYear(); @@ -158,6 +162,19 @@ export default function DoubleCalendar(props) { setCurrentRange(selectedRange); }, [selectedRange]); + useEffect(() => { + const normalizedDisabledDays = normalizeDates(disabledDays); + const newMaxRangeEnd = + selectedRange && selectedRange.length === 1 + ? Math.min( + ...normalizedDisabledDays.filter(day => + isDateBeyondLimit(day, selectedRange[0]), + ), + ) + : undefined; + setMaxRangeEnd(newMaxRangeEnd); + }, [selectedRange, disabledDays]); + const currentYearSelectTabIndex = disablePreviousMonth ? undefined : -1; return ( @@ -223,6 +240,8 @@ export default function DoubleCalendar(props) { selectionType, selectedRange, currentRange, + disabledDays, + maxRangeEnd, privateOnFocus: handleOnDayFocus, privateOnBlur: handleOnDayBlur, privateKeyDown: handleKeyDown, @@ -250,6 +269,8 @@ export default function DoubleCalendar(props) { selectionType, selectedRange, currentRange, + disabledDays, + maxRangeEnd, privateOnFocus: handleOnDayFocus, privateOnBlur: handleOnDayBlur, privateKeyDown: handleKeyDown, @@ -286,6 +307,9 @@ DoubleCalendar.propTypes = { selectedRange: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]), ), + disabledDays: PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), + ), }; DoubleCalendar.defaultProps = { @@ -299,4 +323,5 @@ DoubleCalendar.defaultProps = { locale: undefined, selectionType: 'single', selectedRange: undefined, + disabledDays: [], }; diff --git a/src/components/Calendar/helpers/__test__/isInArray.spec.js b/src/components/Calendar/helpers/__test__/isInArray.spec.js new file mode 100644 index 000000000..db3e2803c --- /dev/null +++ b/src/components/Calendar/helpers/__test__/isInArray.spec.js @@ -0,0 +1,14 @@ +import isInArray from '../isInArray'; + +describe('isInArray', () => { + it('should return true', () => { + const array = ['09/07/2022', new Date('09/08/2022')]; + expect(isInArray(new Date('09/07/2022'), array)).toBe(true); + expect(isInArray('09/08/2022', array)).toBe(true); + }); + it('should return false', () => { + const array = ['09/07/2022', new Date('09/08/2022')]; + expect(isInArray(new Date('10/07/2022'), array)).toBe(false); + expect(isInArray('09/08/2022', undefined)).toBe(false); + }); +}); diff --git a/src/components/Calendar/helpers/__test__/normalizeDates.spec.js b/src/components/Calendar/helpers/__test__/normalizeDates.spec.js new file mode 100644 index 000000000..02aafdee0 --- /dev/null +++ b/src/components/Calendar/helpers/__test__/normalizeDates.spec.js @@ -0,0 +1,9 @@ +import normalizeDates from '../normalizeDates'; + +describe('normalizeDates', () => { + it('should return normalize the array of dates', () => { + const array = [undefined, '09/07/2022', new Date('09/08/2022'), 1]; + const expected = [new Date('09/07/2022'), new Date('09/08/2022')]; + expect(normalizeDates(array)).toEqual(expected); + }); +}); diff --git a/src/components/Calendar/helpers/index.js b/src/components/Calendar/helpers/index.js index 8866e6415..3fd7a1ae3 100644 --- a/src/components/Calendar/helpers/index.js +++ b/src/components/Calendar/helpers/index.js @@ -22,3 +22,4 @@ export { default as buildNewRangeFromValue } from './buildNewRangeFromValue'; export { default as shouldDateBeSelected } from './shouldDateBeSelected'; export { default as isSameDatesRange } from './isSameDatesRange'; export { default as isEmptyRange } from './isEmptyRange'; +export { default as normalizeDates } from './normalizeDates'; diff --git a/src/components/Calendar/helpers/isInArray.js b/src/components/Calendar/helpers/isInArray.js new file mode 100644 index 000000000..adaf5645b --- /dev/null +++ b/src/components/Calendar/helpers/isInArray.js @@ -0,0 +1,6 @@ +import isSameDay from './isSameDay'; + +export default function isInArray(value, array) { + if (!array) return false; + return array.some(day => isSameDay(day, value)); +} diff --git a/src/components/Calendar/helpers/normalizeDate.js b/src/components/Calendar/helpers/normalizeDate.js index 3b05194bc..1a9948517 100644 --- a/src/components/Calendar/helpers/normalizeDate.js +++ b/src/components/Calendar/helpers/normalizeDate.js @@ -1,3 +1,6 @@ export default function normalizeDate(date) { + if (typeof date === 'string') { + return new Date(date); + } return date || new Date(); } diff --git a/src/components/Calendar/helpers/normalizeDates.js b/src/components/Calendar/helpers/normalizeDates.js new file mode 100644 index 000000000..bbe109170 --- /dev/null +++ b/src/components/Calendar/helpers/normalizeDates.js @@ -0,0 +1,7 @@ +import normalizeDate from './normalizeDate'; + +export default function normalizeDates(dates) { + return dates + .filter(date => date && (typeof date === 'string' || date instanceof Date)) + .map(date => normalizeDate(date)); +} diff --git a/src/components/Calendar/index.d.ts b/src/components/Calendar/index.d.ts index d18089c46..741e23e2d 100644 --- a/src/components/Calendar/index.d.ts +++ b/src/components/Calendar/index.d.ts @@ -10,6 +10,7 @@ export interface CalendarProps extends BaseProps { selectionType?: 'single' | 'range'; variant?: 'single' | 'double'; locale?: string; + disabledDays?: Array; } declare const Calendar: ComponentType; diff --git a/src/components/Calendar/index.js b/src/components/Calendar/index.js index 50077ac57..9dfe58c64 100644 --- a/src/components/Calendar/index.js +++ b/src/components/Calendar/index.js @@ -78,6 +78,10 @@ Calendar.propTypes = { selectionType: PropTypes.oneOf(['single', 'range']), /** The component variant. Defaults to 'single' */ variant: PropTypes.oneOf(['single', 'double']), + /** An array containing the days that should be disabled */ + disabledDays: PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), + ), }; Calendar.defaultProps = { @@ -91,4 +95,5 @@ Calendar.defaultProps = { locale: undefined, selectionType: 'single', variant: 'single', + disabledDays: [], }; diff --git a/src/components/Calendar/pageObject/doubleCalendar.js b/src/components/Calendar/pageObject/doubleCalendar.js index 3323e5ad0..a6fd35e70 100644 --- a/src/components/Calendar/pageObject/doubleCalendar.js +++ b/src/components/Calendar/pageObject/doubleCalendar.js @@ -171,6 +171,18 @@ class PageDoubleCalendar { ); } + /** + * Returns true when the specific day element in left month is selected. + * @method + * @returns {string} + */ + async isLeftMonthDayEnabled(day) { + const buttonEl = await $(this.rootElement) + .$$('table[role=grid]')[0] + .$(`button=${day}`); + return buttonEl.isExisting(); + } + /** * Returns true when the year select element in left month has focus. * @method @@ -278,6 +290,18 @@ class PageDoubleCalendar { ); } + /** + * Returns true when the specific day element in right month is selected. + * @method + * @returns {string} + */ + async isRightMonthDayEnabled(day) { + const buttonEl = await $(this.rootElement) + .$$('table[role=grid]')[1] + .$(`button=${day}`); + return buttonEl.isExisting(); + } + /** * Returns true when the year select element in right month has focus. * @method diff --git a/src/components/Calendar/pageObject/singleCalendar.js b/src/components/Calendar/pageObject/singleCalendar.js index 269f39514..091b2e8cf 100644 --- a/src/components/Calendar/pageObject/singleCalendar.js +++ b/src/components/Calendar/pageObject/singleCalendar.js @@ -102,6 +102,18 @@ class PageSingleCalendar { ); } + /** + * Returns true when the specific day element is selected. + * @method + * @returns {string} + */ + async isDayEnabled(day) { + const spanEl = await $(this.rootElement) + .$('table') + .$(`button=${day}`); + return spanEl.isExisting(); + } + /** * Set the value of the year select element * @method diff --git a/src/components/Calendar/readme.md b/src/components/Calendar/readme.md index 8db67810b..ea6386215 100644 --- a/src/components/Calendar/readme.md +++ b/src/components/Calendar/readme.md @@ -1,4 +1,5 @@ -##### Calendar base: +# Base Calendar +##### To pick a single date you can use a basic calendar. ```js import React from 'react'; @@ -43,7 +44,8 @@ const selectStyles = { ``` -##### Calendar with dates contrains: +# Calendar with dates contrains +##### If you need to limit the available dates you can do so with `minDate` and `maxDate` props ```js import React from 'react'; @@ -70,7 +72,35 @@ const calendarContainerStyles = { ``` -##### Calendar with range selection: +# Calendar with disabled days +##### If more control over the available dates is needed, the `disabledDays` prop provides a way to disable individual days + +```js +import React from 'react'; +import { Card, Calendar } from 'react-rainbow-components'; + +const initialState = { date: new Date('2019-11-11 00:00:00') }; +const calendarContainerStyles = { + width: '28rem', + height: '27rem', +}; + +
+
+ + setState({ date: value })} + disabledDays={['2019/11/15', new Date('2019/11/20')]} + /> + +
+
+``` + +# Calendar with range selection +##### Use `selectionType = 'range'` to allow your users to pick a range of dates ```js import React from 'react'; @@ -89,17 +119,19 @@ const calendarContainerStyles = {
setState({ range: value })} + disabledDays={['2019/01/23']} />
``` -##### Calendar with variant double: +# Calendar with variant double +##### Set the `variant` prop to 'double' if you want to show two months side by side ```js import React from 'react'; @@ -115,7 +147,7 @@ const calendarContainerStyles = { style={calendarContainerStyles} > setState({ date: value })} @@ -124,7 +156,8 @@ const calendarContainerStyles = { ``` -##### Calendar with variant double and range selection: +# Calendar with variant double and range selection +##### The best use case for double variant is to ease the selection of large date ranges ```js import React from 'react'; @@ -145,11 +178,12 @@ const calendarContainerStyles = { style={calendarContainerStyles} > setState({ range: value })} + disabledDays={['2020/01/23']} /> diff --git a/src/components/Calendar/singleCalendar.js b/src/components/Calendar/singleCalendar.js index b6ba42708..f399ddccb 100644 --- a/src/components/Calendar/singleCalendar.js +++ b/src/components/Calendar/singleCalendar.js @@ -38,6 +38,7 @@ import { } from '../../libs/constants'; import { uniqueId } from '../../libs/utils'; import { Provider } from './context'; +import normalizeDates from './helpers/normalizeDates'; class SingleCalendar extends Component { constructor(props) { @@ -46,6 +47,7 @@ class SingleCalendar extends Component { focusedDate: normalizeDate(props.value), currentMonth: getFirstDayMonth(normalizeDate(props.value)), currentRange: props.selectedRange, + maxRangeEnd: undefined, }; this.enableNavKeys = false; this.monthLabelId = uniqueId('month'); @@ -131,14 +133,16 @@ class SingleCalendar extends Component { } getContext() { - const { focusedDate, currentRange } = this.state; - const { selectionType, selectedRange } = this.props; + const { focusedDate, currentRange, maxRangeEnd } = this.state; + const { selectionType, selectedRange, disabledDays } = this.props; return { focusedDate, useAutoFocus: this.enableNavKeys, selectionType, selectedRange, currentRange, + disabledDays, + maxRangeEnd, privateKeyDown: this.handleKeyDown, privateOnFocus: this.onDayFocus, privateOnBlur: this.onDayBlur, @@ -203,8 +207,16 @@ class SingleCalendar extends Component { } updateCurrentRange(value) { + const { disabledDays: disabledDaysInProps } = this.props; + const disabledDays = normalizeDates(disabledDaysInProps); + + const maxRangeEnd = + value.length === 1 + ? Math.min(...disabledDays.filter(day => isDateBeyondLimit(day, value[0]))) + : undefined; this.setState({ currentRange: value, + maxRangeEnd, }); } @@ -368,6 +380,9 @@ SingleCalendar.propTypes = { selectedRange: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]), ), + disabledDays: PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), + ), }; SingleCalendar.defaultProps = { @@ -381,6 +396,7 @@ SingleCalendar.defaultProps = { locale: undefined, selectionType: 'single', selectedRange: undefined, + disabledDays: [], }; export default SingleCalendar; diff --git a/src/components/DatePicker/index.d.ts b/src/components/DatePicker/index.d.ts index eff31d607..9d204b0a6 100644 --- a/src/components/DatePicker/index.d.ts +++ b/src/components/DatePicker/index.d.ts @@ -29,6 +29,7 @@ export interface DatePickerProps extends BaseProps { selectionType?: 'single' | 'range'; variant?: 'single' | 'double'; icon?: ReactNode; + disabledDays?: Array; } export default function(props: DatePickerProps): JSX.Element | null; diff --git a/src/components/DatePicker/index.js b/src/components/DatePicker/index.js index 27b7fa5a7..74256dd38 100644 --- a/src/components/DatePicker/index.js +++ b/src/components/DatePicker/index.js @@ -42,6 +42,7 @@ const DatePicker = React.forwardRef((props, ref) => { variant, selectionType, icon: iconInProps, + disabledDays, } = useReduxForm(props); const currentLocale = useLocale(locale); @@ -141,6 +142,7 @@ const DatePicker = React.forwardRef((props, ref) => { value={value} onChange={handleChange} onRequestClose={closeModal} + disabledDays={disabledDays} /> ); @@ -211,6 +213,10 @@ DatePicker.propTypes = { variant: PropTypes.oneOf(['single', 'double']), /** The icon to show if it is passed. It must be a svg icon or a font icon. Defaults to a Calendar icon */ icon: PropTypes.node, + /** An array containing the days that should be disabled */ + disabledDays: PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), + ), }; DatePicker.defaultProps = { @@ -241,6 +247,7 @@ DatePicker.defaultProps = { selectionType: 'single', variant: 'single', icon: undefined, + disabledDays: [], }; export default DatePicker; diff --git a/src/components/DatePickerModal/index.d.ts b/src/components/DatePickerModal/index.d.ts index d3dd4757a..3a74d5e20 100644 --- a/src/components/DatePickerModal/index.d.ts +++ b/src/components/DatePickerModal/index.d.ts @@ -15,6 +15,7 @@ export interface DatePickerModalProps extends BaseProps { selectionType?: 'single' | 'range'; variant?: 'single' | 'double'; locale?: string; + disabledDays?: Array; } export default function(props: DatePickerModalProps): JSX.Element | null; diff --git a/src/components/DatePickerModal/index.js b/src/components/DatePickerModal/index.js index 18e289049..40019a220 100644 --- a/src/components/DatePickerModal/index.js +++ b/src/components/DatePickerModal/index.js @@ -18,6 +18,7 @@ export default function DatePickerModal(props) { onRequestClose, isOpen, title, + disabledDays, } = props; const calendarId = id && `${id}_calendar`; @@ -44,6 +45,7 @@ export default function DatePickerModal(props) { variant={variant} value={value} onChange={onChange} + disabledDays={disabledDays} /> ); @@ -86,6 +88,10 @@ DatePickerModal.propTypes = { /** The title can include text or another component, * and is displayed in the header of the component. */ title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]), + /** An array containing the days that should be disabled */ + disabledDays: PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), + ), }; DatePickerModal.defaultProps = { @@ -102,4 +108,5 @@ DatePickerModal.defaultProps = { onChange: () => {}, onRequestClose: () => {}, title: undefined, + disabledDays: [], }; diff --git a/src/components/DateTimePicker/index.d.ts b/src/components/DateTimePicker/index.d.ts index 07c3c0d40..8f728b0e1 100644 --- a/src/components/DateTimePicker/index.d.ts +++ b/src/components/DateTimePicker/index.d.ts @@ -8,6 +8,7 @@ export interface DateTimePickerProps extends DatePickerProps { hour24?: boolean; labelAlignment?: LabelAlignment; hideLabel?: boolean; + disabledDays?: Array; } declare const DatePicker: ComponentType; diff --git a/src/components/DateTimePicker/index.js b/src/components/DateTimePicker/index.js index d638b7d9d..ac7af0c71 100644 --- a/src/components/DateTimePicker/index.js +++ b/src/components/DateTimePicker/index.js @@ -45,6 +45,7 @@ const DateTimePicker = React.forwardRef((props, ref) => { hour24, locale: localeProp, icon: iconInProps, + disabledDays, } = props; const inputRef = useRef(); @@ -145,6 +146,7 @@ const DateTimePicker = React.forwardRef((props, ref) => { cancelLabel={cancelLabel} locale={locale} hour24={hour24} + disabledDays={disabledDays} /> ); @@ -214,6 +216,10 @@ DateTimePicker.propTypes = { hour24: PropTypes.bool, /** The icon to show if it is passed. It must be a svg icon or a font icon. Defaults to a DateTime icon */ icon: PropTypes.node, + /** An array containing the days that should be disabled */ + disabledDays: PropTypes.arrayOf( + PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]), + ), }; DateTimePicker.defaultProps = { @@ -245,6 +251,7 @@ DateTimePicker.defaultProps = { locale: undefined, hour24: false, icon: undefined, + disabledDays: [], }; export default withReduxForm(DateTimePicker); diff --git a/src/components/DateTimePicker/pickerModal.js b/src/components/DateTimePicker/pickerModal.js index 54b4a0492..055746a63 100644 --- a/src/components/DateTimePicker/pickerModal.js +++ b/src/components/DateTimePicker/pickerModal.js @@ -26,6 +26,7 @@ function DateTimePickerModal(props) { onChange, locale, hour24, + disabledDays, } = props; const [date, setDate] = useState(value); @@ -67,6 +68,7 @@ function DateTimePickerModal(props) { maxDate={maxDate} formatStyle={formatStyle} onChange={handleDateChange} + disabledDays={disabledDays} /> {}, locale: undefined, hour24: false, + disabledDays: [], }; export default DateTimePickerModal;