├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── _prev.tar.gz ├── arredemo.json ├── demo ├── README.md ├── index.html ├── index.js └── src │ ├── Demo.js │ ├── demo.scss │ ├── rdp.js │ └── samples │ ├── RDPBasic.js │ ├── RDPBasicDisabled.js │ ├── RDPBasicFocusBlur.js │ ├── RDPCustomClear.js │ ├── RDPCustomContainer.js │ ├── RDPCustomFormControl.js │ ├── RDPCustomFormat.js │ ├── RDPCustomInputGroup.js │ ├── RDPCustomPickMonth.js │ ├── RDPCustomPickMonthDefault.js │ ├── RDPCustomPrevNext.js │ ├── RDPCustomWeek.js │ ├── RDPPlacementBottom.js │ ├── RDPPlacementLeft.js │ ├── RDPPlacementRight.js │ ├── RDPPlacementTop.js │ ├── RDPSizeLarge.js │ ├── RDPSizeSmall.js │ ├── RDPValidityInvalid.js │ └── RDPValidityValid.js ├── logo ├── favicon │ └── reactstrap-date-picker.ico └── reactstrap-date-picker.png ├── package.json ├── patches ├── react-transition-group+4.4.5.patch └── reactstrap+9.2.3.patch ├── playground ├── check │ └── index.html ├── current │ └── index.html ├── future │ ├── future_build.sh │ ├── index.html │ └── package.json └── gh_issue.html ├── src ├── DatePicker.mjs ├── calendar │ ├── Calendar.mjs │ ├── CalendarBody.mjs │ ├── CalendarDayInMonth.mjs │ ├── CalendarDayOutOfMonth.mjs │ ├── CalendarFooter.mjs │ ├── CalendarHeader.mjs │ ├── CalendarSubHeader.mjs │ ├── CalendarWeekNum.mjs │ ├── pickmonth │ │ └── PickMonthDefault.mjs │ ├── useCalendarDays.mjs │ └── useCalendarProps.mjs ├── index.mjs ├── input │ ├── InputClearButton.mjs │ ├── InputControlInput.mjs │ ├── InputGroup.mjs │ ├── InputHidden.mjs │ ├── InputOverlay.mjs │ ├── useCustomEvents.mjs │ ├── useDayLabels.mjs │ ├── useInputIds.mjs │ └── useInputValues.mjs ├── props.tar.gz └── util │ ├── compareMonths.mjs │ ├── getDateFromIsoString.mjs │ ├── getInstanceCount.mjs │ ├── getIsoStringFromDate.mjs │ ├── getMaybeFuncValue.mjs │ ├── setTimeToNoon.mjs │ ├── useCheckProps.mjs │ └── useMaybeFuncProp.mjs ├── test ├── README.md ├── before.mjs ├── esm_pkg.cjs ├── tools │ ├── checks.cjs │ └── finders.cjs └── units │ ├── integrity │ ├── basic.cjs │ ├── calendar.cjs │ └── typing.cjs │ └── properties │ ├── calendar │ ├── calendarContainer.cjs │ ├── calendarPlacement.cjs │ ├── dayLabels.cjs │ ├── monthLabels.cjs │ ├── nextButtonElement.cjs │ ├── pickMonthElement.cjs │ ├── previousButtonElement.cjs │ ├── roundedCorners.cjs │ ├── showTodayButton.cjs │ └── weekStartsOn.cjs │ ├── events │ ├── changes.cjs │ └── focus.cjs │ ├── globals │ ├── clearButtonElement.cjs │ ├── dateFormat.cjs │ ├── defaultValue.cjs │ └── minMaxDate.cjs │ ├── input │ ├── (in)valid.cjs │ ├── autoFocus.cjs │ ├── className.cjs │ ├── customControl.cjs │ ├── disabled.cjs │ ├── inputRef.cjs │ └── style.cjs │ └── inputGroup │ └── customInputGroup.cjs └── xeira.json /.gitignore: -------------------------------------------------------------------------------- 1 | ### SublimeText ### 2 | *.sublime-workspace 3 | 4 | ### OSX ### 5 | .DS_Store 6 | .AppleDouble 7 | .LSOverride 8 | Icon 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear on external disk 14 | .Spotlight-V100 15 | .Trashes 16 | 17 | ### Windows ### 18 | # Windows image file caches 19 | Thumbs.db 20 | ehthumbs.db 21 | 22 | # Folder config file 23 | Desktop.ini 24 | 25 | # Recycle Bin used on file shares 26 | $RECYCLE.BIN/ 27 | 28 | # App specific 29 | coverage 30 | node_modules 31 | bower_components 32 | .tmp 33 | lib 34 | dist 35 | npm-debug.log* 36 | *.sublime-project 37 | package-lock.json 38 | TODO.lst 39 | .vscode 40 | test/bundle.js 41 | 42 | arredemo -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | test 3 | tests 4 | demo 5 | Procfile 6 | arredemo -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Changelog 3 | 4 | Originally based on [react-bootstrap-date-picker](https://github.com/pushtell/react-bootstrap-date-picker/), 5 | `reactstrap-date-picker` has evolved. From v1.0 it has been refactored, using `react` hooks, 6 | with cleaner code and an improved final performance. 7 | 8 | ## 2.0.0-beta.2 9 | 10 | * Upgraded `xeira` 11 | 12 | ## 2.0.0-beta.1 13 | 14 | * Upgraded versions to: 15 | - `react@^18.3.1` 16 | - `reactstrap@^9.2.3` 17 | - `bootstrap@^5.1.0` 18 | * Now using [`xeira`](https://github.com/afialapis/xeira) for bundling. Forced to rewrite tests using `React Testing Library`. 19 | * Removed `prop-types` usages. Also patched some `reactstrap` warning about it. 20 | * Removed `defaultProps` usages. Also patched some `reactstrap` warning about it. 21 | 22 | ## 1.0.11 23 | 24 | * fixed `calendarContainer` prop causes calendar to close unexpectedly 25 | 26 | ## 1.0.10 27 | 28 | * fixed `onClear` event: if passed, `onChange` is not fired 29 | 30 | ## 1.0.9 31 | 32 | * fixed blur handle when navigating months 33 | 34 | ## 1.0.8 35 | 36 | * fixed `inputRef` property to make it work properly when passing callback refs 37 | * keep Calendar open when clicking inside the control input 38 | * improve bad format handling on blur 39 | 40 | ## 1.0.6 41 | 42 | * cleaner readme 43 | 44 | ## 1.0.5 45 | 46 | * fix valid props on hidden input 47 | 48 | ## 1.0.4 49 | 50 | * fix warning on `prop-types` 51 | 52 | ## 1.0.3 53 | 54 | * Added [pickMonthElement](https://github.com/afialapis/reactstrap-date-picker/issues/22) 55 | 56 | ## 1.0.2 57 | 58 | * Fixed [issue #20: In/valid props doesn't apply the reactstrap in/valid css style](https://github.com/afialapis/reactstrap-date-picker/issues/20) 59 | 60 | ## 1.0.1 61 | 62 | * Fixed [issue #19: ReferenceError: Element is not defined](https://github.com/afialapis/reactstrap-date-picker/issues/19) 63 | 64 | ## 1.0.0 65 | 66 | * Introduction of `react` Hooks 67 | * Deep refactor of the source code 68 | * Supported versions: 69 | - `react` >= 16.13.1 70 | - `reactstrap` >= 8.5.1 71 | - `bootstrap` >= 4.5.2 72 | * Improved performance 73 | * Smaller bundle sizes 74 | 75 | ## 0.0.16 76 | 77 | * Version to use if you wanna go `reactstrap` 9 78 | * Supported versions: 79 | - `react` >= 14 80 | - `reactstrap` 9.0.1 81 | - `bootstrap` 5.1.3 82 | 83 | ## 0.0.12 84 | 85 | * Fixed [issue #15: placeholder will not fallback to dateFormat](https://github.com/afialapis/reactstrap-date-picker/issues/15) 86 | * Fixed [issue #16. do not allow keyboard input of dates out of minDate/maxDate](https://github.com/afialapis/reactstrap-date-picker/issues/16) 87 | * Supported versions: 88 | - `react` >= 14 89 | - `reactstrap` 8.5.1 90 | - `bootstrap` 4.5.2 91 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 afialapis.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # reactstrap-date-picker 4 | [![NPM Version](https://badge.fury.io/js/reactstrap-date-picker.svg)](https://www.npmjs.com/package/reactstrap-date-picker) 5 | [![NPM Downloads](https://img.shields.io/npm/dm/reactstrap-date-picker.svg?style=flat)](https://www.npmjs.com/package/reactstrap-date-picker) 6 | 7 | ![reactstrap-date-picker logo](https://reactstrap-date-picker.afialapis.com/assets/images/logo/reactstrap_date_picker_name.png) 8 | 9 | # Intro 10 | 11 | A Reactstrap based, zero dependencies, date picker. 12 | 13 | Demo and docs at [reactstrap-date-picker](https://reactstrap-date-picker.afialapis.com/). 14 | 15 | # Table of Contents 16 | 17 | 1. [Installation](#installation) 18 | 2. [Usage](#usage) 19 | 3. [API Reference](#api-reference) 20 | 4. [Deeper customizing](#deeper-customizing) 21 | 5. [Inspect this package](#inspect-this-package) 22 | 6. [Changelog](#changelog) 23 | 24 | 25 | # Installation 26 | 27 | Using `npm`: 28 | 29 | ```bash 30 | npm install reactstrap-date-picker 31 | ``` 32 | 33 | `reactstrap-date-picker` works with these [peer dependencies](https://nodejs.org/en/blog/npm/peer-dependencies/): 34 | * [React](https://github.com/facebook/react) ^18.3.1 35 | * [Reactstrap](https://github.com/reactstrap/reactstrap) ^9.2.3 36 | * [Bootstrap](https://github.com/twbs/bootstrap) ^5.1.0 37 | 38 | Check [Changelog](#changelog) for more info on other versions. 39 | 40 | 41 | # Usage 42 | 43 | ```js 44 | import React, {useState, useEffect} from 'react 45 | import {FormGroup, Label, FormText} from 'reactstrap' 46 | import {DatePicker} from 'reactstrap-date-picker' 47 | 48 | const App = () => { 49 | const [value, setValue]= useState(new Date().toISOString()) 50 | const [fmtValue, setFmtValue]= useState(undefined) 51 | 52 | handleChange(value, formattedValue) { 53 | setValue(value) 54 | setFmtValue(formattedValue) 55 | } 56 | 57 | useEffect(( )=> { 58 | console.log(`Formatted value is ${fmtValue}`) 59 | }, [fmtValue]) 60 | 61 | return ( 62 | 63 | 64 | handleChange(v, f)} /> 67 | Help 68 | 69 | ) 70 | } 71 | ``` 72 | 73 | 74 | # API Reference 75 | 76 | ## `` 77 | 78 | `reactstrap-date-picker`'s public component. 79 | 80 | ```js 81 | import {DatePicker} from 'reactstrap-date-picker' 82 | 83 | const Example = () => { 84 | ... 85 | return ( 86 | ... 87 | 88 | ... 89 | ) 90 | } 91 | 92 | ``` 93 | 94 | 95 | ## Global properties 96 | 97 |
98 | 99 | value, defaultValue, id, name, dateFormat, minDate, maxDate, showClearButton, clearButtonElement 100 | 101 |

102 | 103 | ### `value` 104 | 105 | ISO date string representing the current value. Cannot be set alongside `defaultValue`. 106 | 107 | * Optional 108 | * Type: `string`. 109 | * Example: `"2016-05-19T12:00:00.000Z"` 110 | 111 | ### `defaultValue` 112 | 113 | ISO date string representing the default value. Cannot be set alongside `value`. 114 | 115 | * Optional 116 | * Type: `string` 117 | * Example: `"2016-05-19T12:00:00.000Z"` 118 | 119 | ### `id` 120 | 121 | HTML identifier for the `reactstrap-date-picker`'s input (the hidden one). You may 122 | want to use it in case you need to traverse somehow the DOM. 123 | 124 | * Optional 125 | * Type: `string`. 126 | * Example: `"example-datepicker"` 127 | 128 | ### `name` 129 | 130 | HTML `name` attribute for the `reactstrap-date-picker`'s input (the hidden one). You may 131 | need to use it depending on how your handle your Forms. 132 | 133 | * Optional 134 | * Type: `string`. 135 | * Example: `"date-field"` 136 | 137 | ### `dateFormat` 138 | 139 | Date format. Any combination of DD, MM, YYYY and separator. 140 | 141 | * Optional 142 | * Type: `string` 143 | * Examples: `"MM/DD/YYYY"`, `"YYYY/MM/DD"`, `"MM-DD-YYYY"`, or `"DD MM YYYY"` 144 | 145 | ### `minDate` 146 | 147 | ISO date string to set the lowest allowable date value. 148 | 149 | * Optional 150 | * Type: `string` 151 | * Example: `"2016-05-19T12:00:00.000Z"` 152 | 153 | ### `maxDate` 154 | 155 | ISO date string to set the highest allowable date value. 156 | 157 | * Optional 158 | * Type: `string` 159 | * Example: `"2016-05-19T12:00:00.000Z"` 160 | 161 | ### `showClearButton` 162 | 163 | Toggles the visibility of the clearButton 164 | 165 | * Optional 166 | * Type: `bool` 167 | * Default: `false` 168 | 169 | ### `clearButtonElement` 170 | 171 | Character or component to use for the clear button. 172 | 173 | * Optional 174 | * Type: `string` or `ReactClass` 175 | * Default: `"×"` 176 |

177 |
178 | 179 | 180 | ## Input properties 181 |
182 | 183 | autoComplete, autoFocus, disabled, noValidate, placeholder, required, className, style, inputRef, customControl, children 184 | 185 |

186 | 187 | ### `autoComplete` 188 | 189 | Hint for form autofill feature. 190 | 191 | * Optional 192 | * Type: `string` 193 | * Default: `on` 194 | 195 | ### `autoFocus` 196 | 197 | Whether or not component starts with focus. 198 | 199 | * Optional 200 | * Type: `bool` 201 | * Default: `false` 202 | 203 | ### `disabled` 204 | 205 | Whether or not component is disabled. 206 | 207 | * Optional 208 | * Type: `bool` 209 | * Default: `false` 210 | 211 | ### `noValidate` 212 | 213 | When present, it specifies that the form-data (input) should not be validated when submitted. 214 | 215 | * Optional 216 | * Type: `bool` 217 | * Default: `false` 218 | 219 | ### `placeholder` 220 | 221 | Text that appears in the form control when it has no value set. 222 | 223 | * Optional 224 | * Type: `text` 225 | * Example: `John Doe` 226 | 227 | ### `required` 228 | 229 | `boolean`. A value is required or must be check for the form to be submittable 230 | 231 | * Optional 232 | * Type: `boolean` 233 | * Default: `false` 234 | 235 | ### `className` 236 | 237 | Class name passed to the Form Control input element. 238 | 239 | * Optional 240 | * Type: `string` 241 | * Example: `example-class` 242 | 243 | ### `style` 244 | 245 | Style object passed to the Form Control input element. 246 | 247 | * Optional 248 | * Type: `object` 249 | * Example: `{width: "100%"}` 250 | 251 | ### `inputRef` 252 | 253 | A React ref to the Form Control input element 254 | 255 | * Optional 256 | * Type: `ref` 257 | 258 | ### `customControl` 259 | 260 | Overwrite the default Form Control component with your own component. 261 | 262 | * Optional 263 | * Type: `React.Component` 264 | * Example: `` 265 | 266 | ### `children` 267 | 268 | `children` elements from the Form Control` 269 | 270 | * Optional 271 | * Type: `React.Component` 272 |

273 |
274 | 275 | 276 | ## Input Group properties 277 | 278 |
279 | 280 | size, valid, invalid, customInputGroup 281 | 282 |

283 | 284 | ### `size` 285 | 286 | Size of the input 287 | 288 | * Optional 289 | * Type: `string` 290 | * Examples: `lg`, `sm`, ... 291 | 292 | You can also override it completely and pass your own component: 293 | 294 | ### `valid` 295 | 296 | Applies the `is-valid` class when `true`, does nothing when `false` 297 | 298 | * Optional 299 | * Type: `bool` 300 | * Example: `true` 301 | 302 | ### `invalid` 303 | 304 | Applies the `is-invalid` class when `true`, does nothing when `false` 305 | 306 | * Optional 307 | * Type: `bool` 308 | * Example: `true` 309 | 310 | ### `customInputGroup` 311 | 312 | Overwrite the default InputGroup component with your own component. 313 | 314 | * Optional 315 | * Type: `React.Component` 316 | * Example: `` 317 |

318 |
319 | 320 | ## Calendar properties 321 | 322 |
323 | 324 | dayLabels, monthLabels, weekStartsOn, showWeeks, pickMonthElement, previousButtonElement, nextButtonElement, showTodayButton, todayButtonLabel, cellPadding, roundedCorners, calendarPlacement, calendarContainer 325 | 326 |

327 | 328 | ### `dayLabels` 329 | 330 | Array of day names to use in the calendar. Starting on Sunday. 331 | 332 | * Optional 333 | * Type: `array` 334 | * Default: `['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']` 335 | 336 | ### `monthLabels` 337 | 338 | Array of month names to use in the calendar. 339 | 340 | * Optional 341 | * Type: `array` 342 | * Default: `['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']` 343 | 344 | ### `weekStartsOn` 345 | 346 | Makes the calendar's week to start on a specified day. 0 = Sunday, 1 = Monday, etc. 347 | 348 | * Optional 349 | * Type: `number` 350 | * Example: `4` 351 | 352 | ### `showWeeks` 353 | 354 | Shows the number of the week in the calendar 355 | 356 | * Optional 357 | * Type: `bool` 358 | * Default: `false` 359 | 360 | ### `pickMonthElement` 361 | 362 | Optional component to use for the calendar's year and month pickers. 363 | 364 | * Optional 365 | * Type: `string` or `ReactClass` 366 | * Default: `undefined` 367 | 368 | `pickMonthElement = undefined` is the same as `pickMonthElement = "none"`. 369 | 370 | #### custom pickMonthElement 371 | 372 | You can pass a custom `React` component, which will receive these properties: 373 | - `displayDate` 374 | - `minDate` 375 | - `maxDate` 376 | - `onChangeMonth`: a callback receiving an `int` parameter (month number) 377 | - `onChangeYear`: a callback receiving an `int` parameter (year number) 378 | 379 | On the [demo](https://github.com/afialapis/reactstrap-date-picker/blob/master/demo/src/samples/RDPCustomPickMonth.js) 380 | you will find a simple custom element. 381 | 382 | 383 | #### `default` pickMonthElement 384 | 385 | There is a predefined component, consisting of two simple `select` elements, 386 | which can be used by passing `pickMonthElement = "default"`. 387 | 388 | It has a simple styling, which may not fit your needs. Maybe you can tweak it 389 | through the `css` classes used by `reactstrap-date-picker`: 390 | 391 | ```html 392 |

