datePicker.stories.jsx 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137
  1. import React, { useState, useRef, useMemo, useCallback } from 'react';
  2. import {
  3. addDays,
  4. addWeeks,
  5. addMonths,
  6. isBefore,
  7. startOfMonth,
  8. endOfMonth,
  9. parseISO,
  10. startOfWeek,
  11. endOfWeek,
  12. } from 'date-fns';
  13. import { Space, ConfigProvider, InputGroup, InputNumber, Form, withField, Button, RadioGroup, Radio } from '../../index';
  14. // stores
  15. import NeedConfirmDemo from './NeedConfirm';
  16. import RenderDate from './RenderDate';
  17. import RenderFullDate from './RenderFullDate';
  18. import DateOffset from './DateOffset';
  19. import AllTypesDemo from './AllTypes';
  20. import Callbacks from './Callbacks';
  21. import DatePicker from '../index';
  22. import ExceptionDemo from './ExceptionDemo';
  23. import ControlledDemo from './ControlledDemo';
  24. import DisabledDate from './DisabledDate';
  25. import CustomTrigger from './CustomTrigger';
  26. import OverPopover from './OverPopover';
  27. import OnChangeWithDateFirst from './OnChangeWithDateFirst';
  28. import Multiple from './Multiple';
  29. import Autofocus from './Autofocus';
  30. import CycledDatePicker from './Cycled';
  31. import AutoSwitchDate from './AutoSwitchDate';
  32. import TimepikcerOpts from './TimePickerOpts';
  33. import Density from './Density';
  34. import DatePickerSlot from './DatePickerSlot';
  35. import DatePickerTimeZone from './DatePickerTimeZone';
  36. import BetterRangePicker from './BetterRangePicker';
  37. import SyncSwitchMonth from './SyncSwitchMonth';
  38. import { Checkbox } from '../../checkbox';
  39. import Typography from '../../typography/typography';
  40. import { IconClose, IconChevronDown } from '@douyinfe/semi-icons';
  41. export * from './v2';
  42. import * as dateFns from 'date-fns';
  43. export default {
  44. title: 'DatePicker',
  45. parameters: {
  46. chromatic: { disableSnapshot: true },
  47. },
  48. }
  49. export {
  50. ControlledDemo,
  51. NeedConfirmDemo,
  52. ExceptionDemo,
  53. AllTypesDemo,
  54. Callbacks,
  55. DisabledDate,
  56. CustomTrigger,
  57. OverPopover,
  58. OnChangeWithDateFirst,
  59. RenderDate,
  60. RenderFullDate,
  61. Autofocus,
  62. DateOffset,
  63. CycledDatePicker,
  64. AutoSwitchDate,
  65. TimepikcerOpts,
  66. Density,
  67. DatePickerSlot,
  68. DatePickerTimeZone,
  69. BetterRangePicker,
  70. SyncSwitchMonth,
  71. }
  72. const demoDiv = {
  73. marginTop: '20px',
  74. marginLeft: '20px',
  75. };
  76. export const DatePickerDefault = () => (
  77. <div style={{...demoDiv, height: '100vh'}}>
  78. <span>datePicker施工现场</span>
  79. <DatePicker
  80. insetLabel={<span>日期</span>}
  81. onChange={(str, date) => console.log(str)}
  82. onOpenChange={status => console.log(status)}
  83. placeholder="请选择日期"
  84. />
  85. <br />
  86. <span>datePicker默认显示</span>
  87. <DatePicker />
  88. <br />
  89. <span>defaultValue: 2019-07-09</span>
  90. <DatePicker defaultValue="2019-07-09" />
  91. <br />
  92. <span>defaultValue: 1569888000000</span>
  93. <DatePicker
  94. defaultValue={1569888000000}
  95. onChange={(input, value) => console.log({ input, value })}
  96. />
  97. <br />
  98. <span>defaultValue: new Date('2019-07-07')</span>
  99. <DatePicker
  100. defaultValue={new Date('2019-07-07')}
  101. onOpenChange={isOpen => console.log(isOpen)}
  102. defaultOpen
  103. motion={false}
  104. />
  105. </div>
  106. );
  107. DatePickerDefault.parameters = {
  108. chromatic: {
  109. disableSnapshot: false,
  110. delay: 300
  111. }
  112. };
  113. export const DatePickerCallbacks = () => {
  114. const printArgs = (...args) => console.log(...args);
  115. return (
  116. <div style={demoDiv}>
  117. <span>datePicker施工现场</span>
  118. <DatePicker onOpenChange={printArgs} />
  119. <br />
  120. <span>defaultValue: new Date('2019-07-07')</span>
  121. <DatePicker defaultValue={new Date('2019-07-07')} />
  122. <br />
  123. <span>defaultValue: 2019-07-09</span>
  124. <DatePicker defaultValue="2019-07-09" />
  125. <br />
  126. <span>defaultValue: 1569888000000</span>
  127. <DatePicker
  128. defaultValue={1569888000000}
  129. onChange={(input, value) => console.log(input, value)}
  130. />
  131. </div>
  132. );
  133. };
  134. export const DatePickerMultiple = () => <Multiple />;
  135. export const DateRangePicker = () => (
  136. <div style={demoDiv}>
  137. <div>dateRangePicker</div>
  138. <DatePicker type="dateRange" insetLabel="结束日期" prefix="test" validateStatus="error" />
  139. <br />
  140. <div>small dateRangePicker</div>
  141. <DatePicker type="dateRange" size="small" disabled prefix="test" />
  142. <br />
  143. <div>large dateRangePicker</div>
  144. <DatePicker type="dateRange" size="large" />
  145. <br />
  146. <div>compact dateRangePicker</div>
  147. <DatePicker type="dateRange" density="compact" validateStatus="warning" />
  148. <br />
  149. <div>dateRangePicker with offset</div>
  150. <DatePicker
  151. type="dateRange"
  152. startDateOffset={date => startOfWeek(date, { weekStartsOn: 1 })}
  153. endDateOffset={date => endOfWeek(date, { weekStartsOn: 1 })}
  154. />
  155. <br />
  156. <div>defaultValue:07/01-08/02</div>
  157. <DatePicker type="dateRange" defaultValue={[new Date('2019-07-01'), new Date('2019-08-02')]} />
  158. </div>
  159. );
  160. DateRangePicker.parameters = {
  161. chromatic: { disableSnapshot: false },
  162. };
  163. const presets = [
  164. {
  165. text: 'Today',
  166. start: new Date(),
  167. end: new Date(),
  168. },
  169. {
  170. text: 'Tomorrow',
  171. start: addDays(new Date(), 1),
  172. end: addDays(new Date(), 1),
  173. },
  174. {
  175. text: 'Today After Tomorrow',
  176. start: addDays(new Date(), 2),
  177. end: addDays(new Date(), 2),
  178. },
  179. {
  180. text: 'Next Week',
  181. start: addWeeks(new Date(), 1),
  182. end: addWeeks(new Date(), 2),
  183. },
  184. {
  185. text: 'Next Month',
  186. start: startOfMonth(addMonths(new Date(), 1)),
  187. end: endOfMonth(addMonths(new Date(), 1)),
  188. },
  189. {
  190. text: 'Next Bimonthly',
  191. start: startOfMonth(addMonths(new Date(), 1)),
  192. end: endOfMonth(addMonths(new Date(), 2)),
  193. },
  194. {
  195. text: 'Next Quarter',
  196. start: startOfMonth(addMonths(new Date(), 3)),
  197. end: endOfMonth(addMonths(new Date(), 3)),
  198. },
  199. ];
  200. export const DatePickerWithPresets = () => {
  201. const onPresetClick = (item, e) => {
  202. console.log('preset click', item, e);
  203. };
  204. const [presetPosition, setPresetPosition] = useState('right');
  205. const [insetInput, setInsetInput] = useState(false);
  206. const [presetArr, setPresetArr] = useState(presets);
  207. const BottomSlot = function(props) {
  208. const { style } = props;
  209. return (
  210. <div style={{ padding: '12px 20px', ...style }}>
  211. <div strong style={{ color: 'var(--semi-color-text-2)' }}>
  212. 定版前请阅读
  213. </div>
  214. <div link={{ href: 'https://semi.design/', target: '_blank' }}>发版须知</div>
  215. </div>
  216. );
  217. };
  218. return (
  219. <div style={demoDiv}>
  220. <span>带快捷选择的DatePicker</span>
  221. <br/>
  222. <br/>
  223. <RadioGroup onChange={e=>setPresetPosition(e.target.value)} value={presetPosition} aria-label="选择快捷选择面板位置" name="preset-radio-group">
  224. <Radio value={'left'}>left</Radio>
  225. <Radio value={'right'}>right</Radio>
  226. <Radio value={'top'}>top</Radio>
  227. <Radio value={'bottom'}>bottom</Radio>
  228. </RadioGroup>
  229. <Checkbox value={insetInput} onChange={e=>setInsetInput(e.target.checked)}>insetInput</Checkbox>
  230. <Checkbox value={presetArr} onChange={e=>setPresetArr(e.target.checked ? [...presets, ...presets, ...presets]:presets)}>more presets</Checkbox>
  231. <br/>
  232. <div>type="date"</div>
  233. <DatePicker
  234. type="date"
  235. presets={presetArr}
  236. insetInput={insetInput}
  237. presetPosition={presetPosition}
  238. onPresetClick={onPresetClick}
  239. onChange={(...args) => console.log(...args)}
  240. />
  241. <br/>
  242. <br/>
  243. <div>type="dateTime"</div>
  244. <DatePicker
  245. type="dateTime"
  246. insetInput={insetInput}
  247. needConfirm
  248. bottomSlot={<BottomSlot/>}
  249. presetPosition={presetPosition}
  250. presets={presetArr.map(preset => ({
  251. text: preset.text,
  252. start: preset.start,
  253. }))}
  254. onPresetClick={onPresetClick}
  255. onChange={(...args) => console.log(...args)}
  256. />
  257. <br/>
  258. <br/>
  259. <div>type="dateRange"</div>
  260. <DatePicker
  261. type="dateRange"
  262. presets={presetArr}
  263. insetInput={insetInput}
  264. presetPosition={presetPosition}
  265. onPresetClick={onPresetClick}
  266. onChange={(...args) => console.log(...args)}
  267. />
  268. <br/>
  269. <br/>
  270. <div>type="dateTimeRange"</div>
  271. <DatePicker
  272. type="dateTimeRange"
  273. presets={presetArr}
  274. insetInput={insetInput}
  275. presetPosition={presetPosition}
  276. onPresetClick={onPresetClick}
  277. onChange={(...args) => console.log(...args)}
  278. />
  279. <br/>
  280. <br/>
  281. <div>type="month"</div>
  282. <DatePicker
  283. type="month"
  284. insetInput={insetInput}
  285. presetPosition={presetPosition}
  286. presets={presetArr.map(preset => ({
  287. text: preset.text,
  288. start: preset.start,
  289. }))}
  290. onPresetClick={onPresetClick}
  291. onChange={(...args) => console.log(...args)}
  292. />
  293. {/* <br/>
  294. <br/>
  295. <div>type="monthRange"</div>
  296. <DatePicker
  297. type="monthRange"
  298. insetInput={insetInput}
  299. presetPosition={presetPosition}
  300. presets={presetArr.map(preset => ({
  301. text: preset.text,
  302. start: preset.start,
  303. }))}
  304. onPresetClick={onPresetClick}
  305. onChange={(...args) => console.log(...args)}
  306. /> */}
  307. <br/>
  308. <br/>
  309. <div>type="date" density="compact"</div>
  310. <DatePicker
  311. type="date"
  312. presets={presetArr}
  313. insetInput={insetInput}
  314. density="compact"
  315. presetPosition={presetPosition}
  316. onPresetClick={onPresetClick}
  317. onChange={(...args) => console.log(...args)}
  318. />
  319. <br/>
  320. <br/>
  321. <div>type="dateTime" density="compact"</div>
  322. <DatePicker
  323. type="dateTime"
  324. insetInput={insetInput}
  325. needConfirm
  326. density="compact"
  327. presetPosition={presetPosition}
  328. presets={presetArr.map(preset => ({
  329. text: preset.text,
  330. start: preset.start,
  331. }))}
  332. onPresetClick={onPresetClick}
  333. onChange={(...args) => console.log(...args)}
  334. />
  335. <br/>
  336. <br/>
  337. <div>type="dateRange" density="compact"</div>
  338. <DatePicker
  339. type="dateRange"
  340. presets={presetArr}
  341. density="compact"
  342. insetInput={insetInput}
  343. presetPosition={presetPosition}
  344. onPresetClick={onPresetClick}
  345. onChange={(...args) => console.log(...args)}
  346. />
  347. <br/>
  348. <br/>
  349. <div>type="dateTimeRange" density="compact"</div>
  350. <DatePicker
  351. type="dateTimeRange"
  352. density="compact"
  353. presets={presetArr}
  354. insetInput={insetInput}
  355. presetPosition={presetPosition}
  356. onPresetClick={onPresetClick}
  357. onChange={(...args) => console.log(...args)}
  358. />
  359. <br/>
  360. <br/>
  361. <div>type="month" density="compact"</div>
  362. <DatePicker
  363. type="month"
  364. insetInput={insetInput}
  365. presetPosition={presetPosition}
  366. presets={presetArr.map(preset => ({
  367. text: preset.text,
  368. start: preset.start,
  369. }))}
  370. density="compact"
  371. onPresetClick={onPresetClick}
  372. onChange={(...args) => console.log(...args)}
  373. />
  374. </div>
  375. );
  376. };
  377. function isDisabled(dayStr) {
  378. return isBefore(new Date(dayStr), new Date());
  379. }
  380. export const DatePickerDisabledDate = () => (
  381. <div style={demoDiv}>
  382. <span>不可选日期</span>
  383. <DatePicker type="dateRange" presets={presets} disabledDate={isDisabled} />
  384. </div>
  385. );
  386. export const DateTimePicker = () => (
  387. <div style={demoDiv}>
  388. <span>dateTimePicker</span>
  389. <DatePicker
  390. defaultPickerValue={parseISO('2020-02-20 20:00:00')}
  391. type="dateTime"
  392. onChange={(...args) => console.log('onChange: ', ...args)}
  393. />
  394. </div>
  395. );
  396. export const DateTimeRangePicker = () => (
  397. <div style={demoDiv}>
  398. <span>dateTimeRangePicker</span>
  399. <DatePicker type="dateTimeRange" defaultPickerValue={parseISO('2020-02-20 20:00:00')} />
  400. <br />
  401. <span>dateTimeRangePicker</span>
  402. <DatePicker type="dateTimeRange" presets={presets} />
  403. <br />
  404. <span>dateTimeRangePicker - multiple</span>
  405. <DatePicker type="dateTimeRange" multiple />
  406. <br />
  407. </div>
  408. );
  409. export const YearPicker = () => (
  410. <>
  411. <div>
  412. <span>Year Picker</span>
  413. <DatePicker type="dateTimeRange" />
  414. </div>
  415. <div>
  416. <span>Year Picker</span>
  417. <DatePicker type="dateTimeRange" presets={presets} />
  418. </div>
  419. </>
  420. );
  421. export const MonthPicker = () => {
  422. const Demo = () => {
  423. const [controlledValue, setControlledValue] = useState('2019-09');
  424. const _setControlledValue = value => setControlledValue(value);
  425. return (
  426. <>
  427. <div>
  428. <span>MonthPicker</span>
  429. <DatePicker type="month" />
  430. </div>
  431. <div>
  432. <span>MonthPicker with presets</span>
  433. <DatePicker type="month" presets={presets} />
  434. </div>
  435. <div>
  436. <span>MonthPicker with disabledDate</span>
  437. <DatePicker
  438. type="month"
  439. disabledDate={str => {
  440. const date = new Date(str);
  441. if (str.length <= 4) {
  442. return date.getFullYear() < 2015;
  443. }
  444. return date.getMonth() + 1 < 10;
  445. }}
  446. />
  447. </div>
  448. <div>
  449. <span>MonthPicker with presets</span>
  450. <DatePicker type="month" presets={presets} />
  451. </div>
  452. <div>
  453. <span>MonthPicker with controlledValue</span>
  454. <DatePicker type="month" value={controlledValue} onChange={_setControlledValue} />
  455. </div>
  456. </>
  457. );
  458. };
  459. return <Demo />;
  460. };
  461. export const MonthRangePicker = () => {
  462. const { Text } = Typography;
  463. const formatToken = 'yyyy-MM';
  464. const [controlledValue, setControlledValue] = useState(['2023-03', '2023-04']);
  465. const [triggerValue, setTriggerValue] = useState();
  466. const _setControlledValue = value => setControlledValue(value);
  467. const onChange = useCallback(date => {
  468. setTriggerValue(date);
  469. }, []);
  470. const onClear = useCallback(e => {
  471. e && e.stopPropagation();
  472. setTriggerValue(null);
  473. }, []);
  474. const closeIcon = useMemo(() => {
  475. return triggerValue ? <IconClose onClick={onClear} /> : <IconChevronDown />;
  476. }, [triggerValue]);
  477. const triggerContent = (placeholder) => {
  478. if (Array.isArray(triggerValue) && triggerValue.length) {
  479. return `${dateFns.format(triggerValue[0], formatToken)} ~ ${dateFns.format(triggerValue[1], formatToken)}`;
  480. } else {
  481. return '请选择年月时间范围';
  482. }
  483. };
  484. const TopSlot = function(props) {
  485. const { style } = props;
  486. return (
  487. <Space style={{ padding: '12px 20px', ...style }}>
  488. <Text strong style={{ color: 'var(--semi-color-text-2)' }}>
  489. 请选择月份范围
  490. </Text>
  491. </Space>
  492. );
  493. };
  494. const BottomSlot = function(props) {
  495. const { style } = props;
  496. return (
  497. <Space style={{ padding: '12px 20px', ...style }}>
  498. <Text strong style={{ color: 'var(--semi-color-text-2)' }}>
  499. 定版前请阅读
  500. </Text>
  501. <Text link={{ href: 'https://semi.design/', target: '_blank' }}>发版须知</Text>
  502. </Space>
  503. );
  504. };
  505. const disabledDate = date => {
  506. const deadDate = new Date('2023/3/1 00:00:00');
  507. return date.getTime() < deadDate.getTime();
  508. };
  509. return (
  510. <>
  511. <div>
  512. <div>default</div>
  513. <DatePicker type="monthRange" />
  514. <br />
  515. <br />
  516. <div>rangeSeparator 与 placeholder</div>
  517. <DatePicker type="monthRange" rangeSeparator={'➡️'} placeholder={['开始', '结束']} insetLabel='月份范围'/>
  518. <br />
  519. <br />
  520. <div>受控</div>
  521. <DatePicker type="monthRange" bottomSlot={<BottomSlot />} topSlot={<TopSlot />} value={controlledValue} onChange={_setControlledValue}/>
  522. <br />
  523. <br />
  524. <div>insetInput ➕ format</div>
  525. <div data-cy="monthRange">
  526. <DatePicker type="monthRange" insetInput format={'yyyy年MM月'} rangeSeparator={'到'} defaultValue={['2023-03', '2023-04']} style={{ width: 400 }}/>
  527. </div>
  528. <br />
  529. <div>triggerRender</div>
  530. <DatePicker
  531. type="monthRange"
  532. value={triggerValue}
  533. onChange={onChange}
  534. triggerRender={({ placeholder }) => (
  535. <Button theme={'light'} icon={closeIcon} iconPosition='right'>
  536. {triggerContent(placeholder)}
  537. </Button>
  538. )}
  539. />
  540. <br />
  541. <br />
  542. <div>年月禁用:禁用2023年3月前的所有年月</div>
  543. <DatePicker type="monthRange" disabledDate={disabledDate}/>
  544. <br />
  545. <br />
  546. <div>validateStatus</div>
  547. <DatePicker type="monthRange" validateStatus='warning' />
  548. <br />
  549. <DatePicker type="monthRange" validateStatus='error' />
  550. </div>
  551. </>
  552. );
  553. };
  554. export const PropTypesAndDefaultProps = () => (
  555. <div>
  556. <article>
  557. <p>{JSON.stringify(Object.keys(DatePicker.propTypes))}</p>
  558. <p>{JSON.stringify(DatePicker.defaultProps)}</p>
  559. </article>
  560. </div>
  561. );
  562. export const InputReadOnly = () => <DatePicker inputReadOnly={true} />;
  563. export const DropdownClassNameDropdownStyle = () => (
  564. <div>
  565. <h4>fontSize: 16,dropdownClassName: 'my-datePicker'</h4>
  566. <DatePicker
  567. dropdownStyle={{ fontSize: 16 }}
  568. dropdownClassName="my-datePicker"
  569. onChange={(date, dateString) => console.log(dateString)}
  570. />
  571. </div>
  572. );
  573. export const CustomPlaceholder = () => (
  574. <Space wrap>
  575. <DatePicker placeholder="请选择日期" insetLabel="默认" />
  576. <DatePicker placeholder={undefined} insetLabel="undefined" />
  577. <DatePicker placeholder="" insetLabel="空字符串" />
  578. <DatePicker placeholder={false} insetLabel="false" />
  579. <DatePicker placeholder={null} insetLabel="null" />
  580. <DatePicker placeholder="" type="dateRange" insetLabel="空字符串" />
  581. </Space>
  582. );
  583. CustomPlaceholder.parameters = {
  584. chromatic: { disableSnapshot: false },
  585. };
  586. export const FixNotifyChange = () => {
  587. function Demo() {
  588. const [tz, setTz] = useState(0);
  589. const [value, setVal] = useState();
  590. const [value2, setVal2] = useState();
  591. const [value3, setVal3] = useState();
  592. const [value4, setVal4] = useState();
  593. const withLog = fn => {
  594. return val => {
  595. console.log('notifyChange', val);
  596. fn(val);
  597. };
  598. };
  599. return (
  600. <ConfigProvider timeZone={tz}>
  601. <InputGroup>
  602. <InputNumber defaultValue={0} onChange={setTz} hideButtons />
  603. <DatePicker type="dateTimeRange" value={value} onChange={withLog(setVal)} />
  604. <DatePicker
  605. type="dateTimeRange"
  606. needConfirm
  607. value={value2}
  608. onConfirm={withLog(setVal2)}
  609. />
  610. <DatePicker type="date" value={value3} onChange={withLog(setVal3)} />
  611. <DatePicker type="dateRange" value={value4} onChange={withLog(setVal4)} />
  612. </InputGroup>
  613. </ConfigProvider>
  614. );
  615. }
  616. return <Demo />;
  617. };
  618. export const SelectNotDisabledDateV126 = () => {
  619. const defaultValue = ['2021-08-06', '2021-08-15'];
  620. const disabledMonth = dateStr => {
  621. const date = new Date(dateStr);
  622. const month = date.getMonth();
  623. if (month === 7) {
  624. return true;
  625. }
  626. return false;
  627. };
  628. const disabledDate = dateStr => {
  629. const date = new Date(dateStr);
  630. const day = date.getDate();
  631. if (day > 20 && day < 25) {
  632. return true;
  633. }
  634. return false;
  635. };
  636. let props = {
  637. type: 'dateRange',
  638. motion: false,
  639. defaultValue,
  640. onChange: (...args) => console.log('changed', ...args),
  641. style: { width: 300 },
  642. };
  643. return (
  644. <>
  645. <h4>dateRange type + disabled rangeStart and select rangeEnd</h4>
  646. <DatePicker {...props} disabledDate={disabledMonth} />
  647. <h4>date type + multiple select + given disabled defaultValue</h4>
  648. <DatePicker {...props} type="date" multiple disabledDate={disabledDate} />
  649. </>
  650. );
  651. };
  652. SelectNotDisabledDateV126.story = {
  653. name: 'select not disabled date(v1.26+)',
  654. };
  655. const CustomDatePicker = props => {
  656. const { fieldRef, ...rest } = props;
  657. return <DatePicker {...rest} ref={fieldRef} />;
  658. };
  659. const CustomFieldDatePicker = withField(CustomDatePicker);
  660. export const FixOnFocus = () => {
  661. function FocusDemo() {
  662. const [open1, setOpen1] = useState(false);
  663. const [open2, setOpen2] = useState(false);
  664. const [open3, setOpen3] = useState(false);
  665. const ref = useRef();
  666. const ref2 = useRef();
  667. const presets = [
  668. {
  669. text: 'Today',
  670. start: new Date(),
  671. end: new Date(),
  672. },
  673. {
  674. text: 'Tomorrow',
  675. start: new Date(new Date().valueOf() + 1000 * 3600 * 24),
  676. end: new Date(new Date().valueOf() + 1000 * 3600 * 24),
  677. },
  678. ];
  679. return (
  680. <>
  681. <DatePicker
  682. type="date"
  683. presets={presets}
  684. open={open1}
  685. onPresetClick={() => {
  686. setOpen1(false);
  687. }}
  688. onFocus={() => {
  689. console.log('focus');
  690. setOpen1(true);
  691. }}
  692. onBlur={() => {
  693. console.log('blur');
  694. }}
  695. style={{ width: 300 }}
  696. />
  697. <br />
  698. <br />
  699. <DatePicker
  700. type="dateTimeRange"
  701. presets={presets}
  702. open={open2}
  703. onPresetClick={() => {
  704. console.log('click presets', ref);
  705. setOpen2(false);
  706. setTimeout(() => {
  707. ref.current.foundation.closePanel();
  708. console.log(ref);
  709. }, 0);
  710. }}
  711. onFocus={() => {
  712. console.log('focus');
  713. setOpen2(true);
  714. }}
  715. onBlur={() => {
  716. console.log('blur');
  717. }}
  718. style={{ width: 500 }}
  719. ref={ref}
  720. />
  721. <Form>
  722. <CustomFieldDatePicker
  723. type="dateTimeRange"
  724. field="a"
  725. label="Form.DatePicker"
  726. presets={presets}
  727. open={open3}
  728. onPresetClick={() => {
  729. console.log('click presets', ref2);
  730. setOpen3(false);
  731. setTimeout(() => {
  732. ref2.current && ref2.current.foundation.closePanel();
  733. }, 0);
  734. }}
  735. onFocus={() => {
  736. console.log('focus');
  737. setOpen3(true);
  738. }}
  739. onBlur={() => {
  740. console.log('blur');
  741. }}
  742. style={{ width: 500 }}
  743. fieldRef={ref2}
  744. />
  745. </Form>
  746. </>
  747. );
  748. }
  749. return <FocusDemo />;
  750. };
  751. FixOnFocus.story = {
  752. name: 'fix onFocus',
  753. };
  754. export const FixDisabledTimeCallback1418 = () => {
  755. function Demo() {
  756. const disabledTime2 = (date, panelType) => {
  757. console.log('disabledTime callback parameter: ', date, panelType);
  758. if (panelType === 'left') {
  759. return { disabledHours: () => [17, 18] };
  760. } else {
  761. return { disabledHours: () => [12, 13, 14, 15, 16, 17, 18] };
  762. }
  763. };
  764. return (
  765. <>
  766. <strong>fix disabledTime callback parameter bug</strong>
  767. <DatePicker
  768. type="dateTimeRange"
  769. hideDisabledOptions={false}
  770. disabledTime={disabledTime2}
  771. defaultValue={['2021-09-08', '2021-10-03']}
  772. style={{ width: 400 }}
  773. />
  774. <DatePicker
  775. type="dateTime"
  776. hideDisabledOptions={false}
  777. defaultValue={'2021-09-08'}
  778. disabledTime={disabledTime2}
  779. style={{ width: 400 }}
  780. />
  781. </>
  782. );
  783. }
  784. return <Demo />;
  785. };
  786. FixDisabledTimeCallback1418.story = {
  787. name: 'fix disabledTime callback #1418',
  788. };
  789. export const RangeSeparator = () => (
  790. <Space wrap>
  791. <div>
  792. <div>custom rangeSeparator</div>
  793. <DatePicker
  794. type="dateRange"
  795. rangeSeparator="-"
  796. defaultValue={[new Date('2019-07-01'), new Date('2019-08-02')]}
  797. />
  798. <DatePicker
  799. type="dateTimeRange"
  800. rangeSeparator="-"
  801. defaultValue={[new Date('2019-07-01'), new Date('2019-08-02')]}
  802. />
  803. </div>
  804. <div>
  805. <div>default rangeSeparator</div>
  806. <DatePicker
  807. type="dateRange"
  808. defaultValue={[new Date('2019-07-01'), new Date('2019-08-02')]}
  809. />
  810. <DatePicker
  811. type="dateTimeRange"
  812. defaultValue={[new Date('2019-07-01'), new Date('2019-08-02')]}
  813. />
  814. </div>
  815. </Space>
  816. );
  817. /**
  818. * 修复输入 '20221-12-20' 类似这种年份的日期会崩溃问题
  819. * https://github.com/DouyinFE/semi-design/issues/422
  820. *
  821. * 非法日期的来源
  822. * - 用户输入
  823. * - 受控传入
  824. * @returns
  825. */
  826. export const FixParseISOBug = () => (
  827. <div>
  828. <label>
  829. <div>选择一个合法值,然后输入一个非法年份</div>
  830. <DatePicker defaultValue={'2021-12-20'} onChange={v => console.log('onChange', v)} />
  831. </label>
  832. <label>
  833. <div>defaultValue='20221-12-20'</div>
  834. <DatePicker defaultValue={'20221-12-20'} defaultOpen={true} motion={false} onChange={v => console.log('onChange', v)} />
  835. </label>
  836. </div>
  837. );
  838. FixParseISOBug.storyName = '修复 parseISO bug';
  839. FixParseISOBug.parameters = {
  840. chromatic: { disableSnapshot: false },
  841. };
  842. export const FixNeedConfirm = () => {
  843. const defaultDate = '2021-12-27 10:37:13';
  844. const defaultDateRange = ['2021-12-27 10:37:13', '2022-01-28 10:37:13' ];
  845. const props = {
  846. needConfirm: true,
  847. onConfirm: (...args) => {
  848. console.log('Confirmed: ', ...args);
  849. },
  850. onChange: (...args) => {
  851. console.log('Changed: ', ...args);
  852. },
  853. onCancel: (...args) => {
  854. console.log('Canceled: ', ...args);
  855. },
  856. };
  857. return (
  858. <div>
  859. <div data-cy="1">
  860. <span>dateTime + needConfirm + defaultValue</span>
  861. <div>
  862. <DatePicker
  863. type="dateTime"
  864. defaultValue={defaultDate}
  865. {...props}
  866. />
  867. </div>
  868. </div>
  869. <div data-cy="2">
  870. <span>dateTime + needConfirm</span>
  871. <div>
  872. <DatePicker
  873. type="dateTime"
  874. {...props}
  875. />
  876. </div>
  877. </div>
  878. <div data-cy="3">
  879. <span>dateTimeRange + needConfirm + defaultValue</span>
  880. <div>
  881. <DatePicker
  882. type="dateTimeRange"
  883. defaultValue={defaultDateRange}
  884. {...props}
  885. />
  886. </div>
  887. </div>
  888. <div data-cy="4">
  889. <span>dateTimeRange + needConfirm</span>
  890. <div>
  891. <DatePicker
  892. type="dateTimeRange"
  893. {...props}
  894. />
  895. </div>
  896. </div>
  897. </div>
  898. )
  899. }
  900. FixNeedConfirm.storyName = '修复 needConfirm 取消后输入框显示错误';
  901. /**
  902. * fix https://github.com/DouyinFE/semi-design/issues/388
  903. */
  904. export const FixPresetsClick = () => {
  905. const presets = [
  906. {
  907. text: '清空',
  908. start: '',
  909. end: '',
  910. },
  911. {
  912. text: 'Tomorrow',
  913. start: new Date(new Date().valueOf() + 1000 * 3600 * 24),
  914. end: new Date(new Date().valueOf() + 1000 * 3600 * 24),
  915. },
  916. ];
  917. const handleChange = v => {
  918. console.log('change', v);
  919. };
  920. const handleConfirm = v => {
  921. console.log('confirm', v);
  922. }
  923. return (
  924. <div>
  925. <div>
  926. <label>
  927. <span>不设置 needConfirm</span>
  928. <DatePicker onChange={console.log} type="dateRange" presets={presets} />
  929. </label>
  930. </div>
  931. <div>
  932. <label>
  933. <span>设置 needConfirm</span>
  934. <DatePicker needConfirm onChange={handleChange} onConfirm={handleConfirm} type="dateTimeRange" presets={presets} />
  935. </label>
  936. </div>
  937. </div>
  938. );
  939. };
  940. FixPresetsClick.storyName = '修复 presets 点击后不收起问题';
  941. /**
  942. * fix https://github.com/DouyinFE/semi-design/issues/410
  943. */
  944. export const FixTriggerRenderClosePanel = () => {
  945. const [value, setValue] = useState([]);
  946. const handleChange = v => {
  947. console.log('change', v);
  948. setValue(v);
  949. };
  950. const formatValue = (dates) => {
  951. const dateStrs = dates.map(v => String(v));
  952. return dateStrs.join(' ~ ');
  953. };
  954. const showClear = Array.isArray(value) && value.length > 1;
  955. return (
  956. <Space>
  957. <DatePicker
  958. value={value}
  959. type="dateRange"
  960. onChange={handleChange}
  961. motion={false}
  962. triggerRender={({ placeholder }) => (
  963. <Button>
  964. {(value && formatValue(value)) || placeholder}
  965. </Button>
  966. )}
  967. />
  968. {showClear && (
  969. <Button onClick={() => setValue([])}>清除</Button>
  970. )}
  971. </Space>
  972. );
  973. };
  974. FixTriggerRenderClosePanel.storyName = "fix triggerRender close bug"
  975. export const A11yKeyboardDemo = () => {
  976. const [value, setValue] = useState(new Date('2022-08-08 00:00'));
  977. const [rangeValue, setRangeValue] = useState([new Date('2022-08-08 00:00'), new Date('2022-08-09 12:00')]);
  978. const handleChange = v => {
  979. console.log('change', v);
  980. setValue(v);
  981. };
  982. const handleRangeChange = v => {
  983. console.log('change', v);
  984. setRangeValue(v);
  985. };
  986. return (
  987. <Space vertical align='start' data-cy="space">
  988. <div data-cy="dateRange">
  989. <DatePicker
  990. value={rangeValue}
  991. type="dateRange"
  992. onChange={handleRangeChange}
  993. showClear
  994. />
  995. </div>
  996. <div data-cy="date">
  997. <DatePicker
  998. onChange={handleChange}
  999. showClear
  1000. multiple
  1001. />
  1002. </div>
  1003. </Space>
  1004. );
  1005. };
  1006. A11yKeyboardDemo.storyName = "a11y keyboard demo";
  1007. /**
  1008. * test with cypress
  1009. */
  1010. export const NeedConfirmDelete = () => {
  1011. return (
  1012. <div data-cy="dateTimeRange">
  1013. <DatePicker
  1014. value={[new Date('2022-08-08 00:00'), new Date('2022-08-09 12:00')]}
  1015. type="dateTimeRange"
  1016. needConfirm
  1017. />
  1018. </div>
  1019. );
  1020. };
  1021. NeedConfirmDelete.storyName = "cashedSelectedValue return to last selected when needConfirm & input invalid";
  1022. /**
  1023. * test with cypress
  1024. */
  1025. export const CashedSelectedValue = () => {
  1026. return (
  1027. <Space>
  1028. <div data-cy="date">
  1029. <DatePicker
  1030. defaultValue={new Date('2022-08-08')}
  1031. type="date"
  1032. motion={false}
  1033. />
  1034. </div>
  1035. <div data-cy="dateTime">
  1036. <DatePicker
  1037. defaultValue={new Date('2022-08-08 19:11:00')}
  1038. type="dateTime"
  1039. motion={false}
  1040. />
  1041. </div>
  1042. <div data-cy="dateRange">
  1043. <DatePicker
  1044. defaultValue={[new Date('2022-08-08'), new Date('2022-08-09')]}
  1045. type="dateRange"
  1046. motion={false}
  1047. />
  1048. </div>
  1049. </Space>
  1050. );
  1051. };
  1052. CashedSelectedValue.storyName = "cashedSelectedValue";