datePicker.test.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. import React from 'react';
  2. import DatePicker from '../index';
  3. import BaseDatePicker from '../datePicker';
  4. import ConfigProvider from '../../configProvider';
  5. import * as _ from 'lodash-es';
  6. import { clear } from 'jest-date-mock';
  7. import { addDays, startOfWeek, endOfWeek, add, format, addWeeks, set } from 'date-fns';
  8. import { zhCN, enUS } from "date-fns/locale";
  9. import { zonedTimeToUtc } from 'date-fns-tz';
  10. import { strings } from '../../../semi-foundation/datePicker/constants';
  11. import { BASE_CLASS_PREFIX } from '../../../semi-foundation/base/constants';
  12. import en_US from '../../locale/source/en_US';
  13. import LocaleProvider from '../../locale/localeProvider';
  14. import { genBeforeEach, genAfterEach, mount, sleep as baseSleep } from '../../_test_/utils';
  15. import { utcToZonedTime, toIANA } from '../../../semi-foundation/utils/date-fns-extra';
  16. const animationMs = 200;
  17. const baseYear = 2019;
  18. const baseDay = 8;
  19. const baseMon = 8;
  20. const baseDate = new Date(baseYear, baseMon, baseDay, 8, 8, 8, 8);
  21. const popupSelector = `.${BASE_CLASS_PREFIX}-popover .${BASE_CLASS_PREFIX}-datepicker`;
  22. const sleep = (ms = 200) => baseSleep(ms);
  23. const getRandomIANATimeZone = function () {
  24. const TIME_ZONE = Array.from({ length: 26 }).map((_, index) => index - 11);
  25. const offset = Math.floor(Math.random() * TIME_ZONE.length);
  26. const timeZone = toIANA(TIME_ZONE[offset]);
  27. return [timeZone, offset];
  28. };
  29. describe(`DatePicker`, () => {
  30. beforeEach(() => {
  31. clear();
  32. genBeforeEach()();
  33. });
  34. afterEach(genAfterEach());
  35. it(`test appearance`, () => {
  36. const defaultValue = new Date();
  37. /**
  38. * with default value
  39. */
  40. const elem = mount(<DatePicker defaultValue={defaultValue} />);
  41. expect(elem.find(`.${BASE_CLASS_PREFIX}-datepicker`).length).toBe(1);
  42. expect(elem.find(`.${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-input-wrapper-clearable`).length).toBe(1);
  43. });
  44. it(`test defaultOpen`, async () => {
  45. const defaultValue = new Date();
  46. const open = true;
  47. const motion = false;
  48. const elem = mount(<DatePicker motion={motion} defaultOpen={open} defaultValue={defaultValue} />);
  49. expect(document.querySelectorAll(popupSelector).length).toBe(1);
  50. // document.body.click();
  51. document.dispatchEvent(new Event('mousedown', { bubbles: true }));
  52. await sleep();
  53. expect(document.querySelectorAll(popupSelector).length).toBe(0);
  54. });
  55. it(`test open`, async () => {
  56. const defaultValue = new Date();
  57. const open = true;
  58. const motion = false;
  59. const elem = mount(<DatePicker motion={motion} open={open} defaultValue={defaultValue} />);
  60. expect(document.querySelectorAll(popupSelector).length).toBe(1);
  61. /**
  62. * click body without reset open
  63. */
  64. document.body.click();
  65. await sleep();
  66. expect(document.querySelectorAll(popupSelector).length).toBe(1);
  67. /**
  68. * click body and set open
  69. */
  70. elem.setProps({ open: false });
  71. document.body.click();
  72. await sleep();
  73. expect(document.querySelectorAll(popupSelector).length).toBe(0);
  74. });
  75. it(`test presets`, async () => {
  76. const dayOffset = 1;
  77. const presets = [
  78. {
  79. text: 'Today',
  80. start: new Date(baseDate),
  81. end: new Date(baseDate),
  82. },
  83. {
  84. text: 'Next Day',
  85. start: addDays(new Date(baseDate), dayOffset),
  86. end: addDays(new Date(baseDate), dayOffset),
  87. },
  88. ];
  89. const defaultValue = new Date(addDays(new Date(baseDate), -dayOffset));
  90. const open = true;
  91. const motion = false;
  92. const demo = mount(<DatePicker presets={presets} motion={motion} open={open} defaultValue={defaultValue} />);
  93. const elem = demo.find(BaseDatePicker);
  94. const btns = document.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-quick-control-item`);
  95. /**
  96. * click next day
  97. */
  98. btns[1].click();
  99. let value = elem.state('value');
  100. expect(value[0].getDate() - defaultValue.getDate()).toEqual(dayOffset * 2);
  101. /**
  102. * click current day
  103. */
  104. btns[0].click();
  105. value = elem.state('value');
  106. expect(value[0].getDate()).toEqual(defaultValue.getDate() + 1);
  107. });
  108. it(`test value`, async () => {
  109. const currentValue = new Date(baseDate);
  110. const open = true;
  111. const motion = false;
  112. const dayOffset = 3;
  113. const onChange = sinon.spy(async (date, str) => {
  114. expect(date.getDate()).toBe(currentValue.getDate() + dayOffset);
  115. elem.setProps({ value: date });
  116. await sleep();
  117. expect(_.first(datePickerElem.state('value')).getDate() - baseDate.getDate()).toBe(dayOffset);
  118. });
  119. const elem = mount(<DatePicker motion={motion} open={open} value={currentValue} onChange={onChange} />);
  120. const datePickerElem = elem.find(BaseDatePicker);
  121. const popup = document.querySelector(`.${BASE_CLASS_PREFIX}-popover .${BASE_CLASS_PREFIX}-datepicker`);
  122. const selectedDayElem = popup.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-day-selected`);
  123. const nextOffsetDayElem = _.times(dayOffset).reduce(node => node.nextElementSibling, selectedDayElem);
  124. nextOffsetDayElem.click();
  125. await sleep(animationMs * 3);
  126. expect(onChange.called).toBeTruthy();
  127. });
  128. it(`test needConfirm`, async () => {
  129. const currentValue = new Date(baseDate);
  130. const open = true;
  131. const motion = false;
  132. const type = 'dateTime';
  133. const needConfirm = true;
  134. const dayOffset = 3;
  135. const demo = mount(
  136. <DatePicker motion={motion} defaultValue={currentValue} open={open} type={type} needConfirm={needConfirm} />
  137. );
  138. const elem = demo.find(BaseDatePicker);
  139. const btns = document.querySelectorAll(`.${BASE_CLASS_PREFIX}-popover .${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-datepicker-footer .${BASE_CLASS_PREFIX}-button`);
  140. expect(btns.length).toBe(2);
  141. const selectedDayElem = document.querySelector(`.${BASE_CLASS_PREFIX}-popover .${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-datepicker-day-selected`);
  142. const nextOffsetDayElem = _.times(dayOffset).reduce(node => node.nextElementSibling, selectedDayElem);
  143. /**
  144. * click next day
  145. */
  146. nextOffsetDayElem.click();
  147. await sleep();
  148. expect(_.first(elem.state('value')).getDate() === currentValue.getDate()).toBeTruthy();
  149. /**
  150. * click cancel button
  151. */
  152. btns[0].click();
  153. await sleep();
  154. expect(_.first(elem.state('value')).getDate() === currentValue.getDate()).toBeTruthy();
  155. /**
  156. * click ensure button
  157. */
  158. btns[1].click();
  159. await sleep();
  160. expect(_.first(elem.state('value')).getDate() - currentValue.getDate()).toBe(dayOffset);
  161. demo.unmount();
  162. });
  163. it(`test events`, async () => {
  164. const currentValue = new Date(baseDate);
  165. const open = true;
  166. const motion = false;
  167. const type = 'dateTime';
  168. const needConfirm = true;
  169. const dayOffset = 3;
  170. const onOpenChange = sinon.spy();
  171. const onChange = sinon.spy();
  172. const elem = mount(
  173. <DatePicker
  174. onOpenChange={onOpenChange}
  175. motion={motion}
  176. defaultValue={currentValue}
  177. type={type}
  178. needConfirm={needConfirm}
  179. onChange={onChange}
  180. />
  181. );
  182. /**
  183. * click outside
  184. */
  185. document.body.click();
  186. await sleep();
  187. expect(onOpenChange.called).toBeFalsy();
  188. /**
  189. * click datePicker
  190. */
  191. const inputWrapper = document.querySelector(`.${BASE_CLASS_PREFIX}-input-wrapper`);
  192. const dateInputDom = inputWrapper.parentElement;
  193. dateInputDom.click();
  194. await sleep();
  195. expect(onOpenChange.called).toBeTruthy();
  196. /**
  197. * input value change
  198. */
  199. elem.find(`.${BASE_CLASS_PREFIX}-input-wrapper input`).simulate('change', { target: { value: '2019-10-02 08:30:02' } });
  200. expect(onChange.called).toBeTruthy();
  201. });
  202. it(`test range picker`, async () => {
  203. const open = true;
  204. const motion = false;
  205. const type = 'dateTimeRange';
  206. const needConfirm = false;
  207. const dayOffset = 3;
  208. const leftPrevClickTimes = 3;
  209. const currentValue = [new Date(baseDate), new Date(baseDate).setDate(baseDay + dayOffset)];
  210. const demo = mount(
  211. <DatePicker
  212. motion={motion}
  213. defaultOpen={open}
  214. defaultValue={currentValue}
  215. type={type}
  216. needConfirm={needConfirm}
  217. />
  218. );
  219. const elem = demo.find(BaseDatePicker);
  220. const startDayDom = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-day-selected-start`);
  221. const endDayDom = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-day-selected-end`);
  222. /**
  223. * check started day and ended day's gap offset
  224. */
  225. expect(_.times(dayOffset).reduce(cur => cur.nextElementSibling, startDayDom)).toBe(endDayDom);
  226. const leftPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-left`);
  227. const leftNavBtns = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-navigation .${BASE_CLASS_PREFIX}-button`);
  228. const rightPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-right`);
  229. const rightNavBtns = rightPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-navigation .${BASE_CLASS_PREFIX}-button`);
  230. _.get(rightNavBtns, 1).click();
  231. await sleep();
  232. _.times(leftPrevClickTimes).forEach(() => _.first(leftNavBtns).click());
  233. const leftSecondWeek = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[1];
  234. const leftSecondWeekDays = leftSecondWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  235. const startIndex = 0;
  236. /**
  237. * select 2019-06-02 ~ 2019-06-05
  238. */
  239. demo.find('input').at(0).simulate('focus');
  240. leftSecondWeekDays[startIndex].click();
  241. demo.find('input').at(1).simulate('focus');
  242. leftSecondWeekDays[startIndex + dayOffset].click();
  243. const value = elem.state('value');
  244. const startDay = 2;
  245. expect(value[0].getMonth()).toBe(baseMon - leftPrevClickTimes);
  246. expect(value[0].getDate()).toBe(startDay);
  247. expect(value[1].getMonth()).toBe(baseMon - leftPrevClickTimes);
  248. expect(value[1].getDate()).toBe(startDay + dayOffset);
  249. });
  250. it(`test change panel in range picker`, async () => {
  251. const motion = false;
  252. const type = 'dateRange';
  253. const needConfirm = false;
  254. const dayOffset = 3;
  255. const currentValue = [new Date(baseDate), new Date(baseDate).setDate(baseDay + dayOffset)];
  256. const demo = mount(
  257. <DatePicker
  258. motion={motion}
  259. defaultOpen={open}
  260. defaultValue={currentValue}
  261. type={type}
  262. needConfirm={needConfirm}
  263. />
  264. );
  265. const elem = demo.find(BaseDatePicker);
  266. const leftPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-left`);
  267. const leftSecondWeek = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[1];
  268. const leftSecondWeekDays = leftSecondWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  269. const startIndex = 0;
  270. demo.find('input').at(0).simulate('focus');
  271. leftSecondWeekDays[startIndex].click();
  272. await sleep();
  273. expect(elem.state('rangeInputFocus')).toBe('rangeEnd');
  274. expect(elem.instance().focusRecordsRef.current.rangeStart).toBe(true);
  275. leftSecondWeekDays[startIndex + dayOffset].click();
  276. await sleep();
  277. expect(elem.instance().focusRecordsRef.current.rangeStart).toBe(false);
  278. expect(elem.instance().focusRecordsRef.current.rangeEnd).toBe(false);
  279. expect(elem.state('rangeInputFocus')).toBe(false);
  280. });
  281. it(`test change panel in range picker with start greater than endTime`, async () => {
  282. const motion = false;
  283. const type = 'dateRange';
  284. const needConfirm = false;
  285. const dayOffset = 3;
  286. const currentValue = [new Date(baseDate), new Date(baseDate).setDate(baseDay + dayOffset)];
  287. const demo = mount(
  288. <DatePicker
  289. motion={motion}
  290. defaultOpen={open}
  291. defaultValue={currentValue}
  292. type={type}
  293. needConfirm={needConfirm}
  294. />
  295. );
  296. const elem = demo.find(BaseDatePicker);
  297. const leftPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-left`);
  298. const leftThirdWeek = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[2];
  299. const leftThirdWeekDays = leftThirdWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  300. const startIndex = 0;
  301. demo.find('input').at(0).simulate('focus');
  302. leftThirdWeekDays[startIndex].click();
  303. await sleep();
  304. expect(elem.state('rangeInputFocus')).toBe('rangeEnd');
  305. const inputValue = elem.state('inputValue');
  306. expect(inputValue.split('~')[1].trim()).toBe('');
  307. });
  308. /**
  309. * this test suite won't end up with result
  310. */
  311. it.skip(`test year or month picker`, async () => {
  312. const open = true;
  313. const motion = false;
  314. const type = 'month';
  315. const monOffset = 2;
  316. const yearOffset = 3;
  317. const currentValue = new Date(baseDate);
  318. const elem = mount(<DatePicker motion={motion} defaultOpen={open} defaultValue={currentValue} type={type} />);
  319. await sleep();
  320. const lists = document.querySelectorAll(`.${BASE_CLASS_PREFIX}-scrolllist-item-wheel`);
  321. /**
  322. * select year
  323. */
  324. const currentSelectedYear = lists[0].querySelector(`ul .${BASE_CLASS_PREFIX}-scrolllist-item-selected`);
  325. _.times(yearOffset)
  326. .reduce(cur => cur.nextElementSibling, currentSelectedYear)
  327. .click();
  328. /**
  329. * select month
  330. */
  331. const currentSelectedMon = lists[1].querySelector(`ul .${BASE_CLASS_PREFIX}-scrolllist-item-selected`);
  332. _.times(monOffset)
  333. .reduce(cur => cur.nextElementSibling, currentSelectedMon)
  334. .click();
  335. await sleep();
  336. const value = elem.state('value');
  337. expect(value[0].getYear()).toBe(baseYear + yearOffset);
  338. expect(value[0].getMonth()).toBe(baseMon + monOffset);
  339. });
  340. it('test week select', async () => {
  341. const demo = mount(
  342. <DatePicker
  343. type="dateRange"
  344. defaultOpen={open}
  345. weekStartsOn={1}
  346. startDateOffset={date => startOfWeek(date, { weekStartsOn: 1 })}
  347. endDateOffset={date => endOfWeek(date, { weekStartsOn: 1 })}
  348. />
  349. );
  350. const elem = demo.find(BaseDatePicker);
  351. const leftPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-left`);
  352. const leftSecondWeek = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[1];
  353. const leftSecondWeekDays = leftSecondWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  354. const startIndex = 3;
  355. /**
  356. * 点击当前月第二个星期的第四天
  357. */
  358. leftSecondWeekDays[startIndex].click();
  359. const now = new Date();
  360. const year = now.getFullYear();
  361. const month = now.getMonth();
  362. const monthFirstDay = add(new Date(year, 0, 1, 0, 0, 0), { months: month });
  363. const clickDay = addDays(startOfWeek(addWeeks(monthFirstDay, 1), { weekStartsOn: 1 }), 3);
  364. const value = elem.state('value');
  365. const dateFormat = 'yyyy-MM-dd';
  366. expect(format(value[0], dateFormat)).toBe(format(startOfWeek(clickDay, { weekStartsOn: 1 }), dateFormat));
  367. expect(format(value[1], dateFormat)).toBe(format(endOfWeek(clickDay, { weekStartsOn: 1 }), dateFormat));
  368. });
  369. it('test autoFocus', async () => {
  370. const motion = false;
  371. const elem = mount(<DatePicker motion={motion} autoFocus={true} />);
  372. const elem2 = mount(<DatePicker motion={motion} autoFocus={false} />);
  373. expect(elem.find(`.${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-input-wrapper-focus`).length).toBe(1);
  374. expect(elem2.find(`.${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-input-wrapper-focus`).length).toBe(0);
  375. });
  376. it('custom dropdownClassName & dropdownStyle', async () => {
  377. let props = {
  378. dropdownClassName: 'my-datePicker',
  379. dropdownStyle: {
  380. color: 'red',
  381. },
  382. defaultOpen: true,
  383. motion: false,
  384. };
  385. const elem = mount(<DatePicker {...props} />);
  386. expect(elem.exists('.my-datePicker')).toEqual(true);
  387. expect(elem.find('.my-datePicker')).toHaveStyle('color', 'red');
  388. });
  389. it('onClear', async () => {
  390. const onClear = sinon.spy();
  391. let props = {
  392. defaultOpen: true,
  393. motion: false,
  394. autoFocus: true,
  395. defaultValue: baseDate,
  396. showClear: true,
  397. onClear: onClear,
  398. };
  399. const elem = mount(<DatePicker {...props} />);
  400. const clearBtn = elem.find('.semi-input-clearbtn');
  401. clearBtn.simulate('mouseDown', { target: { value: 'test' } });
  402. expect(onClear.called).toBeTruthy();
  403. });
  404. it('input disabled date should not trigger onChange', async () => {
  405. const onChange = sinon.spy();
  406. const defaultValue = '2021-04-12';
  407. const disabeldDate = '2021-04-15';
  408. const notDisabledDate = '2021-04-13';
  409. let props = {
  410. defaultOpen: true,
  411. motion: false,
  412. value: defaultValue,
  413. onChange,
  414. disabledDate: dateStr => {
  415. const date = new Date(dateStr);
  416. const day = date.getDate();
  417. if (day === 15) {
  418. return true;
  419. }
  420. return false;
  421. }
  422. };
  423. const elem = mount(<DatePicker {...props} />);
  424. elem.find(`.${BASE_CLASS_PREFIX}-input-wrapper input`).simulate('change', { target: { value: disabeldDate } });
  425. await sleep();
  426. expect(onChange.called).toBeFalsy();
  427. elem.find(`.${BASE_CLASS_PREFIX}-input-wrapper input`).simulate('change', { target: { value: notDisabledDate } });
  428. await sleep();
  429. expect(onChange.called).toBeTruthy();
  430. });
  431. it('click presets disabled date should not trigger onChange', async () => {
  432. const onChange = sinon.spy();
  433. const defaultValue = '2021-04-12';
  434. const disabeldValue = '2021-04-15';
  435. const notDisabledValue = '2021-04-30';
  436. const defaultDate = new Date(defaultValue);
  437. const disableDate = new Date(disabeldValue);
  438. const notDisabeldDate = new Date(notDisabledValue);
  439. let props = {
  440. open: true,
  441. motion: false,
  442. defaultValue,
  443. onChange,
  444. disabledDate: dateStr => {
  445. const date = new Date(dateStr);
  446. const day = date.getDate();
  447. if (day === 15) {
  448. return true;
  449. }
  450. return false;
  451. },
  452. presets: [
  453. {
  454. text: 'disabled date',
  455. start: disableDate,
  456. },
  457. {
  458. text: 'valid date',
  459. start: notDisabledValue,
  460. },
  461. ],
  462. };
  463. const demo = mount(<DatePicker {...props} />);
  464. const elem = demo.find(BaseDatePicker);
  465. const btns = document.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-quick-control-item`);
  466. // click disabled date
  467. btns[0].click();
  468. let value = elem.state('value');
  469. expect(value[0].getDate()).toEqual(defaultDate.getDate());
  470. expect(onChange.called).toBeFalsy();
  471. // click valid date
  472. btns[1].click();
  473. await sleep();
  474. value = elem.state('value');
  475. expect(value[0].getDate()).toEqual(notDisabeldDate.getDate());
  476. expect(onChange.called).toBeTruthy();
  477. });
  478. it('check inputValue is correct when change timeZone', async () => {
  479. const today = set(new Date(), { hours: 22, minutes: 0, seconds: 0 });
  480. const [originZone, originOffset] = getRandomIANATimeZone();
  481. const [newZone, newOffset] = getRandomIANATimeZone();
  482. const elem = mount(
  483. <ConfigProvider timeZone={originZone}>
  484. <DatePicker type="dateTime" defaultOpen={true} motion={false} defaultPickerValue={today} />
  485. </ConfigProvider>
  486. );
  487. const demo = elem.find(BaseDatePicker);
  488. // 选中一个日期
  489. const days = document.querySelectorAll('.semi-datepicker-day');
  490. // 6 无实际意义,第一行的第7个肯定是有效日期,如第一行最后一天是月首
  491. days[6].click();
  492. await sleep();
  493. // 查看value值
  494. expect(elem.find('.semi-datepicker-day-selected')).toBeTruthy();
  495. const selectedDay = demo.state('value')[0];
  496. const input = document.querySelector('.semi-input');
  497. expect(input.value).toEqual(format(selectedDay, strings.FORMAT_DATE_TIME));
  498. // 切换时区
  499. elem.setProps({ timeZone: newZone });
  500. const newZoneDate = add(selectedDay, { hours: newOffset - originOffset })
  501. const formatNewZoneDate = format(newZoneDate, strings.FORMAT_DATE_TIME);
  502. expect(input.value).toEqual(formatNewZoneDate);
  503. });
  504. it('check inputValue in controlled mode when change timeZone', async () => {
  505. const now = new Date();
  506. const [originZone, originOffset] = getRandomIANATimeZone();
  507. const [newZone, newOffset] = getRandomIANATimeZone();
  508. // 给定一个时区下的date value
  509. const originZoneDate = zonedTimeToUtc(now, originZone);
  510. const elem = mount(
  511. <ConfigProvider timeZone={originZone}>
  512. <DatePicker type="dateTime" defaultOpen={true} motion={false} value={originZoneDate} />
  513. </ConfigProvider>
  514. );
  515. const input = document.querySelector('.semi-input');
  516. const originFormatDate = format(now, strings.FORMAT_DATE_TIME);
  517. expect(input.value).toEqual(originFormatDate);
  518. // 切换时区
  519. elem.setProps({ timeZone: newZone });
  520. const newZoneDate = add(now, { hours: newOffset - originOffset })
  521. const formatNewZoneDate = format(newZoneDate, strings.FORMAT_DATE_TIME);
  522. expect(input.value).toEqual(formatNewZoneDate);
  523. });
  524. it(`test locale format default`, () => {
  525. const localeFormatten = 'yyyy-MM-dd EEEE';
  526. const defaultValue = new Date('2021-04-30');
  527. const localeValue = format(defaultValue, localeFormatten, { locale: zhCN })
  528. // 默认为中文
  529. const elem = mount(<DatePicker format={localeFormatten} defaultValue={defaultValue} />);
  530. expect(elem.find(`.${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-input`).instance().value).toBe(localeValue);
  531. });
  532. it(`test locale format enUS`, () => {
  533. const localeFormatten = 'yyyy-MM-dd EEEE';
  534. const defaultValue = new Date('2021-04-30');
  535. const localeValue = format(defaultValue, localeFormatten, { locale: enUS })
  536. // 英文
  537. const elem = mount(
  538. <LocaleProvider locale={en_US}>
  539. <DatePicker format={localeFormatten} defaultValue={defaultValue} />
  540. </LocaleProvider>
  541. );
  542. expect(elem.find(`.${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-input`).instance().value).toBe(localeValue);
  543. });
  544. it(`test onPresetClick`, async () => {
  545. const dayOffset = 1;
  546. const presets = [
  547. {
  548. text: 'Today',
  549. start: new Date(baseDate),
  550. end: new Date(baseDate),
  551. },
  552. {
  553. text: 'Next Day',
  554. start: addDays(new Date(baseDate), dayOffset),
  555. end: addDays(new Date(baseDate), dayOffset),
  556. },
  557. ];
  558. const defaultValue = new Date(addDays(new Date(baseDate), -dayOffset));
  559. const open = true;
  560. const motion = false;
  561. const handlePresetClick = sinon.spy();
  562. const demo = mount(<DatePicker onPresetClick={handlePresetClick} presets={presets} motion={motion} open={open} defaultValue={defaultValue} />);
  563. const elem = demo.find(BaseDatePicker);
  564. const btns = document.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-quick-control-item`);
  565. btns[0].click();
  566. btns[1].click();
  567. expect(handlePresetClick.calledTwice).toBeTruthy();
  568. const args0 = handlePresetClick.getCall(0).args;
  569. const args1 = handlePresetClick.getCall(1).args;
  570. expect(args0[0]).toEqual(presets[0]);
  571. expect(args0[1] instanceof Event).toBeTruthy;
  572. expect(args1[0]).toEqual(presets[1]);
  573. expect(args1[1] instanceof Event).toBeTruthy;
  574. });
  575. it(`test range type click one not trigger notifyChange`, async () => {
  576. const onChange = sinon.spy(async (date, str) => {
  577. elem.setProps({ value: date });
  578. });
  579. let props = {
  580. defaultOpen: true,
  581. motion: false,
  582. value: undefined,
  583. onChange,
  584. defaultPickerValue: '2021-08-13',
  585. type: 'dateRange'
  586. };
  587. const elem = mount(<DatePicker {...props} />);
  588. const leftPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-left`);
  589. const leftSecondWeek = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[1];
  590. const leftSecondWeekDays = leftSecondWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  591. const startIndex = 0;
  592. const endIndex = 2;
  593. elem.find('input').at(0).simulate('focus');
  594. leftSecondWeekDays[startIndex].click();
  595. expect(onChange.calledOnce).toBe(false);
  596. elem.find('input').at(1).simulate('focus');
  597. leftSecondWeekDays[endIndex].click();
  598. expect(onChange.calledOnce).toBe(true);
  599. const [rangeStart, rangeEnd] = onChange.getCall(0).args[0];
  600. const dateFormat = 'yyyy-MM-dd';
  601. expect(format(rangeStart, dateFormat)).toBe('2021-08-08');
  602. expect(format(rangeEnd, dateFormat)).toBe('2021-08-10');
  603. const inputs = elem.find(`.${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-input`);
  604. expect(inputs.at(0).instance().value).toBe('2021-08-08');
  605. expect(inputs.at(1).instance().value).toBe('2021-08-10');
  606. });
  607. /**
  608. * test disabled rangeStart and select a not disabled range end
  609. * e.g.
  610. * You can select a no disabled date(like one day of september) when defaultValue=['2021-08-06', '2021-08-15'] and disabled august.
  611. */
  612. it('test rangeStart disabled and select rangeEnd', async () => {
  613. const onChange = sinon.spy();
  614. const defaultValue = ['2021-08-06', '2021-08-15'];
  615. let props = {
  616. type: 'dateRange',
  617. defaultOpen: true,
  618. motion: false,
  619. defaultValue,
  620. onChange,
  621. // disabled august
  622. disabledDate: dateStr => {
  623. const date = new Date(dateStr);
  624. const month = date.getMonth();
  625. if (month === 7) {
  626. return true;
  627. }
  628. return false;
  629. },
  630. style: { width: 300 }
  631. };
  632. const elem = mount(<DatePicker {...props} />);
  633. const baseElem = elem.find(BaseDatePicker);
  634. const rightPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-right`);
  635. const rightSecondWeek = rightPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[1];
  636. const rightSecondWeekDays = rightSecondWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  637. /**
  638. * select 2021-09-10 as rangeEnd
  639. */
  640. elem.find('input').at(1).simulate('focus');
  641. const endIndex = 5; // 2021-09-10
  642. rightSecondWeekDays[endIndex].click();
  643. const value = baseElem.state('value');
  644. // test rangeEnd is selected
  645. expect(value[0].getMonth()).toBe(7);
  646. expect(value[0].getDate()).toBe(6);
  647. expect(value[1].getMonth()).toBe(8);
  648. expect(value[1].getDate()).toBe(10);
  649. // test input value is same with state value
  650. expect(elem.find('input').at(0).instance().value).toBe(defaultValue[0]);
  651. expect(elem.find('input').at(1).instance().value).toBe('2021-09-10');
  652. // test event is called
  653. expect(onChange.calledOnce).toBe(true);
  654. });
  655. /**
  656. * test disabled some day and select a no disabled day in multiple mode
  657. */
  658. it('test disabled multiple select', async () => {
  659. const onChange = sinon.spy();
  660. const defaultValue = ['2021-08-06', '2021-08-15'];
  661. let props = {
  662. type: 'date',
  663. multiple: true,
  664. defaultOpen: true,
  665. motion: false,
  666. defaultValue,
  667. onChange,
  668. // disabled august
  669. disabledDate: dateStr => {
  670. const date = new Date(dateStr);
  671. const day = date.getDate();
  672. if (day > 20 && day < 25) {
  673. return true;
  674. }
  675. return false;
  676. },
  677. style: { width: 300 }
  678. };
  679. const elem = mount(<DatePicker {...props} />);
  680. const baseElem = elem.find(BaseDatePicker);
  681. const leftPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-left`);
  682. const leftSecondWeek = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[1];
  683. const leftSecondWeekDays = leftSecondWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  684. /**
  685. * select 2021-08-10(not disabled)
  686. */
  687. leftSecondWeekDays[2].click();
  688. let value = baseElem.state('value');
  689. // test 2021-08-10 is selected
  690. expect(value.length).toBe(3);
  691. expect(value[2].getMonth()).toBe(7);
  692. expect(value[2].getDate()).toBe(10);
  693. // test input value is same with state value
  694. expect(elem.find('input').at(0).instance().value).toBe('2021-08-06,2021-08-15,2021-08-10');
  695. // test event is called
  696. expect(onChange.calledOnce).toBe(true);
  697. /**
  698. * select 2021-08-21(disabled)
  699. */
  700. const leftThirdWeek = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[2];
  701. const leftThirdWeekDays = leftThirdWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  702. leftThirdWeekDays[6].click();
  703. await sleep();
  704. value = baseElem.state('value');
  705. // test 2021-08-21 is not selected
  706. expect(value.length).toBe(3);
  707. expect(elem.find('input').at(0).instance().value).toBe('2021-08-06,2021-08-15,2021-08-10');
  708. expect(onChange.calledOnce).toBe(true); // still calledOnce
  709. });
  710. it('test disabled time callback', async () => {
  711. const disabledTime = sinon.spy((date, panelType) => {
  712. if (panelType === 'left') {
  713. return { disabledHours: () => [17, 18] };
  714. } else {
  715. return { disabledHours: () => [12, 13, 14, 15, 16, 17, 18] };
  716. }
  717. });
  718. let props = {
  719. type: 'dateTimeRange',
  720. defaultValue: ['2021-09-08', '2021-10-03'],
  721. defaultOpen: true,
  722. motion: false,
  723. disabledTime,
  724. style: { width: 400 },
  725. timePickerOpts: {
  726. scrollItemProps: { cycled: false }
  727. }
  728. };
  729. const elem = mount(<DatePicker {...props} />);
  730. elem.find('.semi-datepicker-month-grid-left .semi-datepicker-switch-time').simulate('click');
  731. const args = disabledTime.lastCall.args;
  732. expect(args[0].length).toBe(2);
  733. expect(args[1]).toBe('left');
  734. elem.setProps({ type: 'dateTime' });
  735. elem.update();
  736. elem.find('.semi-datepicker-month-grid-left .semi-datepicker-switch-time').simulate('click');
  737. const args2 = disabledTime.lastCall.args;
  738. expect(Array.isArray(args2[0])).toBe(false);
  739. expect(args2[1]).toBe('left');
  740. });
  741. it('test rangeSeparator', async () => {
  742. const rangeSeparator = '-'
  743. const defaultValue = ['2021-08-06', '2021-08-15'];
  744. let props = {
  745. type: 'dateRange',
  746. motion: false,
  747. defaultValue,
  748. style: { width: 300 },
  749. rangeSeparator,
  750. };
  751. const elem = mount(
  752. <div>
  753. <DatePicker {...props} />
  754. <DatePicker {...props} type="dateTimeRange" />
  755. </div>
  756. );
  757. const allSeparators = document.querySelectorAll('.semi-datepicker-range-input-separator');
  758. expect(allSeparators[0].textContent.trim()).toBe(rangeSeparator);
  759. expect(allSeparators[1].textContent.trim()).toBe(rangeSeparator);
  760. });
  761. });