393 |
394 |
395 | 396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 | ``` 404 | 405 | 406 | ### `previousButtonElement` 407 | 408 | Character or component to use for the calendar's previous button. 409 | 410 | * Optional 411 | * Type: `string` or `ReactClass` 412 | * Default: `"<"` 413 | 414 | ### `nextButtonElement` 415 | 416 | Character or component to use for the calendar's next button. 417 | 418 | * Optional 419 | * Type: `string` or `ReactClass` 420 | * Default: `">"` 421 | 422 | ### `showTodayButton` 423 | 424 | Toggles the visibility of the today-button. 425 | 426 | * Optional 427 | * Type: `boolean` 428 | * Default: `false` 429 | 430 | ### `todayButtonLabel` 431 | 432 | Label for the today-button 433 | 434 | * Optional 435 | * Type: `string` 436 | * Default: `"Today"` 437 | 438 | ### `cellPadding` 439 | 440 | CSS padding value for calendar date cells. 441 | 442 | * Optional 443 | * Type: `string` 444 | * Default: `"5px"` 445 | 446 | ### `roundedCorners` 447 | 448 | CSS border-radius value for calendar date cells. 449 | 450 | * Optional 451 | * Type: `bool` 452 | * Default: `false` 453 | 454 | ### `calendarPlacement` 455 | 456 | Overlay placement for the popover calendar. 457 | 458 | * Optional 459 | * Type: `string` or `function` 460 | * Default: `"top"` 461 | 462 | ### `calendarContainer` 463 | 464 | Overlay container for the popover calendar. When placing the `reactstrap-date-picker` in a scrolling container, set this prop to some ancestor of the scrolling container. 465 | 466 | * Optional 467 | * Type: A DOM element, a string selector or a `ref` 468 | * Example: `document.body` 469 |

470 |
471 | 472 | 473 | ## Event properties 474 |
475 | 476 | onChange, onClear, onFocus, onBlur, onInvalid 477 | 478 |

479 | 480 | ### `onChange` 481 | 482 | Change callback function. 483 | 484 | * Optional 485 | * Type: `function` 486 | * Callback Arguments: 487 | * `value` : ISO date string representing the selected value. 488 | * Type: `String` 489 | * Example: `"2016-05-19T12:00:00.000Z"` 490 | * `formattedValue` : String representing the formatted value as defined by the `dateFormat` property. 491 | * Type: `String` 492 | * Example: `"05/19/2016"` 493 | 494 | ### `onClear` 495 | 496 | Defines what happens when clear button is clicked. 497 | 498 | * Optional 499 | * Type: `function` 500 | 501 | If passed, `onChange` event won't be fired when clicking clear button. This way, you will be able to customize 502 | the input behavior, for example: 503 | 504 | ```jsx 505 | { 507 | const today= new Date() 508 | setValue(today.toISOString()) 509 | }} 510 | /> 511 | ``` 512 | 513 | ### `onFocus` 514 | 515 | Focus callback function. 516 | 517 | * Optional 518 | * Type: `function` 519 | * Callback Arguments: 520 | * `event` : Focus event. 521 | * Type: `Event` 522 | 523 | ### `onBlur` 524 | 525 | Blur callback function. 526 | 527 | * Optional 528 | * Type: `function` 529 | * Callback Arguments: 530 | * `event` : Blur event. 531 | * Type: `Event` 532 | 533 | ### `onInvalid` 534 | 535 | Defines what happens when input has not passed the form validation. 536 | 537 | * Optional 538 | * Type: `function` 539 |

540 |
541 | 542 | 543 | # Deeper customizing 544 | 545 |
546 | 547 | Customize styling directly trough CSS. 548 | 549 |

550 | 551 | You can also customize `reactstrap-date-picker` using `CSS`, trough element's `id` or `class` attributes. 552 | 553 | `reactstrap-date-picker` renders several elements, all contained within a [reactstrap InputGroup](https://reactstrap.github.io/?path=/docs/components-inputgroup--input-group). 554 | Such elements will have its unique `id` attribute, plus `reactstrap-date-picker` custom `class` names (prefixed by `rdp-*`). 555 | 556 | The rendered DOM structure seems like this: 557 | 558 | ```html 559 |

