datePicker.stories.jsx 31 KB

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