Skip to content

Commit

Permalink
test: finish DateTimePicker and Calendar unit tests and VRTs (#4962)
Browse files Browse the repository at this point in the history
* fix: update segment digits overflow

* style: uncomment webkit prefix

* refactor(calendar): extract updateCurrentDate method

* test(date-time-picker): added focus tests

* fix(date-time-picker): focus trap on calendar open

* feat(date-time-picker): change segments focus on empty delete

* test(date-time-picker): created expectFocused helper

* test(date-time-picker): added disabled tests

* test(calendar): added disabled and localized tests

* docs: allow value overrides in storybook

* test(calendar): added clear method test

* test(date-time-picker): added calendar close when disabled changes test

* chore: organize imports

* chore: build

* fix(date-time-picker): mobile text inputMode on dayPeriod segment

* chore: golden img hash update
  • Loading branch information
mizgaionutalexandru authored Dec 4, 2024
1 parent b6670f1 commit 002ed63
Show file tree
Hide file tree
Showing 13 changed files with 601 additions and 419 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ executors:
parameters:
current_golden_images_hash:
type: string
default: 6212f1e2821ec631ba02ddd3784d9c8c03cf6b9a
default: e5761b668fbcb5a97e8f5b297c91b4e2e72c35bd
wireit_cache_name:
type: string
default: wireit
Expand Down
38 changes: 22 additions & 16 deletions packages/calendar/src/Calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
today,
} from '@internationalized/date';
import { NumberFormatter } from '@internationalized/number';