560 | 561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 | 571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 | 581 |
582 |
583 |
584 | 585 |
586 |
587 |
588 |
589 | 590 |
591 |
592 |
593 | ``` 594 | 595 | This `SUFFIX` is: 596 | 597 | - `props.name` 598 | 599 | - if `props.name` is not passed, then use `props.id` 600 | 601 | - if `props.id` is not passed, then take a global counter of active `reactstrap-date-picker` instances 602 | 603 | So, the idea is, depending on your needs: 604 | 605 | - if you don't need handle `id`s at all, `reactstrap-date-picker` will render unique `id` with no problem 606 | 607 | - if you need a basic `id` usage, for example accessing the `reactstrap-date-picker`'s value from the DOM, then 608 | you just have to pass `props.id` and get the value from the element with that `id` 609 | 610 | - if you will perform more complex operations, then use `props.name` or `props.id`, and pay attention to the 611 | previous DOM structure and the `SUFFIX` presences 612 |

613 |
614 | 615 | 616 | # Inspect this package 617 | 618 | ## Demo 619 | 620 | ```bash 621 | 622 | npm run demo 623 | 624 | ``` 625 | 626 | And visit [http://localhost:8003](http://localhost:8003) on your browser 627 | 628 | 629 | ## Running Tests 630 | 631 | ```bash 632 | 633 | npm run test 634 | 635 | ``` 636 | 637 | # Changelog 638 | 639 | See [changelog here](https://github.com/afialapis/reactstrap-date-picker/blob/master/CHANGELOG.md) 640 | -------------------------------------------------------------------------------- /_prev.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afialapis/reactstrap-date-picker/8be567d29eee1dfe43c80524e7463b6ca9ea721f/_prev.tar.gz -------------------------------------------------------------------------------- /arredemo.json: -------------------------------------------------------------------------------- 1 | { 2 | "theme": "default", 3 | "favicon": "logo/favicon/reactstrap-date-picker.ico", 4 | "logo": "logo/reactstrap-date-picker.png", 5 | "url": "https://reactstrap-date-picker.afialapis.com/", 6 | "company": "Afialapis", 7 | "company_url": "https://www.afialapis.com", 8 | "doc_versions": ["1.0.11", "2.0.0-beta.2"], 9 | "md": { 10 | "strip_details_tag": true, 11 | "keep_summary_content": false 12 | }, 13 | "demo_entry": "demo/src/Demo.js" 14 | } 15 | -------------------------------------------------------------------------------- /demo/README.md: -------------------------------------------------------------------------------- 1 | # Run this demo 2 | 3 | 1. Clone the [reactstrap-date-picker repository](https://github.com/afialapis/reactstrap-date-picker) 4 | 2. Run `npm install` to install dependencies 5 | 3. Run `npm run demo` 6 | 4. Open [http://localhost:3010](http://localhost:3010) in your browser 7 | 5. Play with it! -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | reactstrap-date-picker demo 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /demo/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {createRoot} from 'react-dom/client' 3 | 4 | import Demo from './src/Demo' 5 | 6 | const container = document.getElementById('content') 7 | const root = createRoot(container) 8 | root.render() -------------------------------------------------------------------------------- /demo/src/Demo.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Container, Row, Col } from 'reactstrap' 3 | import RDPBasic from './samples/RDPBasic' 4 | import RDPBasicDisabled from './samples/RDPBasicDisabled' 5 | import RDPCustomFormat from './samples/RDPCustomFormat' 6 | import RDPCustomClear from './samples/RDPCustomClear' 7 | import RDPCustomPrevNext from './samples/RDPCustomPrevNext' 8 | import RDPCustomPickMonth from './samples/RDPCustomPickMonth' 9 | import RDPCustomPickMonthDefault from './samples/RDPCustomPickMonthDefault' 10 | import RDPCustomWeek from './samples/RDPCustomWeek' 11 | import RDPFocusBlur from './samples/RDPBasicFocusBlur' 12 | import RDPSizeSmall from './samples/RDPSizeSmall' 13 | import RDPSizeLarge from './samples/RDPSizeLarge' 14 | import RDPPlacementTop from './samples/RDPPlacementTop' 15 | import RDPPlacementBottom from './samples/RDPPlacementBottom' 16 | import RDPPlacementLeft from './samples/RDPPlacementLeft' 17 | import RDPPlacementRight from './samples/RDPPlacementRight' 18 | import RDPCustomInputGroup from './samples/RDPCustomInputGroup' 19 | import RDPCustomFormControl from './samples/RDPCustomFormControl' 20 | import RDPCustomContainer from './samples/RDPCustomContainer' 21 | import RDPValidityValid from './samples/RDPValidityValid' 22 | import RDPValidityInvalid from './samples/RDPValidityInvalid' 23 | 24 | import '../../node_modules/bootstrap/dist/css/bootstrap.min.css' 25 | import './demo.scss' 26 | 27 | const Title = ({title}) => 28 | 29 | 30 |

31 | {title} 32 |

33 | 34 |
35 | 36 | const Pair = ({one, two}) => 37 | 38 | 39 | 40 | 41 | {one()} 42 | 43 | 44 | {two()} 45 | 46 | 47 | 48 | 49 | 50 | const Three = ({one, two, three}) => 51 | 52 | 53 | 54 | 55 | {one()} 56 | 57 | 58 | {two()} 59 | 60 | 61 | {three()} 62 | 63 | 64 | 65 | 66 | 67 | const Four = ({one, two, three, four}) => 68 | 69 | 70 | 71 | 72 | {one()} 73 | 74 | 75 | {two()} 76 | 77 | 78 | {three()} 79 | 80 | 81 | {four()} 82 | 83 | 84 | 85 | 86 | 87 | 88 | const Demo = () => { 89 | return ( 90 |
91 | 92 | 93 | 94 | 95 |

96 | Reactstrap Date Picker demo 97 |

98 | 99 |
100 | 101 | 102 | 103 | <Three one={RDPBasic} two={RDPBasicDisabled} three={RDPFocusBlur}/> 104 | 105 | <Title title="Placement"/> 106 | <Four one={RDPPlacementTop} two={RDPPlacementRight} three={RDPPlacementBottom} four={RDPPlacementLeft}/> 107 | 108 | <Title title="Sizing"/> 109 | <Pair one={RDPSizeSmall} two={RDPSizeLarge}/> 110 | 111 | 112 | <Title title="Validity"/> 113 | <Pair one={RDPValidityValid} two={RDPValidityInvalid}/> 114 | 115 | <Title title="Customize"/> 116 | <Pair one={RDPCustomFormat} two={RDPCustomWeek}/> 117 | <Four one={RDPCustomPickMonthDefault} two={RDPCustomPickMonth} three={RDPCustomPrevNext} four={RDPCustomClear}/> 118 | 119 | <Three one={RDPCustomInputGroup} two={RDPCustomFormControl} three={RDPCustomContainer}/> 120 | 121 | {/*<Pair one={RDPCustomInputGroup} two={RDPCustomFormControl}/>*/} 122 | 123 | </Container> 124 | </div> 125 | ); 126 | } 127 | 128 | 129 | export default Demo; -------------------------------------------------------------------------------- /demo/src/demo.scss: -------------------------------------------------------------------------------- 1 | h1 { 2 | text-align: center; 3 | margin-top: 2em; 4 | font-size: 2em; 5 | } 6 | 7 | h2 { 8 | font-size: 1.5em; 9 | font-style: italic; 10 | } 11 | 12 | 13 | body { 14 | padding-bottom: 50px; 15 | } 16 | .row { 17 | margin-bottom: 1em; 18 | } -------------------------------------------------------------------------------- /demo/src/rdp.js: -------------------------------------------------------------------------------- 1 | import {DatePicker} from '../../src' 2 | 3 | export {DatePicker} -------------------------------------------------------------------------------- /demo/src/samples/RDPBasic.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPBasic = () => { 10 | const ref = useRef() 11 | 12 | const inputName = 'reactstrap_date_picker_basic' 13 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 14 | const [fmtValue, setFmtValue] = useState("06/01/2019") 15 | 16 | 17 | //const [value, setValue] = useState("2017-07-21T00:00:00.000Z") 18 | //const [fmtValue, setFmtValue] = useState("07/21/2017") 19 | //const minDate = "2017-07-01T12:00:00.000Z" 20 | //const maxDate = "2017-07-31T12:00:00.000Z" 21 | 22 | const handleChange = (v, f) => { 23 | setValue(v) 24 | setFmtValue(f) 25 | 26 | // if (ref.current) { 27 | // //console.log(ref.current) 28 | // console.log(ref.current.getValue()) 29 | // console.log(ref.current.getFormattedValue()) 30 | // } 31 | } 32 | 33 | return ( 34 | <FormGroup> 35 | <Label for={inputName} 36 | className="valium-reactstrap-label"> 37 | {"Basic"} 38 | </Label> 39 | <DatePicker 40 | name = {inputName} 41 | ref = {ref} 42 | instanceCount= {1} 43 | value = {value} 44 | onChange = {handleChange} 45 | showTodayButton= {true} 46 | showWeeks={true} 47 | autoFocus={true} 48 | //minDate= {minDate} maxDate={maxDate} 49 | /> 50 | <FormText> 51 | {value 52 | ? `Selected date is: ${value} (formatted: ${fmtValue})` 53 | : 'No date selected'} 54 | </FormText> 55 | </FormGroup> 56 | ) 57 | } 58 | 59 | export default RDPBasic 60 | -------------------------------------------------------------------------------- /demo/src/samples/RDPBasicDisabled.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPBasicDisabled = () => { 10 | const inputName = 'reactstrap_date_picker_disabled' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | const [fmtValue, setFmtValue] = useState("06/01/2019") 13 | 14 | return ( 15 | <FormGroup> 16 | <Label for={inputName} 17 | className="valium-reactstrap-label"> 18 | {"Disabled"} 19 | </Label> 20 | <DatePicker 21 | name = {inputName} 22 | instanceCount= {2} 23 | value = {value} 24 | onChange = {(v, f) => {setValue(v); setFmtValue(f);}} 25 | disabled = {true} 26 | /> 27 | <FormText> 28 | {value 29 | ? `Selected date is: ${value} (formatted: ${fmtValue})` 30 | : 'No date selected'} 31 | </FormText> 32 | </FormGroup> 33 | ) 34 | } 35 | 36 | export default RDPBasicDisabled 37 | -------------------------------------------------------------------------------- /demo/src/samples/RDPBasicFocusBlur.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPFocusBlur = () => { 10 | const inputName = 'reactstrap_date_picker_focus' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | const [focused, setFocused] = useState(false) 13 | 14 | return ( 15 | <FormGroup> 16 | <Label for={inputName} 17 | className="valium-reactstrap-label"> 18 | {"Focus / Blur"} 19 | </Label> 20 | <DatePicker 21 | name = {inputName} 22 | instanceCount= {6} 23 | value = {value} 24 | onChange = {(v, _f) => setValue(v)} 25 | onBlur = {() => setFocused(false)} 26 | onFocus = {() => setFocused(true)} 27 | /> 28 | <FormText> 29 | {`Field is ${focused ? 'focused' : 'blurred'}`} 30 | </FormText> 31 | </FormGroup> 32 | ) 33 | } 34 | 35 | export default RDPFocusBlur 36 | -------------------------------------------------------------------------------- /demo/src/samples/RDPCustomClear.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const clearButtonElement = <div id="clear-button-element">Clear</div>; 10 | const RDPCustomClear = () => { 11 | const inputName = 'reactstrap_date_picker_custom_clear' 12 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 13 | 14 | return ( 15 | <FormGroup> 16 | <Label for={inputName} 17 | className="valium-reactstrap-label"> 18 | {"Custom Clear button"} 19 | </Label> 20 | <DatePicker 21 | name = {inputName} 22 | instanceCount= {3} 23 | value = {value} 24 | onChange = {(v, _f) => setValue(v)} 25 | clearButtonElement = {clearButtonElement} 26 | onClear = {() => { 27 | const today= new Date() 28 | setValue(today.toISOString()) 29 | }} 30 | /> 31 | <FormText> 32 | {`Custom text/elements can be rendered on Clear button. Current value is ${value}.`} 33 | </FormText> 34 | </FormGroup> 35 | ) 36 | } 37 | 38 | export default RDPCustomClear 39 | -------------------------------------------------------------------------------- /demo/src/samples/RDPCustomContainer.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPCustomContainer = () => { 10 | const inputName = 'reactstrap_date_picker_custom_form_control' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | const [fmtValue, setFmtValue] = useState("06/01/2019") 13 | 14 | return ( 15 | <FormGroup> 16 | <Label for={inputName} 17 | className="valium-reactstrap-label"> 18 | {"Custom Container"} 19 | </Label> 20 | <DatePicker 21 | name = {inputName} 22 | instanceCount= {1} 23 | value = {value} 24 | onChange = {(v, f) => {setValue(v); setFmtValue(f);}} 25 | calendarContainer = {document.body} 26 | /> 27 | <FormText> 28 | {value 29 | ? `Selected date is: ${value} (formatted: ${fmtValue})` 30 | : 'No date selected'} 31 | </FormText> 32 | </FormGroup> 33 | ) 34 | } 35 | 36 | export default RDPCustomContainer 37 | -------------------------------------------------------------------------------- /demo/src/samples/RDPCustomFormControl.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPCustomFormControl = () => { 10 | const inputName = 'reactstrap_date_picker_custom_form_control' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | const [fmtValue, setFmtValue] = useState("06/01/2019") 13 | 14 | return ( 15 | <FormGroup> 16 | <Label for={inputName} 17 | className="valium-reactstrap-label"> 18 | {"Custom Form Control"} 19 | </Label> 20 | <DatePicker 21 | name = {inputName} 22 | instanceCount= {1} 23 | value = {value} 24 | onChange = {(v, f) => {setValue(v); setFmtValue(f);}} 25 | 26 | customControl={ 27 | <input className="form-control cusom-form-control" style={{border: '5px solid aliceblue', borderRadius: '10px'}}> 28 | </input> 29 | } 30 | /> 31 | <FormText> 32 | {value 33 | ? `Selected date is: ${value} (formatted: ${fmtValue})` 34 | : 'No date selected'} 35 | </FormText> 36 | </FormGroup> 37 | ) 38 | } 39 | 40 | export default RDPCustomFormControl 41 | -------------------------------------------------------------------------------- /demo/src/samples/RDPCustomFormat.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const spanishDayLabels = ['Dom', 'Lu', 'Ma', 'Mi', 'Ju', 'Vi', 'Sab']; 10 | const spanishMonthLabels = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto', 'Septiembre', 'Octubre', 'Noviembre', 'Diciembre']; 11 | 12 | 13 | const RDPCustomFormat = () => { 14 | const inputName = 'reactstrap_date_picker_custom_format' 15 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 16 | return ( 17 | <FormGroup> 18 | <Label for={inputName} 19 | className="valium-reactstrap-label"> 20 | {"Custom Format and Labels"} 21 | </Label> 22 | <DatePicker 23 | name = {inputName} 24 | instanceCount= {4} 25 | value = {value} 26 | onChange = {(v, _f) => setValue(v)} 27 | dateFormat = {"DD/MM/YYYY"} 28 | dayLabels = {spanishDayLabels} 29 | monthLabels = {spanishMonthLabels} 30 | /> 31 | <FormText> 32 | {"Using ES labels and dateFormat='DD/MM/YYYY'"} 33 | </FormText> 34 | </FormGroup> 35 | ) 36 | } 37 | 38 | export default RDPCustomFormat 39 | -------------------------------------------------------------------------------- /demo/src/samples/RDPCustomInputGroup.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const CustomInputGroup = ({children}) => 10 | <div className="input-group cusom_input_group" style={{border: '5px solid aliceblue', borderRadius: '10px'}}> 11 | {children} 12 | </div> 13 | 14 | const RDPCustomInputGroup = () => { 15 | const inputName = 'reactstrap_date_picker_custom_input_group' 16 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 17 | const [fmtValue, setFmtValue] = useState("06/01/2019") 18 | 19 | return ( 20 | <FormGroup> 21 | <Label for={inputName} 22 | className="valium-reactstrap-label"> 23 | {"Custom Input Group"} 24 | </Label> 25 | <DatePicker 26 | id="xxx" 27 | name = {inputName} 28 | instanceCount= {1} 29 | value = {value} 30 | onChange = {(v, f) => {setValue(v); setFmtValue(f);}} 31 | customInputGroup = {<CustomInputGroup/>} 32 | /> 33 | <FormText> 34 | {value 35 | ? `Selected date is: ${value} (formatted: ${fmtValue})` 36 | : 'No date selected'} 37 | </FormText> 38 | </FormGroup> 39 | ) 40 | } 41 | 42 | export default RDPCustomInputGroup 43 | -------------------------------------------------------------------------------- /demo/src/samples/RDPCustomPickMonth.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label, 6 | Input 7 | } from 'reactstrap' 8 | import {DatePicker} from '../rdp' 9 | 10 | const MONTH_NAMES= ['January', 'February', 'March', 'April', 11 | 'May', 'June', 'July', 'August', 'September', 12 | 'October', 'November', 'December'] 13 | 14 | const YEARS = [2018, 2019, 2020, 2021, 2022, 2023, 2024] 15 | 16 | const PickMonthElement = ({displayDate, _minDate, _maxDate, onChangeMonth, onChangeYear}) => { 17 | const [month, setMonth]= useState((new Date(displayDate)).getMonth()) 18 | const [year, setYear]= useState((new Date(displayDate)).getFullYear()) 19 | 20 | useEffect(() => { 21 | setMonth((new Date(displayDate)).getMonth()) 22 | setYear((new Date(displayDate)).getFullYear()) 23 | }, [displayDate]) 24 | 25 | const handleChangeMonth = (ev) => { 26 | const m= ev.target.value 27 | setMonth(m) 28 | onChangeMonth(m) 29 | } 30 | 31 | const handleChangeYear = (ev) => { 32 | const y= ev.target.value 33 | setYear(y) 34 | onChangeYear(y) 35 | } 36 | 37 | return ( 38 | <div style={{display: 'flex', flexFlow: 'row', flexWrap: 'wrap', width: '100%'}}> 39 | <div style={{flex: '60%'}}> 40 | <Input 41 | type="select" 42 | value={month} 43 | onChange={handleChangeMonth}> 44 | { 45 | MONTH_NAMES.map((lmonth, lidx) => { 46 | return ( 47 | <option key={`month_${lidx}`} 48 | value={lidx}> 49 | {lmonth} 50 | </option> 51 | ) 52 | }) 53 | } 54 | </Input> 55 | </div> 56 | <div style={{flex: '40%'}}> 57 | <Input 58 | type="select" 59 | value={year} 60 | onChange={handleChangeYear}> 61 | { 62 | YEARS.map(lyear => { 63 | return ( 64 | <option key={`year${lyear}`} 65 | value={lyear}> 66 | {lyear} 67 | </option> 68 | ) 69 | }) 70 | } 71 | </Input> 72 | </div> 73 | </div> 74 | ) 75 | } 76 | 77 | const RDPCustomPickMonth = () => { 78 | const inputName = 'reactstrap_date_picker_custom_pick_month' 79 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 80 | 81 | return ( 82 | <FormGroup> 83 | <Label for={inputName} 84 | className="valium-reactstrap-label"> 85 | {"Custom pick Month/Year selectors"} 86 | </Label> 87 | <DatePicker 88 | name = {inputName} 89 | instanceCount= {3} 90 | value = {value} 91 | onChange = {(v, _f) => setValue(v)} 92 | pickMonthElement= {PickMonthElement} 93 | /> 94 | <FormText> 95 | {'Custom elements can be used as Month/Year picker'} 96 | </FormText> 97 | </FormGroup> 98 | ) 99 | } 100 | 101 | export default RDPCustomPickMonth 102 | -------------------------------------------------------------------------------- /demo/src/samples/RDPCustomPickMonthDefault.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPCustomPickMonthDefault = () => { 10 | const inputName = 'reactstrap_date_picker_custom_pick_month_def' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | 13 | return ( 14 | <FormGroup> 15 | <Label for={inputName} 16 | className="valium-reactstrap-label"> 17 | {"Default pick Month/Year selectors"} 18 | </Label> 19 | <DatePicker 20 | name = {inputName} 21 | instanceCount= {3} 22 | value = {value} 23 | minDate = "2015-01-01T00:00:00.000Z" 24 | maxDate = "2025-12-31T00:00:00.000Z" 25 | onChange = {(v, _f) => setValue(v)} 26 | pickMonthElement= {'default'} 27 | /> 28 | <FormText> 29 | {'reactstrap-date-picker provides a default Month/Year picker'} 30 | </FormText> 31 | </FormGroup> 32 | ) 33 | } 34 | 35 | export default RDPCustomPickMonthDefault 36 | -------------------------------------------------------------------------------- /demo/src/samples/RDPCustomPrevNext.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const previousButtonElement = <div id="previous-button-element">Prev</div>; 10 | const nextButtonElement = <div id="next-button-element">Next</div>; 11 | 12 | const RDPCustomElements = () => { 13 | const inputName = 'reactstrap_date_picker_custom_prev_next' 14 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 15 | 16 | return ( 17 | <FormGroup> 18 | <Label for={inputName} 19 | className="valium-reactstrap-label"> 20 | {"Custom Prev/Next buttons"} 21 | </Label> 22 | <DatePicker 23 | name = {inputName} 24 | instanceCount= {3} 25 | value = {value} 26 | onChange = {(v, _f) => setValue(v)} 27 | nextButtonElement = {nextButtonElement} 28 | previousButtonElement= {previousButtonElement} 29 | /> 30 | <FormText> 31 | {'Custom text/elements can be rendered on Prev/Next calendar buttons'} 32 | </FormText> 33 | </FormGroup> 34 | ) 35 | } 36 | 37 | export default RDPCustomElements 38 | -------------------------------------------------------------------------------- /demo/src/samples/RDPCustomWeek.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPBasic = () => { 10 | const inputName = 'reactstrap_date_picker_cweek' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | 13 | return ( 14 | <FormGroup> 15 | <Label for={inputName} 16 | className="valium-reactstrap-label"> 17 | {"Week starts on Monday"} 18 | </Label> 19 | <DatePicker 20 | name = {inputName} 21 | instanceCount= {5} 22 | value = {value} 23 | onChange = {(v, _f) => setValue(v)} 24 | weekStartsOn = {1} 25 | /> 26 | <FormText> 27 | {"Week starts on Monday, weekStartsOn=1"} 28 | </FormText> 29 | </FormGroup> 30 | ) 31 | } 32 | 33 | export default RDPBasic 34 | -------------------------------------------------------------------------------- /demo/src/samples/RDPPlacementBottom.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPPlacementBottom = () => { 10 | const inputName = 'reactstrap_date_picker_placement_bottom' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | 13 | return ( 14 | <FormGroup> 15 | <Label for={inputName} 16 | className="valium-reactstrap-label"> 17 | {"Bottom"} 18 | </Label> 19 | <DatePicker 20 | name = {inputName} 21 | instanceCount= {11} 22 | value = {value} 23 | onChange = {(v, _f) => setValue(v)} 24 | calendarPlacement = "bottom" 25 | /> 26 | <FormText> 27 | {"calendarPlacement='bottom'"} 28 | </FormText> 29 | </FormGroup> 30 | ) 31 | } 32 | 33 | export default RDPPlacementBottom 34 | -------------------------------------------------------------------------------- /demo/src/samples/RDPPlacementLeft.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPPlacementLeft = () => { 10 | const inputName = 'reactstrap_date_picker_placement_left' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | 13 | return ( 14 | <FormGroup> 15 | <Label for={inputName} 16 | className="valium-reactstrap-label"> 17 | {"Left"} 18 | </Label> 19 | <DatePicker 20 | name = {inputName} 21 | instanceCount= {12} 22 | value = {value} 23 | onChange = {(v, _f) => setValue(v)} 24 | calendarPlacement = "left" 25 | /> 26 | <FormText> 27 | {"calendarPlacement='left'"} 28 | </FormText> 29 | </FormGroup> 30 | ) 31 | } 32 | 33 | export default RDPPlacementLeft 34 | -------------------------------------------------------------------------------- /demo/src/samples/RDPPlacementRight.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPPlacementRight = () => { 10 | const inputName = 'reactstrap_date_picker_placement_right' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | 13 | return ( 14 | <FormGroup> 15 | <Label for={inputName} 16 | className="valium-reactstrap-label"> 17 | {"Right"} 18 | </Label> 19 | <DatePicker 20 | name = {inputName} 21 | instanceCount= {10} 22 | value = {value} 23 | onChange = {(v, _f) => setValue(v)} 24 | calendarPlacement = "right" 25 | /> 26 | <FormText> 27 | {"calendarPlacement='right'"} 28 | </FormText> 29 | </FormGroup> 30 | ) 31 | } 32 | 33 | export default RDPPlacementRight 34 | -------------------------------------------------------------------------------- /demo/src/samples/RDPPlacementTop.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPPlacementTop = () => { 10 | const inputName = 'reactstrap_date_picker_placement_top' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | 13 | return ( 14 | <FormGroup> 15 | <Label for={inputName} 16 | className="valium-reactstrap-label"> 17 | {"Top"} 18 | </Label> 19 | <DatePicker 20 | name = {inputName} 21 | instanceCount= {9} 22 | value = {value} 23 | onChange = {(v, _f) => setValue(v)} 24 | calendarPlacement = "top" 25 | /> 26 | <FormText> 27 | {"calendarPlacement='top'"} 28 | </FormText> 29 | </FormGroup> 30 | ) 31 | } 32 | 33 | export default RDPPlacementTop 34 | -------------------------------------------------------------------------------- /demo/src/samples/RDPSizeLarge.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPSizeLarge = () => { 10 | const inputName = 'reactstrap_date_picker_size-large' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | 13 | return ( 14 | <FormGroup> 15 | <Label for={inputName} 16 | className="valium-reactstrap-label"> 17 | {"Large"} 18 | </Label> 19 | <DatePicker 20 | name = {inputName} 21 | instanceCount= {8} 22 | value = {value} 23 | onChange = {(v, _f) => setValue(v)} 24 | size = "lg" 25 | /> 26 | <FormText> 27 | {"Large size input, size='lg'"} 28 | </FormText> 29 | </FormGroup> 30 | ) 31 | } 32 | 33 | export default RDPSizeLarge 34 | -------------------------------------------------------------------------------- /demo/src/samples/RDPSizeSmall.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPSizeSmall = () => { 10 | const inputName = 'reactstrap_date_picker_size-small' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | 13 | return ( 14 | <FormGroup> 15 | <Label for={inputName} 16 | className="valium-reactstrap-label"> 17 | {"Small"} 18 | </Label> 19 | <DatePicker 20 | name = {inputName} 21 | instanceCount= {7} 22 | value = {value} 23 | onChange = {(v, _f) => setValue(v)} 24 | size = "sm" 25 | /> 26 | <FormText> 27 | {"Small size input, size='sm'"} 28 | </FormText> 29 | </FormGroup> 30 | ) 31 | } 32 | 33 | export default RDPSizeSmall 34 | -------------------------------------------------------------------------------- /demo/src/samples/RDPValidityInvalid.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPValidityInvalid = () => { 10 | const inputName = 'reactstrap_date_picker_valid' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | const [fmtValue, setFmtValue] = useState("06/01/2019") 13 | 14 | return ( 15 | <FormGroup> 16 | <Label for={inputName} 17 | className="valium-reactstrap-label"> 18 | {"Invalid"} 19 | </Label> 20 | <DatePicker 21 | name = {inputName} 22 | instanceCount= {16} 23 | value = {value} 24 | onChange = {(v, f) => {setValue(v); setFmtValue(f);}} 25 | invalid = {true} 26 | /> 27 | <FormText> 28 | {value 29 | ? `Selected date is: ${value} (formatted: ${fmtValue})` 30 | : 'No date selected'} 31 | </FormText> 32 | </FormGroup> 33 | ) 34 | } 35 | 36 | export default RDPValidityInvalid 37 | -------------------------------------------------------------------------------- /demo/src/samples/RDPValidityValid.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { 3 | FormGroup, 4 | FormText, 5 | Label 6 | } from 'reactstrap' 7 | import {DatePicker} from '../rdp' 8 | 9 | const RDPValidityValid = () => { 10 | const inputName = 'reactstrap_date_picker_valid' 11 | const [value, setValue] = useState("2019-06-01T00:00:00.000Z") 12 | const [fmtValue, setFmtValue] = useState("06/01/2019") 13 | 14 | return ( 15 | <FormGroup> 16 | <Label for={inputName} 17 | className="valium-reactstrap-label"> 18 | {"Valid"} 19 | </Label> 20 | <DatePicker 21 | name = {inputName} 22 | instanceCount= {15} 23 | value = {value} 24 | onChange = {(v, f) => {setValue(v); setFmtValue(f);}} 25 | valid = {true} 26 | /> 27 | <FormText> 28 | {value 29 | ? `Selected date is: ${value} (formatted: ${fmtValue})` 30 | : 'No date selected'} 31 | </FormText> 32 | </FormGroup> 33 | ) 34 | } 35 | 36 | export default RDPValidityValid 37 | -------------------------------------------------------------------------------- /logo/favicon/reactstrap-date-picker.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afialapis/reactstrap-date-picker/8be567d29eee1dfe43c80524e7463b6ca9ea721f/logo/favicon/reactstrap-date-picker.ico -------------------------------------------------------------------------------- /logo/reactstrap-date-picker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afialapis/reactstrap-date-picker/8be567d29eee1dfe43c80524e7463b6ca9ea721f/logo/reactstrap-date-picker.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactstrap-date-picker", 3 | "version": "2.0.0-beta.4", 4 | "description": "Reactstrap based, zero dependencies, date picker", 5 | "author": "Donato Lorenzo <donato@afialapis.com>", 6 | "contributors": [ 7 | "Donato Lorenzo <donato@afialapis.com>" 8 | ], 9 | "license": "MIT", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/afialapis/reactstrap-date-picker.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/afialapis/reactstrap-date-picker/issues" 16 | }, 17 | "homepage": "https://reactstrap-date-picker.afialapis.com/", 18 | "files": [ 19 | "lib", 20 | "dist" 21 | ], 22 | "type": "module", 23 | "main": "./lib/index.cjs", 24 | "cjs": "./dist/reactstrap-date-picker.cjs", 25 | "browser": "./dist/reactstrap-date-picker.umd.js", 26 | "module": "./dist/reactstrap-date-picker.mjs", 27 | "exports": { 28 | "import": "./dist/reactstrap-date-picker.mjs", 29 | "default": "./lib/index.cjs", 30 | "require": "./dist/reactstrap-date-picker.cjs" 31 | }, 32 | "scripts": { 33 | "postinstall": "patch-package", 34 | "clean-demo": "rm -rf demo/dist && mkdir -p demo/dist/img && ln -s ./logo/favicon/reactstrap-date-picker.ico demo/dist/img/", 35 | "clean-lib": "rm -rf lib && mkdir lib", 36 | "clean-dist": "rm -rf dist && mkdir dist", 37 | "clean-site": "rm -rf arredemo", 38 | "clean-all": "npm run --silent clean-demo && npm run --silent clean-lib && npm run --silent clean-dist && npm run --silent clean-site", 39 | "lint": "npx xeira lint", 40 | "test": "npx xeira test --files=./test/before.mjs,./test/units/integrity/*.cjs,./test/units/properties/**/*.cjs", 41 | "lib": "npm run --silent clean-lib && npx xeira transpile", 42 | "dist": "npm run --silent clean-dist && npx xeira bundle", 43 | "site": "npm run --silent clean-site && npx xeira site", 44 | "prepare": "npm run --silent clean-demo && npm run --silent lint && npm run --silent test && npm run --silent lib && npm run --silent dist && npm run --silent site", 45 | "demo": "npm run --silent clean-demo && npx xeira demo --port=8003", 46 | "reset": "npm run --silent clean-all && rm -fr node_modules package-lock.json && npm i" 47 | }, 48 | "keywords": [ 49 | "js", 50 | "react", 51 | "reactstrap", 52 | "date", 53 | "picker" 54 | ], 55 | "devDependencies": { 56 | "xeira": "^1.0.1", 57 | "bootstrap": "^5.1.0", 58 | "react": "^18.3.1", 59 | "react-dom": "^18.3.1", 60 | "reactstrap": "^9.2.3" 61 | }, 62 | "peerDependencies": { 63 | "bootstrap": "^5.1.0", 64 | "react": "^18.3.1", 65 | "react-dom": "^18.3.1", 66 | "reactstrap": "^9.2.3" 67 | }, 68 | "overrides": { 69 | "css-select": { 70 | "nth-check": "2.0.1" 71 | } 72 | }, 73 | "eslintConfig": { 74 | "extends": [ 75 | "./node_modules/xeira/configs/eslint.react.cjs" 76 | ] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /patches/react-transition-group+4.4.5.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/react-transition-group/cjs/Transition.js b/node_modules/react-transition-group/cjs/Transition.js 2 | index b44cb76..c18d466 100644 3 | --- a/node_modules/react-transition-group/cjs/Transition.js 4 | +++ b/node_modules/react-transition-group/cjs/Transition.js 5 | @@ -540,7 +540,9 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? { 6 | */ 7 | timeout: function timeout(props) { 8 | var pt = _PropTypes.timeoutsShape; 9 | - if (!props.addEndListener) pt = pt.isRequired; 10 | + // reactstrap-date-picker patch 11 | + // for removing warnings about null prop 12 | + //if (!props.addEndListener) pt = pt.isRequired; 13 | 14 | for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 15 | args[_key - 1] = arguments[_key]; 16 | diff --git a/node_modules/react-transition-group/esm/Transition.js b/node_modules/react-transition-group/esm/Transition.js 17 | index 986d29f..d8d037b 100644 18 | --- a/node_modules/react-transition-group/esm/Transition.js 19 | +++ b/node_modules/react-transition-group/esm/Transition.js 20 | @@ -518,7 +518,10 @@ Transition.propTypes = process.env.NODE_ENV !== "production" ? { 21 | */ 22 | timeout: function timeout(props) { 23 | var pt = timeoutsShape; 24 | - if (!props.addEndListener) pt = pt.isRequired; 25 | + 26 | + // reactstrap-date-picker patch 27 | + // for removing warnings about null prop 28 | + //if (!props.addEndListener) pt = pt.isRequired; 29 | 30 | for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 31 | args[_key - 1] = arguments[_key]; 32 | -------------------------------------------------------------------------------- /patches/reactstrap+9.2.3.patch: -------------------------------------------------------------------------------- 1 | diff --git a/node_modules/reactstrap/esm/Popover.js b/node_modules/reactstrap/esm/Popover.js 2 | index 18f0fc4..5b38720 100644 3 | --- a/node_modules/reactstrap/esm/Popover.js 4 | +++ b/node_modules/reactstrap/esm/Popover.js 5 | @@ -2,22 +2,28 @@ function _extends() { _extends = Object.assign ? Object.assign.bind() : function 6 | import React from 'react'; 7 | import classNames from 'classnames'; 8 | import TooltipPopoverWrapper, { propTypes } from './TooltipPopoverWrapper'; 9 | + 10 | +// reactstrap-date-picker patch 11 | +// for removing warnings about defaultProps 12 | +const _DEFAULT_OFFSET = [0, 8]; 13 | + 14 | var defaultProps = { 15 | placement: 'right', 16 | placementPrefix: 'bs-popover', 17 | trigger: 'click', 18 | - offset: [0, 8] 19 | + offset: _DEFAULT_OFFSET 20 | }; 21 | function Popover(props) { 22 | var arrowClasses = classNames('popover-arrow', props.arrowClassName); 23 | var popperClasses = classNames('popover', 'show', props.popperClassName); 24 | var classes = classNames('popover-inner', props.innerClassName); 25 | - return /*#__PURE__*/React.createElement(TooltipPopoverWrapper, _extends({}, props, { 26 | + return /*#__PURE__*/React.createElement(TooltipPopoverWrapper, _extends({}, defaultProps, props, { 27 | arrowClassName: arrowClasses, 28 | popperClassName: popperClasses, 29 | innerClassName: classes 30 | })); 31 | } 32 | Popover.propTypes = propTypes; 33 | -Popover.defaultProps = defaultProps; 34 | +//Popover.defaultProps = defaultProps; 35 | + 36 | export default Popover; 37 | \ No newline at end of file 38 | diff --git a/node_modules/reactstrap/lib/Popover.js b/node_modules/reactstrap/lib/Popover.js 39 | index 318f966..20265e7 100644 40 | --- a/node_modules/reactstrap/lib/Popover.js 41 | +++ b/node_modules/reactstrap/lib/Popover.js 42 | @@ -11,23 +11,28 @@ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "functio 43 | function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } 44 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 45 | function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } 46 | + 47 | +// reactstrap-date-picker patch 48 | +// for removing warnings about defaultProps 49 | +const _DEFAULT_OFFSET = [0, 8]; 50 | + 51 | const defaultProps = { 52 | placement: 'right', 53 | placementPrefix: 'bs-popover', 54 | trigger: 'click', 55 | - offset: [0, 8] 56 | + offset: _DEFAULT_OFFSET 57 | }; 58 | function Popover(props) { 59 | const arrowClasses = (0, _classnames.default)('popover-arrow', props.arrowClassName); 60 | const popperClasses = (0, _classnames.default)('popover', 'show', props.popperClassName); 61 | const classes = (0, _classnames.default)('popover-inner', props.innerClassName); 62 | - return /*#__PURE__*/_react.default.createElement(_TooltipPopoverWrapper.default, _extends({}, props, { 63 | + return /*#__PURE__*/_react.default.createElement(_TooltipPopoverWrapper.default, _extends({}, defaultProps, props, { 64 | arrowClassName: arrowClasses, 65 | popperClassName: popperClasses, 66 | innerClassName: classes 67 | })); 68 | } 69 | Popover.propTypes = _TooltipPopoverWrapper.propTypes; 70 | -Popover.defaultProps = defaultProps; 71 | +//Popover.defaultProps = defaultProps; 72 | var _default = Popover; 73 | exports.default = _default; 74 | \ No newline at end of file 75 | -------------------------------------------------------------------------------- /playground/check/index.html: -------------------------------------------------------------------------------- 1 | <!DOCTYPE html> 2 | <html> 3 | <head> 4 | <!--[if lt IE 9]> 5 | <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.js"></script> 6 | <![endif]--> 7 | <meta charset="utf-8"/> 8 | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/> 9 | <title>reactstrap-date-picker playground (current) 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 28 | 29 | 30 | 33 |
34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /playground/current/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | reactstrap-date-picker playground (current) 10 | 11 | 12 | 13 | 14 | 15 | 24 | 25 | 26 | 29 |
30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /playground/future/future_build.sh: -------------------------------------------------------------------------------- 1 | rm -fr dist 2 | mkdir dist 3 | cd ../.. 4 | npm run clean-all 5 | mv package.json package.json.current 6 | cp playground/future/package.json . 7 | npm i 8 | cp ./dist/*umd* ./playground/future/dist 9 | 10 | mv package.json.current package.json 11 | npm run reset -------------------------------------------------------------------------------- /playground/future/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | reactstrap-date-picker playground (future) 10 | 11 | 12 | 13 | 14 | 15 | 24 | 25 | 26 | 29 |
30 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /playground/future/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactstrap-date-picker", 3 | "version": "0.0.16", 4 | "description": "Reactstrap based, zero dependencies, date picker", 5 | "author": "Donato Lorenzo ", 6 | "contributors": [ 7 | "Donato Lorenzo " 8 | ], 9 | "license": "MIT", 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/afialapis/reactstrap-date-picker.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/afialapis/reactstrap-date-picker/issues" 16 | }, 17 | "homepage": "https://reactstrap-date-picker.afialapis.com/", 18 | "files": [ 19 | "lib", 20 | "dist" 21 | ], 22 | "main": "lib/index.js", 23 | "cjs": "dist/reactstrap-date-picker.cjs.js", 24 | "browser": "dist/reactstrap-date-picker.umd.js", 25 | "module": "dist/reactstrap-date-picker.es.js", 26 | "scripts": { 27 | "lint": "eslint src", 28 | "clean-lib": "rm -rf lib && mkdir lib", 29 | "clean-dist": "rm -rf dist && mkdir dist", 30 | "test_command": "mocha --exit --timeout 500 --slow 300 --require @babel/register --require ./test/dom.js --require ignore-styles ./test/helpers.js ", 31 | "test_file": "npm run test_command --bail ", 32 | "test_r17": "npm run test_command --paralell $(find ./test -name '*.js' ! -path '**/_*.js')", 33 | "test": "npm run test_command --bail $(find ./test -name '*.js' ! -path '**/_*.js')", 34 | "lib": "npm run clean-lib && babel src -d lib", 35 | "dist": "npm run clean-dist && rollup -c", 36 | "prepare": "npm run lint && npm run test && npm run lib && npm run dist", 37 | "demo": "rm -rf demo/dist && mkdir demo/dist && rollup -c rollup.demo.js -w", 38 | "reset": "rm -rf demo/dist && npm run clean-lib && npm run clean-dist && rm -fr node_modules package-lock.json && npm i" 39 | }, 40 | "keywords": [ 41 | "js", 42 | "react", 43 | "reactstrap", 44 | "date", 45 | "picker" 46 | ], 47 | "devDependencies": { 48 | "@babel/cli": "^7.17.10", 49 | "@babel/core": "^7.18.2", 50 | "@babel/eslint-parser": "^7.18.2", 51 | "@babel/preset-env": "^7.18.2", 52 | "@babel/preset-react": "^7.17.12", 53 | "@babel/register": "^7.17.7", 54 | "@rollup/plugin-babel": "^5.3.1", 55 | "@rollup/plugin-commonjs": "^22.0.0", 56 | "@rollup/plugin-node-resolve": "^13.3.0", 57 | "@rollup/plugin-replace": "^4.0.0", 58 | "bootstrap": "^5.1.3", 59 | "chai": "^4.3.6", 60 | "enzyme": "^3.11.0", 61 | "enzyme-adapter-react-16": "^1.15.6", 62 | "eslint": "^8.17.0", 63 | "eslint-plugin-react": "^7.30.0", 64 | "eslint-plugin-react-hooks": "^4.5.0", 65 | "ignore-styles": "^5.0.1", 66 | "jsdom": "^19.0.0", 67 | "mocha": "^10.0.0", 68 | "node-sass": "^7.0.1", 69 | "node-uuid": "^1.4.8", 70 | "postcss": "^8.4.14", 71 | "react": "^16.13.1", 72 | "react-dom": "^16.13.1", 73 | "reactstrap": "9.0.1", 74 | "rollup": "^2.75.6", 75 | "rollup-plugin-livereload": "^2.0.5", 76 | "rollup-plugin-postcss": "^4.0.2", 77 | "rollup-plugin-serve": "^1.1.0", 78 | "rollup-plugin-terser": "^7.0.2" 79 | }, 80 | "peerDependencies": { 81 | "react": ">=16.13.1", 82 | "react-dom": ">=16.13.1", 83 | "reactstrap": ">=9.0.1" 84 | }, 85 | "overrides": { 86 | "enzyme": { 87 | "cheerio": "1.0.0-rc.3" 88 | } 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /playground/gh_issue.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | reactstrap 9 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/DatePicker.mjs: -------------------------------------------------------------------------------- 1 | import React, { useImperativeHandle, forwardRef } from 'react' 2 | 3 | import { useCheckProps } from './util/useCheckProps' 4 | import { InputGroup } from './input/InputGroup' 5 | import { InputOverlay } from './input/InputOverlay' 6 | import { InputHidden } from './input/InputHidden' 7 | import { InputClearButton } from './input/InputClearButton' 8 | import { InputControlInput } from './input/InputControlInput' 9 | import { useInputValues } from './input/useInputValues' 10 | import { useInputIds } from './input/useInputIds' 11 | import { useFixedDayLabels } from './input/useDayLabels' 12 | import { Calendar } from './calendar/Calendar' 13 | import { useCalendarProps } from './calendar/useCalendarProps' 14 | 15 | const _defaultDateFormat= () => { 16 | const language = typeof window !== 'undefined' && window.navigator ? (window.navigator.userLanguage || window.navigator.language || '').toLowerCase() : '' 17 | const dateFormat = !language || language === 'en-us' ? 'MM/DD/YYYY' : 'DD/MM/YYYY' 18 | return dateFormat 19 | } 20 | 21 | const DEFAULT_DAY_LABELS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'] 22 | const DEFAULT_MONTH_LABELS = ['January', 'February', 'March', 'April', 23 | 'May', 'June', 'July', 'August', 'September', 24 | 'October', 'November', 'December'] 25 | 26 | const DatePickerBase = (props, ref) => { 27 | 28 | const { 29 | // Global props 30 | value, 31 | defaultValue, 32 | id, 33 | name, 34 | dateFormat = _defaultDateFormat(), 35 | minDate, 36 | maxDate, 37 | clearButtonElement = '×', 38 | showClearButton = true, 39 | 40 | // Event props 41 | onInvalid, 42 | onChange, 43 | onClear, 44 | onBlur, 45 | onFocus, 46 | 47 | // Input Group props 48 | size, 49 | valid, 50 | invalid, 51 | customInputGroup, 52 | 53 | // Input props 54 | autoComplete = 'on', 55 | autoFocus = false, 56 | disabled = false, 57 | noValidate = false, 58 | placeholder, 59 | required, 60 | className, 61 | style = undefined /* { 62 | width: '100%' 63 | } */, 64 | inputRef, 65 | customControl, 66 | children, 67 | 68 | // Calendar props 69 | // target, 70 | calendarContainer, 71 | 72 | dayLabels = DEFAULT_DAY_LABELS, 73 | monthLabels = DEFAULT_MONTH_LABELS, 74 | weekStartsOn, 75 | showWeeks = false, 76 | previousButtonElement= '<', 77 | nextButtonElement = '>', 78 | pickMonthElement = undefined, 79 | showTodayButton = false, 80 | todayButtonLabel = 'Today', 81 | roundedCorners = false, 82 | cellPadding = '5px', 83 | calendarPlacement = 'bottom' 84 | } = props 85 | 86 | const propError= useCheckProps(value, defaultValue) 87 | if (propError!=undefined) { 88 | throw new Error(propError) 89 | } 90 | 91 | 92 | const [hiddenInputRef, overlayContainerRef, popoverRef, controlInputRef, open, placement, 93 | handleFocus, handleBlur] = useCalendarProps(calendarPlacement, inputRef, autoFocus, onBlur, onFocus) 94 | 95 | const [innerValue, inputValue, displayDate, 96 | selectedDate, handleClear, handleInputChange, 97 | handleChangeMonth, handleChangeDate, handleBadInputOnBlur] = useInputValues(controlInputRef, value, defaultValue, 98 | minDate, maxDate, dateFormat, onClear, onChange) 99 | 100 | const [groupInputId, hiddenInputId, controlInputId, overlayId] = useInputIds(id, name, customControl) 101 | 102 | useImperativeHandle(ref, () => ({ 103 | getValue: () => { 104 | return selectedDate ? selectedDate.toISOString() : null; 105 | }, 106 | getFormattedValue: () => { 107 | return displayDate ? inputValue : null; 108 | }, 109 | getNode: () => controlInputRef?.current 110 | })) //, [controlInputRef, displayDate, inputValue, selectedDate])) 111 | 112 | 113 | const fixedDayLabels = useFixedDayLabels(dayLabels, weekStartsOn) 114 | 115 | const handleChangeDateAndBlur = (nSelectedDate) => { 116 | handleChangeDate(nSelectedDate) 117 | handleBlur(true) 118 | } 119 | 120 | return ( 121 | 125 | 126 | handleFocus()} 143 | onBlur = {(event) => {handleBadInputOnBlur(); handleBlur(event) }} 144 | onChange = {() => handleInputChange()} 145 | /> 146 | 147 | 150 | 151 | {overlayContainerRef.current == undefined 152 | ? null 153 | : 154 | 155 | handleChangeMonth(newDisplayDate)} 168 | monthLabels = {monthLabels} 169 | cellPadding = {cellPadding} 170 | selectedDate = {selectedDate} 171 | onChange = {(newSelectedDate) => handleChangeDateAndBlur(newSelectedDate)} 172 | dayLabels = {fixedDayLabels} 173 | weekStartsOn = {weekStartsOn} 174 | showTodayButton = {showTodayButton} 175 | todayButtonLabel = {todayButtonLabel} 176 | roundedCorners = {roundedCorners} 177 | showWeeks = {showWeeks}/> 178 | } 179 | 180 | 181 | 188 | 189 | 190 | {(showClearButton && !customControl) 191 | ? 192 | handleClear()} 197 | /> 198 | : null 199 | } 200 | 201 | {children} 202 | 203 | 204 | ) 205 | } 206 | 207 | const DatePicker = forwardRef(DatePickerBase) 208 | 209 | export {DatePicker} 210 | -------------------------------------------------------------------------------- /src/calendar/Calendar.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Popover, PopoverHeader, PopoverBody} from 'reactstrap' 3 | import {setTimeToNoon} from '../util/setTimeToNoon' 4 | import {CalendarHeader} from './CalendarHeader' 5 | import {CalendarSubHeader} from './CalendarSubHeader' 6 | import {CalendarBody} from './CalendarBody' 7 | import {CalendarFooter} from './CalendarFooter' 8 | import {useCalendarDays} from './useCalendarDays' 9 | 10 | const Calendar = ( 11 | {popoverRef, selectedDate, displayDate, minDate, maxDate, onChange, dayLabels, 12 | cellPadding, weekStartsOn, showTodayButton, todayButtonLabel, 13 | roundedCorners, showWeeks, monthLabels, previousButtonElement, 14 | nextButtonElement, pickMonthElement, placement, open, 15 | container, target, onChangeMonth}) => { 16 | const calendarDays = useCalendarDays(displayDate, selectedDate, minDate, maxDate, weekStartsOn) 17 | 18 | const handleDayClick = (e) => { 19 | const day = e.currentTarget.getAttribute('data-day') 20 | const newSelectedDate = setTimeToNoon(new Date(displayDate)) 21 | newSelectedDate.setDate(day) 22 | onChange(newSelectedDate) 23 | } 24 | 25 | const handleTodayClick = () => { 26 | const newSelectedDate = setTimeToNoon(new Date()) 27 | onChange(newSelectedDate) 28 | } 29 | 30 | return ( 31 | <> 32 | 33 | handleHide()} 37 | isOpen = {open} 38 | container = {container} 39 | target = {target} 40 | placement = {placement} 41 | // delay = {200} // does not apply for us (manual triggering) 42 | > 43 | 44 | onChangeMonth(newDisplayDate)} 52 | monthLabels = {monthLabels}/> 53 | 54 | 55 | 56 | 57 | 62 | 63 | 70 | 71 | 78 |
79 |
80 |
81 | 82 | ) 83 | } 84 | 85 | export { Calendar } 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/calendar/CalendarBody.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {CalendarDayOutOfMonth} from './CalendarDayOutOfMonth' 3 | import {CalendarDayInMonth} from './CalendarDayInMonth' 4 | import {CalendarWeekNum} from './CalendarWeekNum' 5 | 6 | const CalendarBody = ({calendarDays, showWeeks, onDayClick, cellPadding, roundedCorners}) => { 7 | if (! calendarDays) { 8 | return 9 | } 10 | 11 | return ( 12 | 13 | {calendarDays.map( (week, weekIndex) => 14 | 15 | {showWeeks 16 | ? 20 | : null 21 | } 22 | {week.weekDays.map((weekDay, weekDayIndex) => 23 | weekDay.inMonth 24 | ? 32 | : 34 | )} 35 | 36 | )} 37 | 38 | ) 39 | } 40 | 41 | export {CalendarBody} 42 | -------------------------------------------------------------------------------- /src/calendar/CalendarDayInMonth.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const CAL_DAY_CLASSNAME_BY_MODE= { 4 | 'normal': '', 5 | 'muted': 'text-muted', 6 | 'selected': 'bg-primary', 7 | 'current': 'text-primary' 8 | } 9 | 10 | const CalendarDayInMonth = ({day, mode, onDayClick, cellPadding, roundedCorners}) => { 11 | 12 | const handleClick = (ev) => { 13 | if (mode!='muted') { 14 | onDayClick(ev) 15 | } 16 | } 17 | 18 | return ( 19 | 26 | {day} 27 | 28 | 29 | ) 30 | } 31 | 32 | export {CalendarDayInMonth} 33 | 34 | -------------------------------------------------------------------------------- /src/calendar/CalendarDayOutOfMonth.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const CalendarDayOutOfMonth = () => 4 | 5 | 6 | export {CalendarDayOutOfMonth} -------------------------------------------------------------------------------- /src/calendar/CalendarFooter.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Button} from 'reactstrap' 3 | 4 | const CalendarFooter = ({dayLabels, showWeeks, handleTodayClick, showTodayButton, todayButtonLabel}) => { 5 | if (!showTodayButton) { 6 | return null 7 | } 8 | 9 | return ( 10 | 11 | 12 | 13 | 20 | 21 | 22 | 23 | ) 24 | 25 | } 26 | 27 | export {CalendarFooter} -------------------------------------------------------------------------------- /src/calendar/CalendarHeader.mjs: -------------------------------------------------------------------------------- 1 | 2 | import React, {useState, useEffect} from 'react' 3 | import {compareMonths} from '../util/compareMonths' 4 | import PickMonthDefault from './pickmonth/PickMonthDefault' 5 | 6 | function CalendarHeader ({ 7 | previousButtonElement, nextButtonElement, pickMonthElement, 8 | displayDate, minDate, maxDate, onChange, monthLabels}) { 9 | 10 | const [displayingMinMonth, setDisplayingMinMonth]= useState(false) 11 | const [displayingMaxMonth, setDisplayingMaxMonth]= useState(false) 12 | const [title, setTitle]= useState('') 13 | const PickMonthElement = pickMonthElement 14 | 15 | useEffect(() => { 16 | if (displayDate==undefined) { 17 | return 18 | } 19 | 20 | if (!minDate) { 21 | setDisplayingMinMonth(false) 22 | } else { 23 | setDisplayingMinMonth(compareMonths(displayDate, minDate)) 24 | } 25 | 26 | if (!maxDate) { 27 | setDisplayingMaxMonth(false) 28 | } else { 29 | setDisplayingMaxMonth(compareMonths(displayDate, maxDate)) 30 | } 31 | 32 | try { 33 | if (monthLabels) { 34 | setTitle(`${monthLabels[displayDate.getMonth()]} ${displayDate.getFullYear()}`) 35 | } 36 | } catch(e) { 37 | console.error(e) 38 | } 39 | 40 | }, [displayDate, minDate, maxDate, monthLabels]) 41 | 42 | 43 | const handleChangeMonthIncr = (inc) => { 44 | const newDisplayDate = new Date(displayDate) 45 | newDisplayDate.setMonth(newDisplayDate.getMonth() + inc, 1) 46 | onChange(newDisplayDate) 47 | } 48 | 49 | const handleChangeMonth = (m) => { 50 | const newDisplayDate = new Date(displayDate) 51 | newDisplayDate.setMonth(m) 52 | onChange(newDisplayDate) 53 | } 54 | 55 | const handleChangeYear = (y) => { 56 | const newDisplayDate = new Date(displayDate) 57 | newDisplayDate.setFullYear(y) 58 | onChange(newDisplayDate) 59 | } 60 | 61 | return ( 62 |
63 |
handleChangeMonthIncr(-1)} 65 | style = {{cursor: 'pointer', userSelect: 'none', flexBasis: '1.25em', alignSelf: 'center'}}> 66 | {displayingMinMonth ? null : previousButtonElement} 67 |
68 | 69 |
{ 71 | (PickMonthElement==null || PickMonthElement==='none') 72 | ?
{title}
73 | : PickMonthElement==='default' 74 | ? handleChangeMonth(m) } 80 | onChangeYear = { (y) => handleChangeYear(y) }/> 81 | : handleChangeMonth(m) } 86 | onChangeYear = { (y) => handleChangeYear(y) }/> 87 | 88 | }
89 |
handleChangeMonthIncr(+1)} 91 | style = {{cursor: 'pointer', userSelect: 'none', flexBasis: '1.25em', alignSelf: 'center'}}> 92 | {displayingMaxMonth ? null : nextButtonElement} 93 |
94 |
95 | ) 96 | 97 | } 98 | 99 | export {CalendarHeader} 100 | -------------------------------------------------------------------------------- /src/calendar/CalendarSubHeader.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const CalendarSubHeader = ({dayLabels, showWeeks, cellPadding}) => 4 | 5 | 6 | 7 | {showWeeks 8 | ? 10 | : null 11 | } 12 | {dayLabels.map((label, index) => 13 | 16 | {label} 17 | 18 | )} 19 | 20 | 21 | 22 | export {CalendarSubHeader} -------------------------------------------------------------------------------- /src/calendar/CalendarWeekNum.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const CalendarWeekNum = ({weekNum, cellPadding}) => 4 | 6 | {weekNum} 7 | 8 | 9 | export {CalendarWeekNum} 10 | -------------------------------------------------------------------------------- /src/calendar/pickmonth/PickMonthDefault.mjs: -------------------------------------------------------------------------------- 1 | import React, {useState, useEffect} from 'react' 2 | import {Input} from 'reactstrap' 3 | 4 | const _getYearList = (minDate, maxDate) => { 5 | const minYear= minDate 6 | ? (new Date(minDate)).getFullYear() 7 | : 1970 8 | const maxYear= maxDate 9 | ? (new Date(maxDate)).getFullYear() 10 | : 2045 11 | let yList= [] 12 | for (let y=minYear; y<=maxYear; y++) { 13 | yList.push(y) 14 | } 15 | return yList 16 | } 17 | 18 | 19 | const PickMonthDefault = ({displayDate, minDate, maxDate, monthLabels, onChangeMonth, onChangeYear}) => { 20 | const [month, setMonth]= useState((new Date(displayDate)).getMonth()) 21 | const [year, setYear]= useState((new Date(displayDate)).getFullYear()) 22 | const [yearList, setYearList] = useState(_getYearList(minDate, maxDate)) 23 | 24 | useEffect(() => { 25 | setMonth((new Date(displayDate)).getMonth()) 26 | setYear((new Date(displayDate)).getFullYear()) 27 | }, [displayDate]) 28 | 29 | useEffect(() => { 30 | setYearList(_getYearList(minDate, maxDate)) 31 | }, [minDate, maxDate]) 32 | 33 | const handleChangeMonth = (ev) => { 34 | const m= ev.target.value 35 | setMonth(m) 36 | onChangeMonth(m) 37 | } 38 | 39 | const handleChangeYear = (ev) => { 40 | const y= ev.target.value 41 | setYear(y) 42 | onChangeYear(y) 43 | } 44 | 45 | 46 | return ( 47 |
49 |
51 | 57 | { 58 | monthLabels.map((lmonth, lidx) => { 59 | return ( 60 | 64 | ) 65 | }) 66 | } 67 | 68 |
69 |
71 | 77 | { 78 | yearList.map(lyear => { 79 | return ( 80 | 84 | ) 85 | }) 86 | } 87 | 88 |
89 |
90 | ) 91 | } 92 | 93 | export default PickMonthDefault 94 | -------------------------------------------------------------------------------- /src/calendar/useCalendarDays.mjs: -------------------------------------------------------------------------------- 1 | import {useState, useEffect} from 'react' 2 | import {setTimeToNoon} from '../util/setTimeToNoon' 3 | 4 | const DAYS_BY_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 5 | 6 | /** 7 | groupByWeeks: { 8 | year, 9 | month, 10 | weeks: [ 11 | {weekNum: N, 12 | wekDays: [ 13 | {inMonth: true, day: N, mode: ''} 14 | or 15 | {inMonth: false} 16 | ] 17 | },... 18 | ] 19 | } 20 | */ 21 | 22 | function _groupByWeeks(year, month, weekStartsOn) { 23 | if (year == undefined || month == undefined) { 24 | return undefined 25 | } 26 | 27 | const firstDay = new Date(year, month, 1) 28 | const startingDay = weekStartsOn > 1 29 | ? firstDay.getDay() - weekStartsOn + 7 30 | : weekStartsOn === 1 31 | ? (firstDay.getDay() === 0 ? 6 : firstDay.getDay() - 1) 32 | : firstDay.getDay(); 33 | 34 | let monthLength = DAYS_BY_MONTH[month] 35 | if (month == 1) { 36 | if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { 37 | monthLength = 29; 38 | } 39 | } 40 | 41 | const isInMonth = (monthDay, weekIndex, weekDay) => { 42 | if (monthDay <= monthLength && (weekIndex > 0 || weekDay >= startingDay)) { 43 | return true 44 | } 45 | return false 46 | } 47 | 48 | const getWeekNumber = (monthDay) => { 49 | const date = new Date(year, month, monthDay - 1, 12, 0, 0, 0) 50 | const target = new Date(date.valueOf()); 51 | const dayNr = (date.getDay() + 6) % 7; 52 | target.setDate(target.getDate() - dayNr + 3); 53 | const firstThursday = target.valueOf(); 54 | target.setMonth(0, 1); 55 | if (target.getDay() !== 4) { 56 | target.setMonth(0, 1 + ((4 - target.getDay()) + 7) % 7); 57 | } 58 | return 1 + Math.ceil((firstThursday - target) / 604800000); 59 | } 60 | 61 | 62 | const allWeeks = [] 63 | let monthDay = 1 64 | for (let weekIndex = 0; weekIndex < 9; weekIndex++) { 65 | const weekDays = [] 66 | for (let weekDay = 0; weekDay <= 6; weekDay++) { 67 | if (isInMonth(monthDay, weekIndex, weekDay)) { 68 | weekDays.push({ 69 | inMonth: true, 70 | day: monthDay 71 | }) 72 | monthDay+= 1 73 | } else { 74 | weekDays.push({ 75 | inMonth: false 76 | }) 77 | } 78 | } 79 | 80 | const weekNum = getWeekNumber(monthDay) 81 | 82 | allWeeks.push({ 83 | weekDays, 84 | weekNum 85 | }) 86 | 87 | if (monthDay > monthLength) { 88 | break 89 | } 90 | } 91 | 92 | return { 93 | year, month, weeks: allWeeks 94 | } 95 | 96 | } 97 | 98 | 99 | 100 | /** 101 | calendarDays: [ 102 | {weekNum: N, 103 | wekDays: [ 104 | {inMonth: true, day: N, mode: ''} 105 | or 106 | {inMonth: false} 107 | ] 108 | },... 109 | ] 110 | */ 111 | 112 | function _makeCalendarDays(groupByWeeks, selectedDate, minDate, maxDate) { 113 | 114 | if (groupByWeeks==undefined) { 115 | return [] 116 | } 117 | 118 | 119 | const getDayMode = (day) => { 120 | const date= setTimeToNoon(new Date(groupByWeeks.year, groupByWeeks.month, day, 12, 0, 0, 0)).toISOString() 121 | 122 | 123 | const beforeMinDate = minDate!=undefined ? (Date.parse(date) < Date.parse(setTimeToNoon(new Date(minDate)))) : false 124 | const afterMaxDate = maxDate!=undefined ? (Date.parse(date) > Date.parse(setTimeToNoon(new Date(maxDate)))) : false 125 | const currentDate = setTimeToNoon(new Date()) 126 | const nSelectedDate = setTimeToNoon(new Date(selectedDate)) 127 | 128 | if (beforeMinDate || afterMaxDate) { 129 | return 'muted' 130 | } else if (Date.parse(date) === Date.parse(nSelectedDate)) { 131 | return 'selected' 132 | } else if (Date.parse(date) === Date.parse(currentDate)) { 133 | return 'current' 134 | } else { 135 | return 'normal' 136 | } 137 | } 138 | 139 | let calendarDays= [] 140 | 141 | groupByWeeks.weeks.map(week => { 142 | const weekNum= week.weekNum 143 | const weekDays = week.weekDays.map(weekDay => { 144 | return { 145 | ...weekDay, 146 | mode: weekDay.inMonth ? getDayMode(weekDay.day) : undefined 147 | } 148 | }) 149 | calendarDays.push({ 150 | weekNum, 151 | weekDays 152 | }) 153 | }) 154 | 155 | return calendarDays 156 | } 157 | 158 | function useCalendarDays(displayDate, selectedDate, minDate, maxDate, weekStartsOn) { 159 | const [year, setYear]= useState(undefined) 160 | const [month, setMonth]= useState(undefined) 161 | const [groupByWeeks, setGroupByWeeks]= useState(undefined) 162 | const [calendarDays, setCalendarDays]= useState([]) 163 | 164 | 165 | useEffect(() => { 166 | if (displayDate) { 167 | setYear(displayDate.getFullYear()) 168 | setMonth(displayDate.getMonth()) 169 | } 170 | }, [displayDate]) 171 | 172 | 173 | useEffect(() => { 174 | setGroupByWeeks(_groupByWeeks(year, month, weekStartsOn)) 175 | }, [year, month, weekStartsOn]) 176 | 177 | useEffect(() => { 178 | setCalendarDays(_makeCalendarDays(groupByWeeks, selectedDate, minDate, maxDate)) 179 | }, [groupByWeeks, selectedDate, minDate, maxDate]) 180 | 181 | 182 | return calendarDays 183 | } 184 | 185 | 186 | export {useCalendarDays} -------------------------------------------------------------------------------- /src/calendar/useCalendarProps.mjs: -------------------------------------------------------------------------------- 1 | import {useState, useEffect, useCallback, useRef} from 'react' 2 | import { useCustomEvents } from '../input/useCustomEvents' 3 | import { getMaybeFuncValue } from '../util/getMaybeFuncValue' 4 | 5 | // About autoFocus 6 | // We could handle by our own through ref callbacks 7 | // (https://blog.maisie.ink/react-ref-autofocus/) 8 | // But let's just use react's autoFocus attribute by now 9 | 10 | 11 | 12 | const useCalendarProps = (calendarPlacement, inputRef, autoFocus, onBlur, onFocus) => { 13 | const [open, setOpen] = useState(false) 14 | 15 | const [placement, setPlacement] = useState(getMaybeFuncValue(calendarPlacement)) 16 | 17 | const hiddenInputRef = useRef() 18 | const overlayContainerRef = useRef() 19 | const popoverRef = useRef() 20 | const controlInputRef = typeof inputRef == 'object' 21 | ? inputRef 22 | : useRef(inputRef) // eslint-disable-line react-hooks/rules-of-hooks 23 | 24 | // NOTE: do we want to use the controlInput or the hiddenInput here? 25 | const [customOnBlur, customOnFocus] = useCustomEvents(/*hiddenInputRef*/ controlInputRef, onBlur, onFocus) 26 | 27 | const isOutside = useCallback((event) => { 28 | const outOfCalendar = (popoverRef?.current==undefined) || (!popoverRef?.current.contains(event.target)) 29 | const outOfControlInput = (controlInputRef?.current==undefined) || (!controlInputRef.current.contains(event.target)) 30 | const isOut= (outOfCalendar && outOfControlInput) 31 | return isOut 32 | }, [popoverRef, controlInputRef]) 33 | 34 | // Control the click outside 35 | useEffect(() => { 36 | function handleClickOutside(event) { 37 | event.stopPropagation() 38 | 39 | if (open) { 40 | if (isOutside(event)) { 41 | setOpen(false) 42 | customOnBlur() 43 | } 44 | } 45 | } 46 | 47 | document.addEventListener("mousedown", handleClickOutside) 48 | return () => { 49 | document.removeEventListener("mousedown", handleClickOutside) 50 | } 51 | }, [open, isOutside, customOnBlur]) 52 | 53 | 54 | // 55 | // This callback is bound to Calendar.Popover's toggle() method, 56 | // but seems it's unneccesary 57 | // Leaving, by now, this testimonial comments 58 | // 59 | // const handleHide = () => { 60 | // let inputFocused = false 61 | // try { 62 | // inputFocused= controlInputRef.current==document.activeElement 63 | // } catch(e) {} 64 | // 65 | // if (inputFocused) { 66 | // return 67 | // } 68 | // setOpen(false) 69 | // customOnBlur() 70 | // } 71 | 72 | const handleFocus = useCallback(() => { 73 | const nPlacement = getMaybeFuncValue(calendarPlacement) 74 | setPlacement(nPlacement) 75 | 76 | setOpen(true) 77 | customOnFocus() 78 | }, [calendarPlacement, customOnFocus]) 79 | 80 | const handleBlur = useCallback((event) => { 81 | if (isOutside(event)) { 82 | setOpen(false) 83 | customOnBlur() 84 | } 85 | }, [isOutside, customOnBlur]) 86 | 87 | return [hiddenInputRef, overlayContainerRef, popoverRef, controlInputRef, open, placement, handleFocus, handleBlur] 88 | } 89 | 90 | export { useCalendarProps } 91 | 92 | -------------------------------------------------------------------------------- /src/index.mjs: -------------------------------------------------------------------------------- 1 | import {DatePicker} from './DatePicker' 2 | 3 | export {DatePicker} 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/input/InputClearButton.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {InputGroupText} from 'reactstrap' 3 | 4 | const InputClearButton = ({inputValue, disabled, clearButtonElement, onClick}) => 5 |
6 | disabled ? null : onClick()} 8 | style = {{ 9 | opacity: (inputValue && !disabled) ? 1 : 0.5, 10 | cursor:(inputValue && !disabled) ? 'pointer' : 'not-allowed', 11 | height: "100%" 12 | }}> 13 | {clearButtonElement} 14 | 15 |
16 | 17 | 18 | export {InputClearButton} -------------------------------------------------------------------------------- /src/input/InputControlInput.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Input} from 'reactstrap' 3 | 4 | const InputControlInput = ( 5 | {customControl, controlId, 6 | value, required, placeholder, inputRef, disabled, 7 | className, style, autoFocus, autoComplete, valid, invalid, onInvalid, noValidate, 8 | onKeyDown, onFocus, onBlur, onChange}) => { 9 | 10 | const validityClassNames= `${invalid===true ? 'is-invalid' : ''} ${valid===true ? 'is-valid' : ''}` 11 | 12 | if (customControl!=undefined) { 13 | return React.cloneElement(customControl, { 14 | id : controlId, 15 | value : value || '', 16 | required : required, 17 | placeholder : placeholder, 18 | ref : inputRef, 19 | disabled : disabled, 20 | className : `rdp-form-control ${className || ''} ${customControl.props.className||''} ${validityClassNames}`, 21 | style : {...customControl.props.style||{}, ...style || {}}, 22 | autoComplete: autoComplete, 23 | onInvalid : onInvalid, 24 | noValidate : noValidate, 25 | onKeyDown : onKeyDown, 26 | onFocus : onFocus, 27 | onBlur : onBlur, 28 | onChange : onChange, 29 | valid : valid, 30 | invalid : invalid 31 | }) 32 | } 33 | 34 | return ( 35 | 57 | ) 58 | 59 | } 60 | 61 | export {InputControlInput} -------------------------------------------------------------------------------- /src/input/InputGroup.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import {InputGroup as RSInputGroup} from 'reactstrap' 4 | 5 | const InputGroup = ({children, customInputGroup, size, inputId}) => { 6 | 7 | if (customInputGroup != undefined) { 8 | return ( 9 | React.cloneElement(customInputGroup, {children: children}) 10 | ) 11 | } 12 | 13 | return ( 14 | 19 | {children} 20 | 21 | ) 22 | } 23 | 24 | export {InputGroup} -------------------------------------------------------------------------------- /src/input/InputHidden.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const InputHidden = ({inputId, name, value, formattedValue, hiddenInputRef}) => 4 | 13 | export {InputHidden} 14 | -------------------------------------------------------------------------------- /src/input/InputOverlay.mjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const InputOverlay = ({oid, overlayContainerRef, children}) => 4 |
8 | {children} 9 |
10 | 11 | export {InputOverlay} 12 | -------------------------------------------------------------------------------- /src/input/useCustomEvents.mjs: -------------------------------------------------------------------------------- 1 | import {useCallback} from 'react' 2 | 3 | // NOTE: do we want to use the controlInput or the hiddenInput here? 4 | // We were previously using the hidden one, but I see no reasons. 5 | // 'change' events would make sense on the hidden input, but focus 6 | // control seems to be more on the control input. 7 | // Anyway, this is not decided here, but when calling useCustomEvents() 8 | 9 | const useCustomEvents = (inputRef, onBlur, onFocus) => { 10 | const customOnBlur = useCallback(() => { 11 | 12 | if (onBlur) { 13 | const event = document.createEvent('CustomEvent') 14 | event.initEvent('Change Date', true, false) 15 | inputRef.current.dispatchEvent(event) 16 | onBlur(event) 17 | } 18 | }, [inputRef, onBlur]) 19 | 20 | const customOnFocus = useCallback(() => { 21 | if (onFocus) { 22 | const event = document.createEvent('CustomEvent') 23 | event.initEvent('Change Date', true, false) 24 | inputRef.current.dispatchEvent(event) 25 | onFocus(event) 26 | } 27 | }, [inputRef, onFocus]) 28 | 29 | return [customOnBlur, customOnFocus] 30 | } 31 | 32 | export { useCustomEvents } -------------------------------------------------------------------------------- /src/input/useDayLabels.mjs: -------------------------------------------------------------------------------- 1 | import {useState, useEffect} from 'react' 2 | 3 | const _getFixedDayLabels = (dayLabels, weekStartsOn) => { 4 | if (weekStartsOn > 1) { 5 | return dayLabels 6 | .slice(weekStartsOn) 7 | .concat(dayLabels.slice(0, weekStartsOn)) 8 | } 9 | 10 | if (weekStartsOn === 1) { 11 | return dayLabels.slice(1).concat(dayLabels.slice(0,1)) 12 | } 13 | 14 | return dayLabels 15 | } 16 | 17 | 18 | const useFixedDayLabels = (dayLabels, weekStartsOn) => { 19 | 20 | const [fixedDayLabels, setFixedDayLabels]= useState(_getFixedDayLabels(dayLabels, weekStartsOn)) 21 | 22 | useEffect(() => { 23 | setFixedDayLabels(_getFixedDayLabels(dayLabels, weekStartsOn)) 24 | }, [dayLabels, weekStartsOn]) 25 | 26 | return fixedDayLabels 27 | } 28 | 29 | export {useFixedDayLabels} -------------------------------------------------------------------------------- /src/input/useInputIds.mjs: -------------------------------------------------------------------------------- 1 | import {useState, useEffect} from 'react' 2 | import {getInstanceCount} from '../util/getInstanceCount' 3 | 4 | const _getIdSuffix = (id, name) => { 5 | // Try or props to determine elements' id suffix 6 | if (id!=undefined && id!='') 7 | return id 8 | if (name!=undefined && name!='') 9 | return name 10 | // If none was passed, use global vars 11 | const iCount= getInstanceCount() 12 | return iCount.toString() 13 | } 14 | 15 | 16 | const _getInputIds = (id, name, customControl) => { 17 | const idSuffix = _getIdSuffix(id, name) 18 | const group= `rdp-input-group-${idSuffix}` 19 | const hidden = id!=undefined ? id : `rdp-hidden-${idSuffix}` 20 | let control= `rdp-form-control-${idSuffix}` 21 | if (customControl!=undefined && customControl?.props?.id) { 22 | control= customControl.props.id 23 | } 24 | const overlay = `rdp-overlay-${idSuffix}` 25 | return [group, hidden, control, overlay] 26 | } 27 | 28 | 29 | const useInputIds = (id, name, customControl) => { 30 | 31 | const [inputIds, setInputIds]= useState(_getInputIds(id, name, customControl)) 32 | 33 | useEffect(() => { 34 | setInputIds(_getInputIds(id, name, customControl)) 35 | }, [id, name, customControl]) 36 | 37 | return inputIds 38 | } 39 | 40 | 41 | 42 | export {useInputIds} -------------------------------------------------------------------------------- /src/input/useInputValues.mjs: -------------------------------------------------------------------------------- 1 | import {useState, useEffect /*, useCallback*/} from 'react' 2 | import { getDateFromIsoString } from '../util/getDateFromIsoString' 3 | import { getIsoStringFromDate } from '../util/getIsoStringFromDate' 4 | 5 | const _makeInputValueString = (date, separator, dateFormat) => { 6 | const month = date.getMonth() + 1 7 | const day = date.getDate() 8 | 9 | if (dateFormat.match(/MM.DD.YYYY/)) { 10 | return (month > 9 ? month : `0${month}`) + separator + (day > 9 ? day : `0${day}`) + separator + date.getFullYear() 11 | } 12 | else if (dateFormat.match(/DD.MM.YYYY/)) { 13 | return (day > 9 ? day : `0${day}`) + separator + (month > 9 ? month : `0${month}`) + separator + date.getFullYear() 14 | } 15 | else { 16 | return date.getFullYear() + separator + (month > 9 ? month : `0${month}`) + separator + (day > 9 ? day : `0${day}`) 17 | } 18 | } 19 | 20 | const useInputValues = (controlInputRef, value, defaultValue, minDate, maxDate, dateFormat, onClear, onChange) => { 21 | const [separator, setSeparator]= useState(dateFormat.match(/[^A-Z]/)[0]) 22 | const [innerValue, setInnerValue]= useState(null) 23 | const [inputValue, setInputValue]= useState(null) 24 | const [displayDate, setDisplayDate]= useState(null) 25 | const [selectedDate, setSelectedDate]= useState(null) 26 | 27 | 28 | 29 | // handle props changes 30 | useEffect(() => { 31 | setSeparator(dateFormat.match(/[^A-Z]/)[0]) 32 | }, [dateFormat]) 33 | 34 | 35 | // handle input values 36 | useEffect(() => { 37 | const isoString= value || defaultValue 38 | const minDate = getDateFromIsoString(minDate) 39 | const maxDate = getDateFromIsoString(maxDate) 40 | 41 | 42 | 43 | const nSelectedDate = getDateFromIsoString(isoString) 44 | const nInnerValue = getIsoStringFromDate(nSelectedDate) 45 | const nInputValue = isoString ? _makeInputValueString(nSelectedDate, separator, dateFormat) : null 46 | 47 | let nDisplayDate 48 | if (nSelectedDate) { 49 | //nDisplayDate = new Date(nSelectedDate) 50 | nDisplayDate = nSelectedDate 51 | } else { 52 | const today = getDateFromIsoString(new Date().toISOString()) 53 | if (minDate && Date.parse(minDate) >= Date.parse(today)){ 54 | nDisplayDate = minDate 55 | } else if (maxDate && Date.parse(maxDate) <= Date.parse(today)){ 56 | nDisplayDate = maxDate 57 | } else { 58 | nDisplayDate = today 59 | } 60 | } 61 | 62 | setInnerValue(nInnerValue) 63 | setInputValue(nInputValue) 64 | setSelectedDate(nSelectedDate) 65 | setDisplayDate(nDisplayDate) 66 | 67 | }, [value, defaultValue, minDate, maxDate, separator, dateFormat]) 68 | 69 | // 70 | const handleClear = /*useCallback(*/() => { 71 | if (onClear) { 72 | onClear() 73 | } 74 | else { 75 | setInnerValue(null) 76 | setInputValue(null) 77 | setSelectedDate(null) 78 | setDisplayDate(null) 79 | 80 | if (onChange) { 81 | onChange(null, null) 82 | } 83 | } 84 | }/*, [onClear, onChange])*/ 85 | 86 | const handleBadInput = /*useCallback(*/(originalValue, tail= false) => { 87 | 88 | const parts = originalValue.replace(new RegExp(`[^0-9${separator}]`), '').split(separator) 89 | 90 | if (dateFormat.match(/MM.DD.YYYY/) || dateFormat.match(/DD.MM.YYYY/)) { 91 | if (parts[0] && parts[0].length > 2) { 92 | parts[1] = parts[0].slice(2) + (parts[1] || '') 93 | parts[0] = parts[0].slice(0, 2) 94 | } 95 | if (parts[1] && parts[1].length > 2) { 96 | parts[2] = parts[1].slice(2) + (parts[2] || '') 97 | parts[1] = parts[1].slice(0, 2) 98 | } 99 | if (parts[2]) { 100 | parts[2] = parts[2].slice(0,4) 101 | if (tail) { 102 | if (parts[2].length < 4) { 103 | parts[2]= parts[2].padEnd(4, '0') 104 | } 105 | } 106 | } 107 | } else { 108 | if (parts[0] && parts[0].length > 4) { 109 | parts[1] = parts[0].slice(4) + (parts[1] || '') 110 | parts[0] = parts[0].slice(0, 4) 111 | } 112 | if (parts[1] && parts[1].length > 2) { 113 | parts[2] = parts[1].slice(2) + (parts[2] || '') 114 | parts[1] = parts[1].slice(0, 2) 115 | } 116 | if (parts[2]) { 117 | parts[2] = parts[2].slice(0,2) 118 | } 119 | } 120 | const nInputValue= parts.join(separator) 121 | setInputValue(nInputValue) 122 | }/*, [dateFormat, separator])*/ 123 | 124 | const handleBadInputOnBlur = () => { 125 | const originalValue = controlInputRef?.current?.value || '' 126 | if (originalValue) { 127 | handleBadInput(originalValue, true) 128 | } 129 | } 130 | 131 | const handleInputChange = /*useCallback(*/() => { 132 | const originalValue = controlInputRef?.current?.value || '' 133 | const nInputValue = originalValue.replace(/(-|\/\/)/g, separator).slice(0,10) 134 | 135 | if (!nInputValue) { 136 | handleClear() 137 | return 138 | } 139 | 140 | let month, day, year 141 | if (dateFormat.match(/MM.DD.YYYY/)) { 142 | if (!nInputValue.match(/[0-1][0-9].[0-3][0-9].[1-2][0-9][0-9][0-9]/)) { 143 | return handleBadInput(originalValue) 144 | } 145 | 146 | month = nInputValue.slice(0,2).replace(/[^0-9]/g, '') 147 | day = nInputValue.slice(3,5).replace(/[^0-9]/g, '') 148 | year = nInputValue.slice(6,10).replace(/[^0-9]/g, '') 149 | } else if (dateFormat.match(/DD.MM.YYYY/)) { 150 | if (!nInputValue.match(/[0-3][0-9].[0-1][0-9].[1-2][0-9][0-9][0-9]/)) { 151 | return handleBadInput(originalValue) 152 | } 153 | 154 | day = nInputValue.slice(0,2).replace(/[^0-9]/g, '') 155 | month = nInputValue.slice(3,5).replace(/[^0-9]/g, '') 156 | year = nInputValue.slice(6,10).replace(/[^0-9]/g, '') 157 | } else { 158 | if (!nInputValue.match(/[1-2][0-9][0-9][0-9].[0-1][0-9].[0-3][0-9]/)) { 159 | return handleBadInput(originalValue) 160 | } 161 | 162 | year = nInputValue.slice(0,4).replace(/[^0-9]/g, '') 163 | month = nInputValue.slice(5,7).replace(/[^0-9]/g, '') 164 | day = nInputValue.slice(8,10).replace(/[^0-9]/g, '') 165 | } 166 | 167 | const monthInteger = parseInt(month, 10) 168 | const dayInteger = parseInt(day, 10) 169 | const yearInteger = parseInt(year, 10) 170 | if (monthInteger > 12 || dayInteger > 31) { 171 | return handleBadInput(originalValue) 172 | } 173 | 174 | const beforeMinDate = minDate && Date.parse(originalValue) < Date.parse(minDate) 175 | const afterMaxDate = maxDate && Date.parse(originalValue) > Date.parse(maxDate) 176 | 177 | if (beforeMinDate || afterMaxDate) { 178 | return handleBadInput(originalValue) 179 | } 180 | 181 | if (!isNaN(monthInteger) && !isNaN(dayInteger) && !isNaN(yearInteger) && monthInteger <= 12 && dayInteger <= 31 && yearInteger > 999) { 182 | const nSelectedDate = getDateFromIsoString(new Date(yearInteger, monthInteger - 1, dayInteger, 12, 0, 0, 0).toISOString()) 183 | const nInnerValue = getIsoStringFromDate(nSelectedDate) 184 | 185 | setSelectedDate(nSelectedDate) 186 | setDisplayDate(nSelectedDate) 187 | setInnerValue(nInnerValue) 188 | 189 | if (onChange) { 190 | onChange(nInnerValue, nInputValue) 191 | } 192 | } 193 | 194 | setInputValue(nInputValue) 195 | }/*, [controlInputRef, separator, onChange, minDate, maxDate])*/ 196 | 197 | const handleChangeMonth = (nDisplayDate) => { 198 | setDisplayDate(nDisplayDate) 199 | } 200 | 201 | const handleChangeDate = /*useCallback(*/(nSelectedDate) => { 202 | const nInnerValue = getIsoStringFromDate(nSelectedDate) 203 | const nInputValue = _makeInputValueString(nSelectedDate, separator, dateFormat) 204 | 205 | setInputValue(nInputValue) 206 | setSelectedDate(nSelectedDate) 207 | setDisplayDate(nSelectedDate) 208 | setInnerValue(nInnerValue) 209 | 210 | if (onChange) { 211 | onChange(nInnerValue, nInputValue) 212 | } 213 | }/*, [separator, dateFormat, onChange])*/ 214 | 215 | 216 | return [innerValue, inputValue, displayDate, selectedDate, handleClear, handleInputChange, handleChangeMonth, handleChangeDate, handleBadInputOnBlur] 217 | 218 | } 219 | 220 | 221 | 222 | export { useInputValues } -------------------------------------------------------------------------------- /src/props.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afialapis/reactstrap-date-picker/8be567d29eee1dfe43c80524e7463b6ca9ea721f/src/props.tar.gz -------------------------------------------------------------------------------- /src/util/compareMonths.mjs: -------------------------------------------------------------------------------- 1 | const compareMonths = (a, b) => { 2 | try { 3 | const da= new Date(a) 4 | const db= new Date(b) 5 | 6 | const sameYear= da.getFullYear() == db.getFullYear() 7 | const sameMonth = da.getMonth() == db.getMonth() 8 | 9 | return sameMonth && sameYear 10 | }catch(e) { 11 | console.error(e) 12 | return true 13 | } 14 | } 15 | 16 | 17 | export {compareMonths} -------------------------------------------------------------------------------- /src/util/getDateFromIsoString.mjs: -------------------------------------------------------------------------------- 1 | import {setTimeToNoon} from './setTimeToNoon' 2 | 3 | const getDateFromIsoString = (isoString) => 4 | isoString ? setTimeToNoon(new Date(isoString)) : null 5 | //isoString ? new Date(`${isoString.slice(0,10)}T12:00:00.000Z`) : null 6 | 7 | 8 | export { getDateFromIsoString } -------------------------------------------------------------------------------- /src/util/getInstanceCount.mjs: -------------------------------------------------------------------------------- 1 | const getInstanceCount = () => { 2 | if (typeof window === 'object') { 3 | if (window._reactstrapDatePickerInstance == undefined) { 4 | window._reactstrapDatePickerInstance= 0 5 | } 6 | const next= window._reactstrapDatePickerInstance+1 7 | window._reactstrapDatePickerInstance= next 8 | return next 9 | } else if (typeof process === 'object') { 10 | if (process._reactstrapDatePickerInstance == undefined) { 11 | process._reactstrapDatePickerInstance= 0 12 | } 13 | const next= process._reactstrapDatePickerInstance+1 14 | process._reactstrapDatePickerInstance= next 15 | return next 16 | } else { 17 | console.error("Reactstrap Date Picker cannot determine environment (it is neither browser's nor Node's ).") 18 | return 1 19 | } 20 | } 21 | 22 | export {getInstanceCount} -------------------------------------------------------------------------------- /src/util/getIsoStringFromDate.mjs: -------------------------------------------------------------------------------- 1 | import {setTimeToNoon} from './setTimeToNoon' 2 | 3 | const getIsoStringFromDate = (date) => 4 | date ? setTimeToNoon(date).toISOString() : null 5 | //date ? `${date.toISOString().slice(0,10)}T12:00:00.000Z` : null 6 | 7 | export { getIsoStringFromDate } -------------------------------------------------------------------------------- /src/util/getMaybeFuncValue.mjs: -------------------------------------------------------------------------------- 1 | const getMaybeFuncValue = (value) => { 2 | const tag = Object.prototype.toString.call(value) 3 | const isFunction = tag === '[object AsyncFunction]' || tag === '[object Function]' || tag === '[object GeneratorFunction]' || tag === '[object Proxy]' 4 | if (isFunction) { 5 | return value() 6 | } 7 | else { 8 | return value 9 | } 10 | } 11 | 12 | 13 | export { getMaybeFuncValue } -------------------------------------------------------------------------------- /src/util/setTimeToNoon.mjs: -------------------------------------------------------------------------------- 1 | const setTimeToNoon = (date) => { 2 | if (! date) { 3 | return null 4 | } 5 | date.setHours(12 - date.getTimezoneOffset()/60) 6 | date.setMinutes(0) 7 | date.setSeconds(0) 8 | date.setMilliseconds(0) 9 | return date 10 | } 11 | 12 | export {setTimeToNoon} -------------------------------------------------------------------------------- /src/util/useCheckProps.mjs: -------------------------------------------------------------------------------- 1 | const useCheckProps = (value, defaultValue) => { 2 | if (value && defaultValue) { 3 | return 'Conflicting DatePicker properties \'value\' and \'defaultValue\'' 4 | } 5 | 6 | return undefined 7 | } 8 | 9 | export {useCheckProps} -------------------------------------------------------------------------------- /src/util/useMaybeFuncProp.mjs: -------------------------------------------------------------------------------- 1 | import {useState, useEffect} from 'react' 2 | import { getMaybeFuncValue } from './getMaybeFuncValue' 3 | 4 | 5 | const useMaybeFuncProp = (propValue) => { 6 | 7 | const [value, setValue]= useState(getMaybeFuncValue(propValue)) 8 | 9 | useEffect(() => { 10 | setValue(getMaybeFuncValue(propValue)) 11 | }, [propValue]) 12 | 13 | return [value, setValue] 14 | } 15 | 16 | 17 | 18 | export {useMaybeFuncProp} -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Run tests 2 | 3 | 1. Clone the [reactstrap-date-picker repository](https://github.com/afialapis/reactstrap-date-picker) 4 | 2. Run `npm install` to install dependencies 5 | 3. Run `npm run test` 6 | 7 | # About `enzyme` and `cheerio` 8 | 9 | `enzyme` needs `cheerio`. At least for current `enzyme` version (3.11.0), there is an import error. 10 | 11 | We need to stick to `cheerio`'s version "1.0.0-rc.3". 12 | 13 | [More info here](https://github.com/enzymejs/enzyme/issues/2518) 14 | 15 | -------------------------------------------------------------------------------- /test/before.mjs: -------------------------------------------------------------------------------- 1 | before(async function(){ 2 | // console.log('Preloading functions (ESM) for being used in test units (CJS)') 3 | 4 | const { _resolve } = await import("./esm_pkg.cjs") 5 | await _resolve() 6 | }) 7 | 8 | -------------------------------------------------------------------------------- /test/esm_pkg.cjs: -------------------------------------------------------------------------------- 1 | 2 | "use strict"; 3 | 4 | // Wrapper for dynamic import() of ESM-only packages. 5 | // Only works in later versions of Node.js 12+ 6 | 7 | let _rdpPkg 8 | const _getRDPPkg = async () => { 9 | if (!_rdpPkg) { 10 | _rdpPkg = await import("../src/index.mjs"); 11 | } 12 | 13 | return _rdpPkg; 14 | }; 15 | 16 | 17 | const _resolve = async () => Promise.all([ 18 | global.formigaRPkg = await _getRDPPkg() 19 | ]); 20 | 21 | module.exports = { 22 | _resolve, 23 | }; 24 | 25 | -------------------------------------------------------------------------------- /test/tools/checks.cjs: -------------------------------------------------------------------------------- 1 | const expect= global.expect 2 | 3 | const assertIsoStringsHaveSameDate = (IsoStringA, IsoStringB) => { 4 | const dateA = new Date(IsoStringA) 5 | const dateB = new Date(IsoStringB) 6 | 7 | expect(dateA.getMonth()).to.equal(dateB.getMonth()) 8 | expect(dateA.getDate()).to.equal(dateB.getDate()) 9 | expect(dateA.getFullYear()).to.equal(dateB.getFullYear()) 10 | } 11 | 12 | 13 | export {assertIsoStringsHaveSameDate} -------------------------------------------------------------------------------- /test/tools/finders.cjs: -------------------------------------------------------------------------------- 1 | const getHiddenInput = (container, did) => container.querySelector(`input#${did}`) 2 | const getHiddenInputValue = (container, did) => getHiddenInput(container, did).value 3 | const getHiddenInputFmtValue = (container, did) => getHiddenInput(container, did).getAttribute('data-formattedvalue') 4 | const getInput = (container) => container.querySelector(`input.form-control`) 5 | const getCalendar = (container) => container.querySelector(`div.rdp-popover`) 6 | const getCalendarRandomDay = (container) => container.querySelectorAll(`table tbody tr:last-child td`)[0] 7 | const getCalendarDay = (container, row, col) => container.querySelectorAll(`table tbody tr`)[row].querySelectorAll('td')[col] 8 | const getCalendarDayHeader = (container, col) => container.querySelectorAll(`table thead tr`)[0].querySelectorAll('td')[col] 9 | 10 | export { 11 | getHiddenInput, 12 | getHiddenInputValue, 13 | getHiddenInputFmtValue, 14 | getInput, 15 | getCalendar, 16 | getCalendarRandomDay, 17 | getCalendarDay, 18 | getCalendarDayHeader 19 | } 20 | -------------------------------------------------------------------------------- /test/units/integrity/basic.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../src/index' 3 | import {assertIsoStringsHaveSameDate} from '../../tools/checks' 4 | 5 | import { 6 | getHiddenInput, 7 | getHiddenInputValue, 8 | getHiddenInputFmtValue 9 | } from '../../tools/finders' 10 | 11 | const expect= global.expect 12 | const render= global.render 13 | 14 | 15 | describe('integrity: basic', function () { 16 | // this.timeout(100) 17 | 18 | 19 | it('should render a date picker', () => { 20 | const did= 'basic' 21 | const {container} = render() 22 | 23 | // check the hidden input is actually there 24 | const hiddenInput= getHiddenInput(container, did) 25 | expect(hiddenInput).to.exist 26 | 27 | }) 28 | 29 | 30 | it("should render a date picker with a value.", () => { 31 | const did = 'basic-with-value' 32 | const value = `${new Date().toISOString().slice(0,10)}T12:00:00.000Z` 33 | const Unit = () => 34 | 35 | 36 | const {container} = render() 37 | 38 | // get both hidden and shown values 39 | const hiddenInputValue = getHiddenInputValue(container, did) 40 | const hiddenInputFmtValue = getHiddenInputFmtValue(container, did) 41 | 42 | // check they are the same 43 | assertIsoStringsHaveSameDate(hiddenInputValue, hiddenInputFmtValue) 44 | expect(hiddenInputFmtValue) 45 | .to.equal(`${value.slice(5,7)}/${value.slice(8,10)}/${value.slice(0,4)}`) 46 | }) 47 | 48 | 49 | }) -------------------------------------------------------------------------------- /test/units/integrity/calendar.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../src/index' 3 | import {assertIsoStringsHaveSameDate} from '../../tools/checks' 4 | 5 | import { 6 | getHiddenInputValue, 7 | getHiddenInputFmtValue, 8 | getInput, 9 | getCalendarRandomDay, 10 | getCalendarDay, 11 | getCalendar 12 | } 13 | from '../../tools/finders' 14 | 15 | const { 16 | expect, 17 | render, 18 | fireEvent 19 | } = global 20 | 21 | describe('integrity: calendar', function () { 22 | this.timeout(5000) 23 | 24 | it("should open the calendar and select a date.", () => { 25 | const did = 'calendar-select' 26 | const Unit = () => 27 | 28 | 29 | const {container} = render() 30 | 31 | // check hidden input starts empty 32 | let hiddenInputValue = getHiddenInputValue(container, did) 33 | let hiddenInputFmtValue = getHiddenInputFmtValue(container, did) 34 | 35 | expect(hiddenInputValue).to.equal('') 36 | expect(hiddenInputFmtValue).to.equal('') 37 | 38 | // focus on control input, open calendar 39 | const input = getInput(container) 40 | fireEvent.focus(input) 41 | 42 | // click on some day in the calendar 43 | const day = getCalendarRandomDay(container) 44 | fireEvent.click(day) 45 | 46 | // check some value has been selected 47 | hiddenInputValue = getHiddenInputValue(container, did) 48 | hiddenInputFmtValue = getHiddenInputFmtValue(container, did) 49 | 50 | expect(hiddenInputValue).to.not.equal('') 51 | expect(hiddenInputFmtValue).to.not.equal('') 52 | 53 | 54 | }) 55 | 56 | it("should open the calendar, select a date, and trigger a change event.", () => { 57 | const did = 'calendar-select' 58 | 59 | let value = null; 60 | let formattedValue = null; 61 | 62 | const Unit = () => 63 | {value= v; formattedValue= f}}/> 64 | 65 | const {container} = render() 66 | 67 | // focus on control input, open calendar 68 | const input = getInput(container) 69 | fireEvent.focus(input) 70 | 71 | // check values are still empty empty 72 | expect(value).to.equal(null) 73 | expect(formattedValue).to.equal(null) 74 | 75 | // click on some day in the calendar 76 | const day = getCalendarRandomDay(container) 77 | fireEvent.click(day) 78 | 79 | // check values are ok after selection 80 | expect(value).to.exist 81 | expect(formattedValue).to.exist 82 | expect(typeof value).to.equal('string') 83 | expect(typeof formattedValue).to.equal('string') 84 | assertIsoStringsHaveSameDate(value, formattedValue) 85 | 86 | 87 | 88 | }) 89 | 90 | it("should open the calendar and render 29 days on a leap year.", () => { 91 | const did = 'calendar-leap-year' 92 | const value = "2016-02-15T00:00:00.000Z" 93 | const Unit = () => 94 | 95 | 96 | const {container} = render() 97 | 98 | // focus on control input, open calendar 99 | const input = getInput(container) 100 | fireEvent.focus(input) 101 | 102 | // get the 29th day element 103 | const day29container= getCalendarDay(container, 4, 1) 104 | expect(day29container.innerHTML).to.equal('29') 105 | 106 | // select it and check the value is ok 107 | fireEvent.click(day29container) 108 | 109 | const hiddenInputValue = getHiddenInputValue(container, did) 110 | assertIsoStringsHaveSameDate(hiddenInputValue, "2016-02-29T00:00:00.000Z") 111 | 112 | 113 | }) 114 | 115 | it("should go to the previous and next month.", () => { 116 | const did = 'calendar-month-iter' 117 | const Unit = () => 118 | 119 | 120 | const {container} = render() 121 | 122 | // focus on control input, open calendar 123 | const input = getInput(container) 124 | fireEvent.focus(input) 125 | 126 | // find month switchers 127 | const previousButtonElement = container.querySelector(`div.rdp-header-previous-wrapper`) 128 | const nextButtonElement = container.querySelector(`div.rdp-header-next-wrapper`) 129 | 130 | // go to previous month 131 | fireEvent.click(previousButtonElement) 132 | 133 | // select any day in that month and get the value 134 | const previousDay = getCalendarRandomDay(container) 135 | fireEvent.click(previousDay) 136 | const previousMonthISOString = getHiddenInputValue(container, did) 137 | 138 | // reopen calendar 139 | fireEvent.focus(input) 140 | 141 | // go to next month 142 | fireEvent.click(nextButtonElement) 143 | 144 | // select any day in that month and get the value 145 | const nextDay = getCalendarRandomDay(container) 146 | fireEvent.click(nextDay) 147 | const currentMonthISOString = getHiddenInputValue(container, did) 148 | 149 | // check taht previous month' day is actually older 150 | expect(previousMonthISOString < currentMonthISOString).to.equal(true) 151 | 152 | 153 | }) 154 | 155 | it("should cycle through every month in the year.", () => { 156 | const did = 'calendar-month-year-loop' 157 | const value = "2016-01-15T00:00:00.000Z" 158 | const Unit = () => 159 | 160 | 161 | const {container} = render() 162 | // focus on control input, open calendar 163 | const input = getInput(container) 164 | fireEvent.focus(input) 165 | 166 | 167 | // iter 12 months 168 | for(let monthIndex = 0; monthIndex < 12; monthIndex++) { 169 | // on the first loop we are already on the wanted month 170 | if (monthIndex>0) { 171 | // open calendar 172 | fireEvent.focus(input) 173 | 174 | // find month switchers 175 | const nextButtonElement = container.querySelector('div.rdp-header-next-wrapper') 176 | fireEvent.click(nextButtonElement) 177 | } 178 | // select any day in that month and get the value 179 | const day = getCalendarRandomDay(container) 180 | fireEvent.click(day) 181 | const randomMonthISOString = getHiddenInputValue(container, did) 182 | 183 | const randomMonthDate = new Date(randomMonthISOString) 184 | expect(randomMonthDate.getMonth()).to.equal(monthIndex) 185 | } 186 | 187 | 188 | }) 189 | 190 | it("should change month and year using default pick month element.", () => { 191 | const did = 'calendar-month-iter-using-pick-month' 192 | const value = "2019-07-15T00:00:00.000Z" 193 | const minDate = "2018-07-15T00:00:00.000Z" 194 | const maxDate = "2023-07-15T00:00:00.000Z" 195 | const Unit = () => 196 | 201 | 202 | const {container} = render() 203 | 204 | // focus on control input, open calendar 205 | const input = getInput(container) 206 | fireEvent.focus(input) 207 | 208 | // find month switcher 209 | const pickMonthElement = container.querySelector(`div.rdp-header-pick-month-default-month select`) 210 | 211 | // go to March 212 | fireEvent.change(pickMonthElement, { target: { name: 'rdp-header-pick-month-default-month', value: 2 }}) 213 | 214 | // select any day in that month and get the value 215 | const previousDay = getCalendarRandomDay(container) 216 | fireEvent.click(previousDay) 217 | const some2019MarchISOString = getHiddenInputValue(container, did) 218 | 219 | // reopen calendar 220 | fireEvent.focus(input) 221 | 222 | // find year switcher 223 | const pickYearElement = container.querySelector(`div.rdp-header-pick-month-default-year select`) 224 | 225 | // go to 2023 226 | fireEvent.change(pickYearElement, { target: { name: 'rdp-header-pick-month-default-year', value: 2023 }}) 227 | 228 | // select any day in that month and get the value 229 | const nextDay = getCalendarRandomDay(container) 230 | fireEvent.click(nextDay) 231 | const some2023ISOString = getHiddenInputValue(container, did) 232 | 233 | // check taht first date is actually older 234 | expect(some2019MarchISOString < some2023ISOString).to.equal(true) 235 | 236 | // check picked dates are alright 237 | const some2019MarchDate = new Date(some2019MarchISOString) 238 | const some2023Date = new Date(some2023ISOString) 239 | 240 | expect(some2019MarchDate.getMonth()).to.equal(2) 241 | expect(some2019MarchDate.getFullYear()).to.equal(2019) 242 | 243 | expect(some2023Date.getMonth()).to.equal(2) 244 | expect(some2023Date.getFullYear()).to.equal(2023) 245 | 246 | 247 | }) 248 | 249 | it("should display the correct day of the week in the calendar.", () => { 250 | 251 | const did = 'calndar-integrity-one' 252 | const Unit = () => 253 | 256 | 257 | const {container} = render() 258 | 259 | const input= getInput(container) 260 | 261 | const checkMonthAndYear = function(startValue) { 262 | fireEvent.change(input, {target: {value: `${startValue.slice(5,7)}/${startValue.slice(8,10)}/${startValue.slice(0,4)}`}}) 263 | fireEvent.focus(input) 264 | 265 | const calendar = getCalendar(container) 266 | 267 | const weeks = calendar.querySelectorAll("table tbody tr") 268 | 269 | weeks.forEach( (week, _weekIdx) => { 270 | const days= week.querySelectorAll('td') 271 | 272 | days.forEach( (day, dayIdx) => { 273 | const dayText = day.innerHTML 274 | 275 | if (dayText !== '') { 276 | 277 | fireEvent.click(day) 278 | 279 | const hiddenInputValue = getHiddenInputValue(container, did) 280 | let date = new Date(hiddenInputValue) 281 | 282 | expect(date.getDay()).to.equal(dayIdx) 283 | } 284 | }) 285 | }) 286 | } 287 | 288 | 289 | // const check5Years = () => { 290 | // const today = new Date() 291 | // for(let year = today.getFullYear() - 2; year < today.getFullYear() + 2; year++) { 292 | // for(let month = 0; month < 12; month++) { 293 | // const date = new Date() 294 | // date.setMonth(month) 295 | // date.setYear(year) 296 | // checkMonthAndYear(date.toISOString()) 297 | // } 298 | // } 299 | // } 300 | 301 | const checkSeveralDates = () => { 302 | checkMonthAndYear('2011-12-01T12:00:00.000Z') 303 | checkMonthAndYear('2012-11-02T12:00:00.000Z') 304 | checkMonthAndYear('2013-10-03T12:00:00.000Z') 305 | checkMonthAndYear('2014-09-04T12:00:00.000Z') 306 | checkMonthAndYear('2015-08-05T12:00:00.000Z') 307 | checkMonthAndYear('2016-07-06T12:00:00.000Z') 308 | checkMonthAndYear('2017-06-07T12:00:00.000Z') 309 | checkMonthAndYear('2018-05-08T12:00:00.000Z') 310 | checkMonthAndYear('2019-04-09T12:00:00.000Z') 311 | checkMonthAndYear('2020-03-10T12:00:00.000Z') 312 | checkMonthAndYear('2021-02-11T12:00:00.000Z') 313 | checkMonthAndYear('2022-01-12T12:00:00.000Z') 314 | } 315 | 316 | //check5Years() 317 | checkSeveralDates() 318 | 319 | 320 | }) 321 | }) -------------------------------------------------------------------------------- /test/units/integrity/typing.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../src/index' 3 | import { 4 | getInput 5 | } from '../../tools/finders' 6 | 7 | const { 8 | expect, 9 | render, 10 | fireEvent 11 | } = global 12 | 13 | describe('integrity: typing', function () { 14 | // this.timeout(100) 15 | 16 | it('should trim extra characters.', () => { 17 | const did = 'typing-one' 18 | const Unit = () => 19 | 21 | 22 | const {container} = render() 23 | 24 | const input = getInput(container) 25 | 26 | fireEvent.change(input, {target: {value: "05/31/1980 extra"}}) 27 | expect(input.value).to.equal("05/31/1980") 28 | }) 29 | 30 | 31 | it("should automatically insert slashes.", () => { 32 | const did = 'typing-two' 33 | const Unit = () => 34 | 36 | 37 | const {container} = render() 38 | 39 | const input= getInput(container) 40 | 41 | fireEvent.change(input, {target: {value: "05/31/1980 extra"}}) 42 | expect(input.value).to.equal("05/31/1980") 43 | 44 | fireEvent.change(input, {target: {value: "0"}}) 45 | fireEvent.change(input, {target: {value: "053"}}) 46 | expect(input.value).to.equal("05/3") 47 | 48 | fireEvent.change(input, {target: {value: "05/311"}}) 49 | expect(input.value).to.equal("05/31/1") 50 | }) 51 | }) -------------------------------------------------------------------------------- /test/units/properties/calendar/calendarContainer.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import { 5 | getInput 6 | } from '../../../tools/finders' 7 | 8 | 9 | const { 10 | expect, 11 | render, 12 | fireEvent 13 | } = global 14 | 15 | describe('props:calendar: calendar', function () { 16 | // this.timeout(500) 17 | 18 | it("should allow placing the popover calendar in a container specified in the props.", () => { 19 | const did = 'calendar-container' 20 | 21 | const Unit = () => 22 | <> 23 |
24 | 26 | 27 | const {container} = render() 28 | 29 | const input= getInput(container) 30 | fireEvent.focus(input) 31 | 32 | // check using document 33 | const calendar= container.querySelector(`div#heyho`) 34 | expect(calendar).to.exist 35 | expect(calendar.innerHTML.indexOf('rdp-popover')>=0).to.equal(true) 36 | 37 | // NOTE Dunno if intended/expected, but trough enzyme 38 | // wrappers the calendar is always inside the DatePicker tree 39 | // const calendar= container.querySelector('div#heyho') 40 | 41 | 42 | }) 43 | }) -------------------------------------------------------------------------------- /test/units/properties/calendar/calendarPlacement.cjs: -------------------------------------------------------------------------------- 1 | 2 | import React from 'react' 3 | import {DatePicker} from '../../../../src/index' 4 | 5 | import { 6 | getInput 7 | } from '../../../tools/finders' 8 | 9 | const { 10 | expect, 11 | render, 12 | fireEvent 13 | } = global 14 | 15 | 16 | describe('props:calendar: calendarPlacement', function () { 17 | // this.timeout(250) 18 | 19 | it("should allow for a string to determine calendar placement", () => { 20 | const did = 'calendar-placement-string' 21 | 22 | const Unit = () => 23 | 26 | 27 | const {container} = render() 28 | 29 | const input= getInput(container) 30 | fireEvent.focus(input) 31 | 32 | const calendar= container.querySelector(`div.rdp-popover.fade.right`) 33 | expect(calendar).to.exist 34 | 35 | 36 | }) 37 | 38 | 39 | it("should allow for a function to determine calendar placement", () => { 40 | const did = 'calendar-placement-function' 41 | const handlePlacement = () => "top" 42 | 43 | const Unit = () => 44 | 47 | 48 | const {container} = render() 49 | 50 | const input= getInput(container) 51 | fireEvent.focus(input) 52 | 53 | const calendar= container.querySelector(`div.rdp-popover.fade.top`) 54 | expect(calendar).to.exist 55 | 56 | 57 | }) 58 | }) -------------------------------------------------------------------------------- /test/units/properties/calendar/dayLabels.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import {getInput, getCalendar} from '../../../tools/finders' 5 | 6 | const { 7 | expect, 8 | render, 9 | fireEvent 10 | } = global 11 | 12 | 13 | describe('props:calendar: dayLabels', function () { 14 | // this.timeout(300) 15 | 16 | it("should render custom day labels", (done) => { 17 | //// this.timeout(300) 18 | 19 | const did = 'calendar-day-labels' 20 | const valueDate = new Date('2011-10-05T14:48:00.000Z') 21 | const spanishDayLabels = ['Dom', 'Lu', 'Ma', 'Mx', 'Ju', 'Vi', 'Sab'] 22 | 23 | const Unit = () => 24 | 28 | 29 | const {container} = render() 30 | 31 | // focus on control input, open calendar 32 | const input = getInput(container) 33 | fireEvent.focus(input) 34 | 35 | // get the calendar 36 | const calendar= getCalendar(container) 37 | 38 | // check the day titles 39 | const dayTitles= calendar.querySelectorAll('table thead tr')[0].querySelectorAll('td small') 40 | 41 | dayTitles.forEach( (dayWrap, dayIdx) => { 42 | expect(dayWrap.innerHTML).to.equal(spanishDayLabels[dayIdx]) 43 | }) 44 | 45 | 46 | 47 | done() 48 | }) 49 | 50 | }) 51 | -------------------------------------------------------------------------------- /test/units/properties/calendar/monthLabels.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import {getInput, getCalendar} from '../../../tools/finders' 5 | 6 | const { 7 | expect, 8 | render, 9 | fireEvent 10 | } = global 11 | 12 | 13 | describe('props:calendar: monthLabels', function () { 14 | // this.timeout(150) 15 | 16 | 17 | it("should render custom month labels", () => { 18 | const did = 'calendar-previous-button-element' 19 | const valueDate = new Date('2011-10-05T14:48:00.000Z') 20 | const spanishMonthLabels = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 21 | 'Junio', 'Julio', 'Agosto', 'Septiembre', 22 | 'Octubre', 'Noviembre', 'Diciembre'] 23 | 24 | const Unit = () => 25 | 29 | 30 | const {container} = render() 31 | 32 | // focus on control input, open calendar 33 | const input = getInput(container) 34 | fireEvent.focus(input) 35 | 36 | // get the calendar 37 | const calendar= getCalendar(container) 38 | 39 | // check the month title 40 | const currentMonthLabel = spanishMonthLabels[valueDate.getMonth()] 41 | const calendarTitle = calendar.querySelector('div.rdp-header-pick-month-wrapper div').innerHTML 42 | expect(calendarTitle.indexOf(currentMonthLabel)>=0).to.equal(true) 43 | 44 | 45 | }) 46 | 47 | }) -------------------------------------------------------------------------------- /test/units/properties/calendar/nextButtonElement.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import {getInput, getCalendar} from '../../../tools/finders' 5 | 6 | const { 7 | expect, 8 | render, 9 | fireEvent 10 | } = global 11 | 12 | 13 | describe('props:calendar: nextButtonElement', function () { 14 | // this.timeout(150) 15 | 16 | 17 | it("should render custom element nextButtonElement", () => { 18 | const did = 'calendar-next-button-element' 19 | const nextButtonElement =
20 | 21 | const Unit = () => 22 | 25 | 26 | const {container} = render() 27 | 28 | // focus on control input, open calendar 29 | const input = getInput(container) 30 | fireEvent.focus(input) 31 | 32 | // get the calendar 33 | const calendar= getCalendar(container) 34 | 35 | // check custom button is there 36 | expect(calendar.querySelector('#custom-next-button-element')).to.exist 37 | 38 | 39 | }) 40 | 41 | }) -------------------------------------------------------------------------------- /test/units/properties/calendar/pickMonthElement.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import {getInput, getCalendar} from '../../../tools/finders' 5 | 6 | const { 7 | expect, 8 | render, 9 | fireEvent 10 | } = global 11 | 12 | describe('props:calendar: pickMonthElement', function () { 13 | // this.timeout(150) 14 | 15 | 16 | it("should render custom element pickMonthElement", () => { 17 | const did = 'calendar-previous-button-element' 18 | const pickMonthElement = () =>
19 | 20 | const Unit = () => 21 | 24 | 25 | const {container} = render() 26 | 27 | // focus on control input, open calendar 28 | const input = getInput(container) 29 | fireEvent.focus(input) 30 | 31 | // get the calendar 32 | const calendar= getCalendar(container) 33 | 34 | // check custom button is there 35 | expect(calendar.querySelector('#custom-pick-month-element')).to.exist 36 | 37 | 38 | }) 39 | 40 | }) -------------------------------------------------------------------------------- /test/units/properties/calendar/previousButtonElement.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import {getInput, getCalendar} from '../../../tools/finders' 5 | 6 | const { 7 | expect, 8 | render, 9 | fireEvent 10 | } = global 11 | 12 | 13 | describe('props:calendar: previousButtonElement', function () { 14 | // this.timeout(150) 15 | 16 | 17 | it("should render custom element previousButtonElement", () => { 18 | const did = 'calendar-previous-button-element' 19 | const previousButtonElement =
20 | 21 | const Unit = () => 22 | 25 | 26 | const {container} = render() 27 | 28 | // focus on control input, open calendar 29 | const input = getInput(container) 30 | fireEvent.focus(input) 31 | 32 | // get the calendar 33 | const calendar= getCalendar(container) 34 | 35 | // check custom button is there 36 | expect(calendar.querySelector('#custom-previous-button-element')).to.exist 37 | 38 | 39 | }) 40 | 41 | }) -------------------------------------------------------------------------------- /test/units/properties/calendar/roundedCorners.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | const { 5 | expect, 6 | render, 7 | fireEvent 8 | } = global 9 | 10 | 11 | describe('props:calendar: roundedCorners', function () { 12 | // this.timeout(200) 13 | 14 | it("should allow for rounded corners.", () => { 15 | const withId = 'rdp-with-rounded-borders' 16 | const woutId = 'rdp-without-rounded-borders' 17 | 18 | const Unit = () => 19 |
20 | 22 | 23 |
24 | 25 | const {container} = render() 26 | 27 | const checkForInputId = (id, expected) => { 28 | // focus and open calendar 29 | const input = container.querySelector("input#rdp-form-control-" + id) 30 | fireEvent.focus(input) 31 | 32 | // get and click some day in the calendar 33 | const calendar = container.querySelector("input#" + id).closest('.input-group').querySelector('div.rdp-popover') 34 | const day= calendar.querySelectorAll(`table tbody tr:last-child td`)[0] 35 | fireEvent.click(day) 36 | 37 | // check borders on the day cell 38 | expect(day.style.borderRadius).to.equal(expected) 39 | } 40 | 41 | checkForInputId(withId, '5px') 42 | checkForInputId(woutId, '0px') 43 | 44 | 45 | }) 46 | 47 | }) -------------------------------------------------------------------------------- /test/units/properties/calendar/showTodayButton.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import { 5 | getCalendar, 6 | getInput 7 | } from '../../../tools/finders' 8 | 9 | const { 10 | expect, 11 | render, 12 | fireEvent 13 | } = global 14 | 15 | 16 | describe('props:calendar: showTodayButton / todayButtonLabel', function () { 17 | // this.timeout(150) 18 | 19 | 20 | it('should render with today button element', () => { 21 | const did = 'today-button' 22 | const todayButtonLabel = "Today is the day" 23 | 24 | const Unit = () => 25 | 29 | 30 | const {container} = render() 31 | 32 | // focus and open calendar 33 | const input= getInput(container) 34 | fireEvent.focus(input) 35 | 36 | // get today button 37 | const calendar= getCalendar(container) 38 | const today = calendar.querySelector('button.u-today-button') 39 | 40 | // check the label 41 | expect(today.innerHTML).to.equal(todayButtonLabel) 42 | 43 | 44 | }) 45 | }) -------------------------------------------------------------------------------- /test/units/properties/calendar/weekStartsOn.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import { 5 | getHiddenInputValue, 6 | getInput, 7 | getCalendar, 8 | getCalendarDayHeader 9 | } from '../../../tools/finders' 10 | 11 | 12 | const { 13 | expect, 14 | render, 15 | fireEvent 16 | } = global 17 | 18 | 19 | describe('props:calendar: weekStartsOn', function () { 20 | //// this.timeout(10000) 21 | this.timeout(3000) 22 | 23 | it("week should start on Monday.", () => { 24 | const did = 'week-starts-monday' 25 | 26 | const Unit = () => 27 | 30 | 31 | const {container} = render() 32 | 33 | // focus on control input, open calendar 34 | const input = getInput(container) 35 | fireEvent.focus(input) 36 | 37 | // get the first day header in the calendar 38 | const firstDay = getCalendarDayHeader(container, 0) 39 | expect(firstDay.querySelector('small').innerHTML).to.equal('Mon') 40 | }) 41 | 42 | it("week should start on Thursday.", () => { 43 | const did = 'week-starts-thursday' 44 | 45 | const Unit = () => 46 | 49 | 50 | const {container} = render() 51 | 52 | // focus on control input, open calendar 53 | const input = getInput(container) 54 | fireEvent.focus(input) 55 | 56 | // get the first day header in the calendar 57 | const firstDay = getCalendarDayHeader(container, 0) 58 | 59 | expect(firstDay.querySelector('small').innerHTML).to.equal('Thu') 60 | 61 | 62 | }) 63 | 64 | it("week should start on Saturday.", () => { 65 | const did = 'week-starts-saturday' 66 | 67 | const Unit = () => 68 | 71 | 72 | const {container} = render() 73 | 74 | // focus on control input, open calendar 75 | const input = getInput(container) 76 | fireEvent.focus(input) 77 | 78 | // get the first day header in the calendar 79 | const firstDay = getCalendarDayHeader(container, 0) 80 | 81 | expect(firstDay.querySelector('small').innerHTML).to.equal('Sat') 82 | 83 | 84 | }) 85 | 86 | it("should display the correct day of the week in the calendar when starting on Monday.", () => { 87 | const did = 'correct-week-day' 88 | 89 | let _value = null; 90 | let _formattedValue = null; 91 | 92 | const Unit = () => 93 | {_value= v; _formattedValue= f}} 95 | dateFormat="MM/DD/YYYY" 96 | weekStartsOn={1} 97 | /> 98 | 99 | const {container} = render() 100 | 101 | // get some s 102 | const input = getInput(container) 103 | 104 | const checkMonthAndYear = function(startValue) { 105 | 106 | fireEvent.change(input, {target: {value: `${startValue.slice(5,7)}/${startValue.slice(8,10)}/${startValue.slice(0,4)}`}}) 107 | fireEvent.focus(input) 108 | 109 | const calendar = getCalendar(container) 110 | 111 | const weeks = calendar.querySelectorAll("table tbody tr") 112 | 113 | weeks.forEach( (week, _weekIdx) => { 114 | const days= week.querySelectorAll('td') 115 | 116 | days.forEach( (day, dayIdx) => { 117 | const dayText = day.innerHTML 118 | 119 | if (dayText !== '') { 120 | 121 | fireEvent.click(day) 122 | 123 | const hiddenInputValue = getHiddenInputValue(container, did) 124 | let date = new Date(hiddenInputValue) 125 | 126 | const dayN= dayIdx === 6 ? 0 : dayIdx + 1 127 | expect(date.getDay()).to.equal(dayN) 128 | } 129 | }) 130 | }) 131 | } 132 | 133 | // const check5Years = () => { 134 | // const today = new Date() 135 | // for(let year = today.getFullYear() - 2; year < today.getFullYear() + 2; year++) { 136 | // for(let month = 0; month < 12; month++) { 137 | // const date = new Date() 138 | // date.setMonth(month) 139 | // date.setYear(year) 140 | // checkMonthAndYear(date.toISOString()) 141 | // } 142 | // } 143 | // } 144 | 145 | const checkSeveralDates = () => { 146 | checkMonthAndYear('2011-12-01T12:00:00.000Z') 147 | checkMonthAndYear('2012-11-02T12:00:00.000Z') 148 | checkMonthAndYear('2013-10-03T12:00:00.000Z') 149 | checkMonthAndYear('2014-09-04T12:00:00.000Z') 150 | checkMonthAndYear('2015-08-05T12:00:00.000Z') 151 | checkMonthAndYear('2016-07-06T12:00:00.000Z') 152 | checkMonthAndYear('2017-06-07T12:00:00.000Z') 153 | checkMonthAndYear('2018-05-08T12:00:00.000Z') 154 | checkMonthAndYear('2019-04-09T12:00:00.000Z') 155 | checkMonthAndYear('2020-03-10T12:00:00.000Z') 156 | checkMonthAndYear('2021-02-11T12:00:00.000Z') 157 | checkMonthAndYear('2022-01-12T12:00:00.000Z') 158 | } 159 | 160 | //check5Years() 161 | checkSeveralDates() 162 | 163 | 164 | }) 165 | }) -------------------------------------------------------------------------------- /test/units/properties/events/changes.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import { 5 | getInput, 6 | getCalendarRandomDay 7 | } from '../../../tools/finders' 8 | 9 | const { 10 | expect, 11 | render, 12 | fireEvent 13 | } = global 14 | 15 | 16 | describe('props:events: changes', function () { 17 | // this.timeout(500) 18 | 19 | it("should update via a change handler when the input is changed.", () => { 20 | const did = 'changes-one' 21 | let value = null; 22 | let formattedValue = null; 23 | 24 | const Unit = () => 25 | {value= v; formattedValue= f}} 27 | dateFormat="MM/DD/YYYY" 28 | /> 29 | 30 | const {container} = render() 31 | 32 | const input= getInput(container) 33 | 34 | fireEvent.change(input, {target: {value: "05/31/1980"}}) 35 | 36 | const date = new Date(value) 37 | expect(date.getMonth() ).to.equal(4) 38 | expect(date.getDate() ).to.equal(31) 39 | expect(date.getFullYear()).to.equal(1980) 40 | expect(formattedValue ).to.equal("05/31/1980") 41 | }) 42 | 43 | it("should update via a change handler when cleared.", () => { 44 | const did = 'changes-two' 45 | let value = null; 46 | let formattedValue = null; 47 | 48 | const Unit = () => 49 | {value= v; formattedValue= f}} 51 | dateFormat="MM/DD/YYYY" 52 | /> 53 | 54 | const {container} = render() 55 | 56 | const input= getInput(container) 57 | fireEvent.focus(input) 58 | 59 | const clearButton = container.querySelector("span.input-group-text") 60 | const day= getCalendarRandomDay(container) 61 | 62 | fireEvent.click(day) 63 | expect(value ).to.exist 64 | expect(formattedValue).to.exist 65 | 66 | fireEvent.click(clearButton) 67 | expect(value ).to.equal(null) 68 | expect(formattedValue).to.equal(null) 69 | 70 | 71 | }) 72 | }) -------------------------------------------------------------------------------- /test/units/properties/events/focus.cjs: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | import {assertIsoStringsHaveSameDate} from '../../../tools/checks' 4 | 5 | import { 6 | getInput 7 | } from '../../../tools/finders' 8 | 9 | const { 10 | expect, 11 | render, 12 | fireEvent 13 | } = global 14 | 15 | 16 | describe('props:events: focus', function () { 17 | // this.timeout(500) 18 | 19 | 20 | it("should call focus and blur handlers.", () => { 21 | const did = 'focus-one' 22 | let value = `${new Date().toISOString().slice(0,10)}T12:00:00.000Z` 23 | 24 | const Unit = () => { 25 | const [focused, setFocused]= useState(false) 26 | const [foo, _setFoo]= useState('foo') 27 | 28 | const focusHandler = (e) => { 29 | //expect(e.target).to.equal(container.querySelector("input[type=hidden]")) 30 | expect(e.target).to.equal(container.querySelector("input.form-control")) 31 | assertIsoStringsHaveSameDate(e.target.value, value) 32 | setFocused(true) 33 | } 34 | 35 | const blurHandler = (e) => { 36 | //expect(e.target).to.equal(container.querySelector("input[type=hidden]")) 37 | expect(e.target).to.equal(container.querySelector("input.form-control")) 38 | assertIsoStringsHaveSameDate(e.target.value, value) 39 | setFocused(false) 40 | } 41 | 42 | return ( 43 |
44 |
45 | setFocused(false)} 48 | onClick={() => setFocused(false)}> 49 | {focused 50 | ?
Focused
51 | :
Blurred
52 | } 53 |
54 | blurHandler(e)} 56 | onFocus= {(e) => focusHandler(e)} 57 | value = {value} /> 58 |
59 | ) 60 | } 61 | 62 | const {container} = render() 63 | const input= getInput(container) 64 | 65 | const focus_it = () => { 66 | input.focus() 67 | fireEvent.focus(input) // This is not enough to update document.activeElement, dunno why yet 68 | } 69 | 70 | const check_it_is_blurred_soft = () => expect(container.querySelector('div#blurred').length, 'checking blurred (soft)').to.not.equal(0) 71 | const check_it_is_focused_soft = () => expect(container.querySelector('div#focused').length, 'checking focused (soft)').to.not.equal(0) 72 | 73 | const check_it_is_blurred_hard = () => expect(input, 'checking blurred (hard)').to.not.equal(document.activeElement) 74 | const check_it_is_focused_hard = () => expect(input, 'checking focused (hard)').to.equal(document.activeElement) 75 | 76 | // It should start blurred 77 | check_it_is_blurred_soft() 78 | check_it_is_blurred_hard() 79 | 80 | // Let's focus it 81 | focus_it() 82 | check_it_is_focused_soft() 83 | check_it_is_focused_hard() 84 | 85 | // Let's blur it by focusing external element 86 | const blurringClick = container.querySelector('input#blurringClickTarget') 87 | blurringClick.focus() 88 | fireEvent.focus(blurringClick) 89 | 90 | check_it_is_blurred_soft() 91 | check_it_is_blurred_hard() 92 | 93 | 94 | }) 95 | }) -------------------------------------------------------------------------------- /test/units/properties/globals/clearButtonElement.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | const { 5 | expect, 6 | render 7 | } = global 8 | 9 | describe('props:globals: clearButtonElement', function () { 10 | // this.timeout(100) 11 | 12 | 13 | it("should render with custom elements", () => { 14 | const did = 'clear-button' 15 | const clearButtonElement =
16 | const valueDate= new Date('2011-10-05T14:48:00.000Z') 17 | 18 | const Unit = () => 19 | 23 | 24 | const {container} = render() 25 | 26 | const clear= container.querySelector('#clear-button-element') 27 | expect(clear).to.exist 28 | 29 | 30 | }) 31 | 32 | 33 | it("should render without clear button element", () => { 34 | const did = 'clear-button-hidden' 35 | const clearButtonElement =
36 | const valueDate= new Date('2011-10-05T14:48:00.000Z') 37 | 38 | const Unit = () => 39 | 44 | 45 | const {container} = render() 46 | 47 | const clear= container.querySelector('#clear-button-element') 48 | expect(clear).to.not.exist 49 | }) 50 | }) -------------------------------------------------------------------------------- /test/units/properties/globals/dateFormat.cjs: -------------------------------------------------------------------------------- 1 | import React, {useState, useRef} from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | import {assertIsoStringsHaveSameDate} from '../../../tools/checks' 4 | 5 | import { 6 | getInput 7 | } from '../../../tools/finders' 8 | 9 | const { 10 | expect, 11 | render, 12 | fireEvent 13 | } = global 14 | 15 | 16 | describe('props:globals: dateFormat', function () { 17 | // this.timeout(500) 18 | 19 | it("should automatically insert in YYYY/MM/DD format.", () => { 20 | const did = 'date-formats-one' 21 | const Unit = () => 22 | 25 | 26 | const {container} = render() 27 | 28 | const input= getInput(container) 29 | 30 | fireEvent.change(input, {target: {value: '0'}}) 31 | 32 | fireEvent.change(input, {target: {value: '19800'}}) 33 | expect(input.value).to.equal('1980/0') 34 | 35 | fireEvent.change(input, {target: {value: '1980/053'}}) 36 | expect(input.value).to.equal('1980/05/3') 37 | }) 38 | 39 | it("should render dates in different formats.", () => { 40 | const FORMATS= { 41 | d1: 'MM/DD/YYYY', 42 | d2: 'DD/MM/YYYY', 43 | d3: 'YYYY/MM/DD' 44 | } 45 | const did1 = 'date-formats-d1' 46 | const did2 = 'date-formats-d2' 47 | const did3 = 'date-formats-d3' 48 | const values = {} 49 | const formattedValues= {} 50 | const getValues = {} 51 | const getFormattedValues= {} 52 | 53 | const Unit = () => { 54 | const [value1, setValue1]= useState(null) 55 | const [value2, setValue2]= useState(null) 56 | const [value3, setValue3]= useState(null) 57 | 58 | const ref1= useRef() 59 | const ref2= useRef() 60 | const ref3= useRef() 61 | 62 | const handleCLick = () => { 63 | if (ref1.current) { 64 | getValues[FORMATS.d1] = ref1.current.getValue() 65 | getFormattedValues[FORMATS.d1] = ref1.current.getFormattedValue() 66 | } 67 | 68 | if (ref2.current) { 69 | getValues[FORMATS.d2] = ref2.current.getValue() 70 | getFormattedValues[FORMATS.d2] = ref2.current.getFormattedValue() 71 | } 72 | 73 | if (ref3.current) { 74 | getValues[FORMATS.d3] = ref3.current.getValue() 75 | getFormattedValues[FORMATS.d3] = ref3.current.getFormattedValue() 76 | } 77 | } 78 | 79 | return ( 80 | <> 81 | {setValue1(v); values[FORMATS.d1] = v; formattedValues[FORMATS.d1]= f}} 86 | value = {value1}/> 87 | {setValue2(v); values[FORMATS.d2] = v; formattedValues[FORMATS.d2]= f}} 92 | value = {value2}/> 93 | {setValue3(v); values[FORMATS.d3] = v; formattedValues[FORMATS.d3]= f}} 98 | value = {value3}/> 99 | } 18 | /> 19 | 20 | const {container} = render() 21 | 22 | const customControl = container.querySelector('button#test-btn') 23 | 24 | expect(customControl).to.exist 25 | expect(customControl.innerHTML).to.equal('Test button') 26 | }) 27 | }) -------------------------------------------------------------------------------- /test/units/properties/input/disabled.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | import {assertIsoStringsHaveSameDate} from '../../../tools/checks' 4 | 5 | import { 6 | getInput 7 | } from '../../../tools/finders' 8 | 9 | const { 10 | expect, 11 | render, 12 | fireEvent 13 | } = global 14 | 15 | describe('props:input: disabled', function () { 16 | // this.timeout(100) 17 | 18 | it("should disable the input.", () => { 19 | const did = 'disabled-one' 20 | const valueDate= new Date('2011-10-05T14:48:00.000Z') 21 | 22 | const Unit = () => 23 | 27 | 28 | const {container} = render() 29 | 30 | const input= getInput(container) 31 | expect(input.disabled).to.equal(true) 32 | 33 | 34 | }) 35 | 36 | it("should disable the input and ensure clear button is not clickable.", () => { 37 | const did = 'disabled-two' 38 | let valueDate= new Date('2011-10-05T14:48:00.000Z') 39 | const originalValue= valueDate.toISOString() 40 | 41 | const Unit = () => 42 | valueDate = v} 46 | /> 47 | 48 | const {container} = render() 49 | 50 | const input= getInput(container) 51 | expect(input.disabled).to.equal(true) 52 | 53 | const clearButton = container.querySelector("span.input-group-text") 54 | fireEvent.click(clearButton) 55 | 56 | assertIsoStringsHaveSameDate(valueDate, originalValue) 57 | 58 | 59 | }) 60 | }) -------------------------------------------------------------------------------- /test/units/properties/input/inputRef.cjs: -------------------------------------------------------------------------------- 1 | import React, {useState, useRef} from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import { 5 | getInput 6 | } from '../../../tools/finders' 7 | 8 | const { 9 | expect, 10 | render, 11 | fireEvent 12 | } = global 13 | 14 | 15 | describe('props:input: inputRef', function () { 16 | // this.timeout(500) 17 | 18 | 19 | it("should check a forwarded ref and inputRef both points to the same element", () => { 20 | const did = 'using-input-ref' 21 | 22 | const Unit = () => { 23 | const [value, setValue]= useState('2015-04-15T12:00:00.000Z') 24 | 25 | const mainRef= useRef() 26 | const inputRef= useRef() 27 | 28 | const handleChange = (newValue) => { 29 | setValue(newValue) 30 | expect(mainRef.current.getNode()).to.equal(inputRef.current) 31 | } 32 | 33 | return ( 34 | <> 35 | handleChange(v)} 40 | value = {value}/> 41 | 42 | )} 43 | 44 | const {container} = render() 45 | 46 | const input= getInput(container) 47 | fireEvent.focus(input) 48 | 49 | fireEvent.change(input, {target: {value: '01/01/2000'}}) 50 | 51 | fireEvent.change(input, {target: {value: '01/01/2000'}}) 52 | 53 | 54 | }) 55 | }) -------------------------------------------------------------------------------- /test/units/properties/input/style.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import { 5 | getInput, 6 | } from '../../../tools/finders' 7 | 8 | const { 9 | expect, 10 | render 11 | } = global 12 | 13 | describe('props:input: style', function () { 14 | // this.timeout(100) 15 | 16 | it("should set the FormControl style.", () => { 17 | const did = 'style' 18 | const backgroundColor = `rgb(${Math.round(Math.random() * 255, 0)}, ${Math.round(Math.random() * 255, 0)}, ${Math.round(Math.random() * 255, 0)})` 19 | 20 | const Unit = () => 21 | 24 | 25 | const {container} = render() 26 | 27 | const input= getInput(container) 28 | expect(input.style.backgroundColor).to.equal(backgroundColor) 29 | 30 | 31 | }) 32 | 33 | }) -------------------------------------------------------------------------------- /test/units/properties/inputGroup/customInputGroup.cjs: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {DatePicker} from '../../../../src/index' 3 | 4 | import { 5 | getInput 6 | } from '../../../tools/finders' 7 | 8 | const { 9 | expect, 10 | render 11 | } = global 12 | 13 | 14 | describe('props:inputGroup: customInputGroup', function () { 15 | // this.timeout(100) 16 | 17 | 18 | it('should render a custom inpupt group element', () => { 19 | const did = 'custom-input-group-hidden' 20 | 21 | const Unit = () => 22 |
} 24 | /> 25 | 26 | const {container} = render() 27 | 28 | const customIGroup = container.querySelector('#custom-input-group') 29 | expect(customIGroup).to.exist 30 | 31 | const input= getInput(container) 32 | expect(input).to.exist 33 | 34 | 35 | }) 36 | }) -------------------------------------------------------------------------------- /xeira.json: -------------------------------------------------------------------------------- 1 | { 2 | "product": "package", 3 | "target": "both", 4 | "source_index": "./src/index.mjs", 5 | "linter": "eslint", 6 | "transpile_folder": "./lib", 7 | "transpiler": "babel", 8 | "minifier": "none", 9 | "bundle_folder": "./dist", 10 | "bundler": "rollup", 11 | "test_folder": "./test", 12 | "demo_mode": "auto", 13 | "demo_demoer": "rollup", 14 | "verbose": false 15 | } 16 | --------------------------------------------------------------------------------