datePicker.stories.jsx 31 KB

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