datePicker.test.js 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873
  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 disabledValue = '2021-04-15';
  435. const notDisabledValue = '2021-04-30';
  436. const defaultDate = new Date(`${defaultValue} 00:00:00`);
  437. const disableDate = new Date(`${disabledValue} 00:00:00`);
  438. const notDisabledDate = new Date(`${notDisabledValue} 00:00:00`);
  439. let props = {
  440. open: true,
  441. motion: false,
  442. defaultValue,
  443. onChange,
  444. disabledDate: date => {
  445. const day = date.getDate();
  446. if (day === 15) {
  447. return true;
  448. }
  449. return false;
  450. },
  451. presets: [
  452. {
  453. text: 'disabled date',
  454. start: disableDate,
  455. },
  456. {
  457. text: 'valid date',
  458. start: notDisabledValue,
  459. },
  460. ],
  461. };
  462. const demo = mount(<DatePicker {...props} />);
  463. const elem = demo.find(BaseDatePicker);
  464. const btns = document.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-quick-control-item`);
  465. // click disabled date
  466. btns[0].click();
  467. let value = elem.state('value');
  468. expect(value[0].getDate()).toEqual(defaultDate.getDate());
  469. expect(onChange.called).toBeFalsy();
  470. // click valid date
  471. btns[1].click();
  472. await sleep();
  473. value = elem.state('value');
  474. expect(value[0].getDate()).toEqual(notDisabledDate.getDate());
  475. expect(onChange.called).toBeTruthy();
  476. });
  477. it('check inputValue is correct when change timeZone', async () => {
  478. const today = set(new Date(), { hours: 22, minutes: 0, seconds: 0 });
  479. const [originZone, originOffset] = getRandomIANATimeZone();
  480. const [newZone, newOffset] = getRandomIANATimeZone();
  481. const elem = mount(
  482. <ConfigProvider timeZone={originZone}>
  483. <DatePicker type="dateTime" defaultOpen={true} motion={false} defaultPickerValue={today} />
  484. </ConfigProvider>
  485. );
  486. const demo = elem.find(BaseDatePicker);
  487. // 选中一个日期
  488. const days = document.querySelectorAll('.semi-datepicker-day');
  489. // 6 无实际意义,第一行的第7个肯定是有效日期,如第一行最后一天是月首
  490. days[6].click();
  491. await sleep();
  492. // 查看value值
  493. expect(elem.find('.semi-datepicker-day-selected')).toBeTruthy();
  494. const selectedDay = demo.state('value')[0];
  495. const input = document.querySelector('.semi-input');
  496. expect(input.value).toEqual(format(selectedDay, strings.FORMAT_DATE_TIME));
  497. // 切换时区
  498. elem.setProps({ timeZone: newZone });
  499. const newZoneDate = add(selectedDay, { hours: newOffset - originOffset })
  500. const formatNewZoneDate = format(newZoneDate, strings.FORMAT_DATE_TIME);
  501. expect(input.value).toEqual(formatNewZoneDate);
  502. });
  503. it('check inputValue in controlled mode when change timeZone', async () => {
  504. const now = new Date();
  505. const [originZone, originOffset] = getRandomIANATimeZone();
  506. const [newZone, newOffset] = getRandomIANATimeZone();
  507. // 给定一个时区下的date value
  508. const originZoneDate = zonedTimeToUtc(now, originZone);
  509. const elem = mount(
  510. <ConfigProvider timeZone={originZone}>
  511. <DatePicker type="dateTime" defaultOpen={true} motion={false} value={originZoneDate} />
  512. </ConfigProvider>
  513. );
  514. const input = document.querySelector('.semi-input');
  515. const originFormatDate = format(now, strings.FORMAT_DATE_TIME);
  516. expect(input.value).toEqual(originFormatDate);
  517. // 切换时区
  518. elem.setProps({ timeZone: newZone });
  519. const newZoneDate = add(now, { hours: newOffset - originOffset })
  520. const formatNewZoneDate = format(newZoneDate, strings.FORMAT_DATE_TIME);
  521. expect(input.value).toEqual(formatNewZoneDate);
  522. });
  523. it(`test locale format default`, () => {
  524. const localeFormatten = 'yyyy-MM-dd EEEE';
  525. const defaultValue = new Date('2021-04-30');
  526. const localeValue = format(defaultValue, localeFormatten, { locale: zhCN })
  527. // 默认为中文
  528. const elem = mount(<DatePicker format={localeFormatten} defaultValue={defaultValue} />);
  529. expect(elem.find(`.${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-input`).instance().value).toBe(localeValue);
  530. });
  531. it(`test locale format enUS`, () => {
  532. const localeFormatten = 'yyyy-MM-dd EEEE';
  533. const defaultValue = new Date('2021-04-30');
  534. const localeValue = format(defaultValue, localeFormatten, { locale: enUS })
  535. // 英文
  536. const elem = mount(
  537. <LocaleProvider locale={en_US}>
  538. <DatePicker format={localeFormatten} defaultValue={defaultValue} />
  539. </LocaleProvider>
  540. );
  541. expect(elem.find(`.${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-input`).instance().value).toBe(localeValue);
  542. });
  543. it(`test onPresetClick`, async () => {
  544. const dayOffset = 1;
  545. const presets = [
  546. {
  547. text: 'Today',
  548. start: new Date(baseDate),
  549. end: new Date(baseDate),
  550. },
  551. {
  552. text: 'Next Day',
  553. start: addDays(new Date(baseDate), dayOffset),
  554. end: addDays(new Date(baseDate), dayOffset),
  555. },
  556. ];
  557. const defaultValue = new Date(addDays(new Date(baseDate), -dayOffset));
  558. const open = true;
  559. const motion = false;
  560. const handlePresetClick = sinon.spy();
  561. const demo = mount(<DatePicker onPresetClick={handlePresetClick} presets={presets} motion={motion} open={open} defaultValue={defaultValue} />);
  562. const elem = demo.find(BaseDatePicker);
  563. const btns = document.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-quick-control-item`);
  564. btns[0].click();
  565. btns[1].click();
  566. expect(handlePresetClick.calledTwice).toBeTruthy();
  567. const args0 = handlePresetClick.getCall(0).args;
  568. const args1 = handlePresetClick.getCall(1).args;
  569. expect(args0[0]).toEqual(presets[0]);
  570. expect(args0[1] instanceof Event).toBeTruthy;
  571. expect(args1[0]).toEqual(presets[1]);
  572. expect(args1[1] instanceof Event).toBeTruthy;
  573. });
  574. it(`test range type click one not trigger notifyChange`, async () => {
  575. const onChange = sinon.spy(async (date, str) => {
  576. elem.setProps({ value: date });
  577. });
  578. let props = {
  579. defaultOpen: true,
  580. motion: false,
  581. value: undefined,
  582. onChange,
  583. defaultPickerValue: '2021-08-13',
  584. type: 'dateRange'
  585. };
  586. const elem = mount(<DatePicker {...props} />);
  587. const leftPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-left`);
  588. const leftSecondWeek = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[1];
  589. const leftSecondWeekDays = leftSecondWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  590. const startIndex = 0;
  591. const endIndex = 2;
  592. elem.find('input').at(0).simulate('focus');
  593. leftSecondWeekDays[startIndex].click();
  594. expect(onChange.calledOnce).toBe(false);
  595. elem.find('input').at(1).simulate('focus');
  596. leftSecondWeekDays[endIndex].click();
  597. expect(onChange.calledOnce).toBe(true);
  598. const [rangeStart, rangeEnd] = onChange.getCall(0).args[0];
  599. const dateFormat = 'yyyy-MM-dd';
  600. expect(format(rangeStart, dateFormat)).toBe('2021-08-08');
  601. expect(format(rangeEnd, dateFormat)).toBe('2021-08-10');
  602. const inputs = elem.find(`.${BASE_CLASS_PREFIX}-datepicker .${BASE_CLASS_PREFIX}-input`);
  603. expect(inputs.at(0).instance().value).toBe('2021-08-08');
  604. expect(inputs.at(1).instance().value).toBe('2021-08-10');
  605. });
  606. /**
  607. * test disabled rangeStart and select a not disabled range end
  608. * e.g.
  609. * You can select a no disabled date(like one day of september) when defaultValue=['2021-08-06', '2021-08-15'] and disabled august.
  610. */
  611. it('test rangeStart disabled and select rangeEnd', async () => {
  612. const onChange = sinon.spy();
  613. const defaultValue = ['2021-08-06', '2021-08-15'];
  614. let props = {
  615. type: 'dateRange',
  616. defaultOpen: true,
  617. motion: false,
  618. defaultValue,
  619. onChange,
  620. // disabled august
  621. disabledDate: dateStr => {
  622. const date = new Date(dateStr);
  623. const month = date.getMonth();
  624. if (month === 7) {
  625. return true;
  626. }
  627. return false;
  628. },
  629. style: { width: 300 }
  630. };
  631. const elem = mount(<DatePicker {...props} />);
  632. const baseElem = elem.find(BaseDatePicker);
  633. const rightPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-right`);
  634. const rightSecondWeek = rightPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[1];
  635. const rightSecondWeekDays = rightSecondWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  636. /**
  637. * select 2021-09-10 as rangeEnd
  638. */
  639. elem.find('input').at(1).simulate('focus');
  640. const endIndex = 5; // 2021-09-10
  641. rightSecondWeekDays[endIndex].click();
  642. const value = baseElem.state('value');
  643. // test rangeEnd is selected
  644. expect(value[0].getMonth()).toBe(7);
  645. expect(value[0].getDate()).toBe(6);
  646. expect(value[1].getMonth()).toBe(8);
  647. expect(value[1].getDate()).toBe(10);
  648. // test input value is same with state value
  649. expect(elem.find('input').at(0).instance().value).toBe(defaultValue[0]);
  650. expect(elem.find('input').at(1).instance().value).toBe('2021-09-10');
  651. // test event is called
  652. expect(onChange.calledOnce).toBe(true);
  653. });
  654. /**
  655. * test disabled some day and select a no disabled day in multiple mode
  656. */
  657. it('test disabled multiple select', async () => {
  658. const onChange = sinon.spy();
  659. const defaultValue = ['2021-08-06', '2021-08-15'];
  660. let props = {
  661. type: 'date',
  662. multiple: true,
  663. defaultOpen: true,
  664. motion: false,
  665. defaultValue,
  666. onChange,
  667. // disabled august
  668. disabledDate: dateStr => {
  669. const date = new Date(dateStr);
  670. const day = date.getDate();
  671. if (day > 20 && day < 25) {
  672. return true;
  673. }
  674. return false;
  675. },
  676. style: { width: 300 }
  677. };
  678. const elem = mount(<DatePicker {...props} />);
  679. const baseElem = elem.find(BaseDatePicker);
  680. const leftPanel = document.querySelector(`.${BASE_CLASS_PREFIX}-datepicker-month-grid-left`);
  681. const leftSecondWeek = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[1];
  682. const leftSecondWeekDays = leftSecondWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  683. /**
  684. * select 2021-08-10(not disabled)
  685. */
  686. leftSecondWeekDays[2].click();
  687. let value = baseElem.state('value');
  688. // test 2021-08-10 is selected
  689. expect(value.length).toBe(3);
  690. expect(value[2].getMonth()).toBe(7);
  691. expect(value[2].getDate()).toBe(10);
  692. // test input value is same with state value
  693. expect(elem.find('input').at(0).instance().value).toBe('2021-08-06,2021-08-15,2021-08-10');
  694. // test event is called
  695. expect(onChange.calledOnce).toBe(true);
  696. /**
  697. * select 2021-08-21(disabled)
  698. */
  699. const leftThirdWeek = leftPanel.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-week`)[2];
  700. const leftThirdWeekDays = leftThirdWeek.querySelectorAll(`.${BASE_CLASS_PREFIX}-datepicker-day`);
  701. leftThirdWeekDays[6].click();
  702. await sleep();
  703. value = baseElem.state('value');
  704. // test 2021-08-21 is not selected
  705. expect(value.length).toBe(3);
  706. expect(elem.find('input').at(0).instance().value).toBe('2021-08-06,2021-08-15,2021-08-10');
  707. expect(onChange.calledOnce).toBe(true); // still calledOnce
  708. });
  709. it('test disabled time callback', async () => {
  710. const disabledTime = sinon.spy((date, panelType) => {
  711. if (panelType === 'left') {
  712. return { disabledHours: () => [17, 18] };
  713. } else {
  714. return { disabledHours: () => [12, 13, 14, 15, 16, 17, 18] };
  715. }
  716. });
  717. let props = {
  718. type: 'dateTimeRange',
  719. defaultValue: ['2021-09-08', '2021-10-03'],
  720. defaultOpen: true,
  721. motion: false,
  722. disabledTime,
  723. style: { width: 400 },
  724. timePickerOpts: {
  725. scrollItemProps: { cycled: false }
  726. }
  727. };
  728. const elem = mount(<DatePicker {...props} />);
  729. elem.find('.semi-datepicker-month-grid-left .semi-datepicker-switch-time').simulate('click');
  730. const args = disabledTime.lastCall.args;
  731. expect(args[0].length).toBe(2);
  732. expect(args[1]).toBe('left');
  733. elem.setProps({ type: 'dateTime' });
  734. elem.update();
  735. elem.find('.semi-datepicker-month-grid-left .semi-datepicker-switch-time').simulate('click');
  736. const args2 = disabledTime.lastCall.args;
  737. expect(Array.isArray(args2[0])).toBe(false);
  738. expect(args2[1]).toBe('left');
  739. });
  740. it('test rangeSeparator', async () => {
  741. const rangeSeparator = '-'
  742. const defaultValue = ['2021-08-06', '2021-08-15'];
  743. let props = {
  744. type: 'dateRange',
  745. motion: false,
  746. defaultValue,
  747. style: { width: 300 },
  748. rangeSeparator,
  749. };
  750. const elem = mount(
  751. <div>
  752. <DatePicker {...props} />
  753. <DatePicker {...props} type="dateTimeRange" />
  754. </div>
  755. );
  756. const allSeparators = document.querySelectorAll('.semi-datepicker-range-input-separator');
  757. expect(allSeparators[0].textContent.trim()).toBe(rangeSeparator);
  758. expect(allSeparators[1].textContent.trim()).toBe(rangeSeparator);
  759. });
  760. });