import {
CSSResultArray,
html,
Expand All @@ -45,19 +46,19 @@ import {
languageResolverUpdatedSymbol,
} from '@spectrum-web-components/reactive-controllers/src/LanguageResolution.js';

import styles from './calendar.css.js';

import '@spectrum-web-components/action-button/sp-action-button.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-chevron-left.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-chevron-right.js';

import {
CalendarValue,
CalendarWeekday,
DateCellProperties,
DateValue,
} from './types.js';

import styles from './calendar.css.js';

import '@spectrum-web-components/action-button/sp-action-button.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-chevron-left.js';
import '@spectrum-web-components/icons-workflow/icons/sp-icon-chevron-right.js';

export const DAYS_PER_WEEK = 7;
/**
* @element sp-calendar
Expand Down Expand Up @@ -191,16 +192,7 @@ export class Calendar extends SpectrumElement {
if (changesDates) {
this.convertToCalendarDates();
this.checkDatePropsCompliance(changesMin || changesMax);
if (this.value) this.currentDate = this.value as CalendarDate;
else {
const isTodayNonCompliant = this.isNonCompliantDate(this.today);

if (isTodayNonCompliant) {
if (this.min) this.currentDate = this.min as CalendarDate;
else if (this.max)
this.currentDate = this.max as CalendarDate;
} else this.currentDate = this.today;
}
this.updateCurrentDate();
}

const previousDate = changedProperties.get('currentDate');
Expand Down Expand Up @@ -264,6 +256,20 @@ export class Calendar extends SpectrumElement {
}
}

private updateCurrentDate(): void {
if (this.value) {
this.currentDate = this.value as CalendarDate;
return;
}

const isTodayNonCompliant = this.isNonCompliantDate(this.today);

if (isTodayNonCompliant) {
if (this.min) this.currentDate = this.min as CalendarDate;
else if (this.max) this.currentDate = this.max as CalendarDate;
} else this.currentDate = this.today;
}

/**
* Whether the date is non-compliant with the min and max constraints
*/
Expand Down
95 changes: 49 additions & 46 deletions packages/calendar/stories/calendar.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA
OF ANY KIND, either express or implied. See the License for the specific language
governing permissions and limitations under the License.
*/
import { CalendarDate, DateValue } from '@internationalized/date';

import { html, type TemplateResult } from '@spectrum-web-components/base';
import { CalendarValue } from '@spectrum-web-components/calendar';

import { spreadProps } from '../../../test/lit-helpers.js';
import { CalendarValue } from '../src/types.js';
import { CalendarDate, DateValue } from '@internationalized/date';

import '@spectrum-web-components/calendar/sp-calendar.js';
import '@spectrum-web-components/theme/sp-theme.js';

type ComponentArgs = {
value?: CalendarValue;
Expand Down Expand Up @@ -71,54 +75,46 @@ export default {
},
};

const dateControlsDisabledArgTypes = {
min: {
table: {
disable: true,
},
},
max: {
table: {
disable: true,
},
},
value: {
table: {
disable: true,
},
},
};
const computeProps = (args: StoryArgs): ComponentArgs => {
const timestampToValue = (timestamp: number): CalendarValue => {
const date = new Date();
date.setTime(timestamp);
return new CalendarDate(
date.getFullYear(),
date.getMonth() + 1, // Date months are 0-indexed while CalendarDate months are 1-indexed
date.getDate()
);
};

const timestampToValue = (timestamp: number): CalendarValue => {
const date = new Date();
date.setTime(timestamp);
return new CalendarDate(
date.getFullYear(),
date.getMonth() + 1, // Date months are 0-indexed while CalendarDate months are 1-indexed
date.getDate()
);
return {
value: args.value
? timestampToValue(args.value as unknown as number)
: undefined,
min: args.min
? timestampToValue(args.min as unknown as number)
: undefined,
max: args.max
? timestampToValue(args.max as unknown as number)
: undefined,
padded: args.padded,
disabled: args.disabled,
};
};

const Template = (args: StoryArgs = {}): TemplateResult => {
args.value = args.value
? timestampToValue(args.value as unknown as number)
: undefined;
args.min = args.min
? timestampToValue(args.min as unknown as number)
: undefined;
args.max = args.max
? timestampToValue(args.max as unknown as number)
: undefined;

return html`
<sp-calendar
...=${spreadProps(args)}
...=${spreadProps(computeProps(args))}
@change=${args.onChange}
></sp-calendar>
`;
};

export const Default = (args: StoryArgs): TemplateResult => Template(args);
Default.swc_vrt = {
// Needed because the style on the current day will cause the snapshot to fail every day it runs
skip: true,
};

export const disabled = (args: StoryArgs): TemplateResult => Template(args);
disabled.args = {
Expand All @@ -133,43 +129,50 @@ padded.args = {
export const preselectedValue = (args: StoryArgs): TemplateResult => {
return html`
<sp-calendar
...=${spreadProps(args)}
.value=${new CalendarDate(2022, 4, 16)}
...=${spreadProps(computeProps(args))}
></sp-calendar>
`;
};
preselectedValue.argTypes = dateControlsDisabledArgTypes;

export const minDate = (args: StoryArgs): TemplateResult => {
return html`
<sp-calendar
...=${spreadProps(args)}
.min=${new CalendarDate(2022, 4, 12)}
.value=${new CalendarDate(2022, 4, 16)}
...=${spreadProps(computeProps(args))}
></sp-calendar>
`;
};
minDate.argTypes = dateControlsDisabledArgTypes;

export const maxDate = (args: StoryArgs): TemplateResult => {
return html`
<sp-calendar
...=${spreadProps(args)}
.max=${new CalendarDate(2022, 4, 19)}
.value=${new CalendarDate(2022, 4, 16)}
...=${spreadProps(computeProps(args))}
></sp-calendar>
`;
};
maxDate.argTypes = dateControlsDisabledArgTypes;

export const minAndMaxDates = (args: StoryArgs): TemplateResult => {
return html`
<sp-calendar
...=${spreadProps(args)}
.min=${new CalendarDate(2022, 4, 12)}
.max=${new CalendarDate(2022, 4, 19)}
.value=${new CalendarDate(2022, 4, 16)}
...=${spreadProps(computeProps(args))}
></sp-calendar>
`;
};
minAndMaxDates.argTypes = dateControlsDisabledArgTypes;

export const bengaliIndiaLocale = (args: StoryArgs): TemplateResult => {
return html`
<sp-theme lang="bn-IN">
<sp-calendar
.value=${new CalendarDate(2022, 4, 16)}
...=${spreadProps(computeProps(args))}
></sp-calendar>
</sp-theme>
`;
};
86 changes: 64 additions & 22 deletions packages/calendar/test/calendar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,21 @@ import {
parseDate,
today,
} from '@internationalized/date';
import { elementUpdated, expect, fixture, html } from '@open-wc/testing';

import { ActionButton } from '@spectrum-web-components/action-button';
import { Calendar, DAYS_PER_WEEK } from '@spectrum-web-components/calendar';

import '@spectrum-web-components/calendar/sp-calendar.js';
import '@spectrum-web-components/theme/sp-theme.js';

import { elementUpdated, expect, fixture, html } from '@open-wc/testing';
import { sendKeys, sendMouse } from '@web/test-runner-commands';
import { spy, stub } from 'sinon';
import { testForLitDevWarnings } from '../../../test/testing-helpers.js';
import {
expectSameDates,
fixtureElement,
getElementCenter,
sendKeyMultipleTimes,
} from './helpers.js';

Expand Down Expand Up @@ -498,13 +502,9 @@ describe('Calendar', () => {
await sendKeys({ press: 'Enter' });
await elementUpdated(element);

const rect = unavailableDayElement.getBoundingClientRect();
const centerX = Math.round(rect.left + rect.width / 2);
const centerY = Math.round(rect.top + rect.height / 2);

await sendMouse({
type: 'click',
position: [centerX, centerY],
position: getElementCenter(unavailableDayElement),
});
await elementUpdated(element);

Expand Down Expand Up @@ -652,13 +652,9 @@ describe('Calendar', () => {
});

it('should update value when an available day is clicked', async () => {
const rect = availableDayElement.getBoundingClientRect();
const centerX = Math.round(rect.left + rect.width / 2);
const centerY = Math.round(rect.top + rect.height / 2);

await sendMouse({
type: 'click',
position: [centerX, centerY],
position: getElementCenter(availableDayElement),
});
await elementUpdated(element);

Expand All @@ -680,6 +676,19 @@ describe('Calendar', () => {

expectSameDates(element.value!, availableDateToSelect);
});

it('should clear the selected value when the clear method is called', async () => {
await sendMouse({
type: 'click',
position: getElementCenter(availableDayElement),
});
await elementUpdated(element);

element.clear();
await elementUpdated(element);

expect(element.value).to.be.undefined;
});
});

describe('Dispatched change', () => {
Expand All @@ -701,13 +710,9 @@ describe('Calendar', () => {
});

it("should dispatch 'change' when an available day is selected by clicking", async () => {
const rect = availableDayElement.getBoundingClientRect();
const centerX = Math.round(rect.left + rect.width / 2);
const centerY = Math.round(rect.top + rect.height / 2);

await sendMouse({
type: 'click',
position: [centerX, centerY],
position: getElementCenter(availableDayElement),
});
await elementUpdated(element);

Expand All @@ -716,7 +721,7 @@ describe('Calendar', () => {
changeSpy.resetHistory();
await sendMouse({
type: 'click',
position: [centerX, centerY],
position: getElementCenter(availableDayElement),
});
await elementUpdated(element);
expect(changeSpy.callCount).to.equal(0);
Expand Down Expand Up @@ -797,9 +802,48 @@ describe('Calendar', () => {
});

describe('Disabled', () => {
it('should disable the next and previous month buttons');
it("should not focus the calendar's days");
it("should not select a day when it's clicked");
beforeEach(async () => {
element = await fixtureElement({ props: { disabled: true } });
});

it('should disable the next and previous month buttons', () => {
const nextButton = element.shadowRoot.querySelector(
NEXT_BUTTON_SELECTOR
) as ActionButton;
const prevButton = element.shadowRoot.querySelector(
PREV_BUTTON_SELECTOR
) as ActionButton;

expect(nextButton.disabled).to.be.true;
expect(prevButton.disabled).to.be.true;
});

it('should not have focusable days', () => {
const focusableDay = element.shadowRoot.querySelector(
"td.tableCell[tabindex='0']"
) as HTMLElement;

expect(focusableDay).to.be.null;
});

it("should not select a day when it's clicked", async () => {
const availableDateToSelect = new CalendarDate(
fixedYear,
fixedMonth,
fixedDay + 1
);
const availableDayElement = element.shadowRoot.querySelector(
`[data-value='${availableDateToSelect.toString()}']`
) as HTMLElement;

await sendMouse({
type: 'click',
position: getElementCenter(availableDayElement),
});
await elementUpdated(element);

expect(element.value).to.be.undefined;
});
});

describe('Gregorian AD era limits', () => {
Expand Down Expand Up @@ -903,6 +947,4 @@ describe('Calendar', () => {
expectSameDates(element['currentDate'], lastDate);
});
});

describe('Localized', () => {});
});
Loading

0 comments on commit 002ed63

Please sign in to comment.