Repository: gpbl/react-day-picker (ts)

Issue:
Move input related props to `inputProps`
In DayPickerInput, we are spreading input props from the component, e.g.:

```jsx
// Will render 
// <input name="birthday" autocomplete={false} placeholder="Enter a date" />
<DayPickerInput 
    name="birthday"
    autocomplete={false}
    placeholder="Enter a date"
/>
```

We are also using props that are not compatible with `input`, or that may override `input` props:

```jsx
<DayPickerInput 
    format="LL" /* format will not be passed down to <input> */
    name="birthday"
    autocomplete={false}
    placeholder="Enter a date"
/>
```

This may create issues as React 16 now supports custom dom attributes should pass https://reactjs.org/blog/2017/09/26/react-v16.0.html#support-for-custom-dom-attributes

So to fix it, we should add a `inputProps` prop similar to what `dayPickerProps` does:

**Before**

```jsx
<DayPickerInput 
    name="birthday"
    autocomplete={false}
    placeholder="Enter a date"
/>
```

**After**

```jsx
<DayPickerInput 
    inputProps={{
        name: 'birthday',
        autocomplete: false,
        placeholder: 'Enter a date'
    }}
/>
```

Pull request description:
Add `inputProps` prop
Close #508 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d0adccdc..65d1b6aae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-## Next version
+# Next version
 
 _The following changes are not released yet, but are on the master branch and
 will be released soon._
@@ -31,41 +31,52 @@ will be released soon._
   ([#548](https://github.com/gpbl/react-day-picker/issues/548) by
   [trezy](https://github.com/trezy))
 
-**DayPickerInput**
+### DayPickerInput
 
-* New:
+* **Breaking:** pass additional props to the input component using the
+  [`inputProps`](http://react-day-picker.js.org/api/DayPickerInput#inputProps)
+  prop. _This is not a breaking change if you are just using `placeholder` or
+  `value`_.
+
+- New:
+  [`inputProps`](http://react-day-picker.js.org/api/DayPickerInput#inputProps)
+  prop to pass additional props to the input component
+- New:
+  [`placeholder`](http://react-day-picker.js.org/api/DayPickerInput#placeholder)
+  prop
+- New:
   [`overlayComponent`](http://react-day-picker.js.org/api/DayPickerInput#overlayComponent)
   prop: useful to customize the overlay component
   ([#477](https://github.com/gpbl/react-day-picker/issues/477), thanks to
   [wldcordeiro](https://github.com/wldcordeiro))
-* New: allow to change `numberOfMonths`, `selectedDays` props from
+- New: allow to change `numberOfMonths`, `selectedDays` props from
   `dayPickerProps` ([#513](https://github.com/gpbl/react-day-picker/issues/513),
   [#531](https://github.com/gpbl/react-day-picker/issues/531) by
   [hydrognomik](https://github.com/hydrognomik)). Useful for selecting range of
   days ([example](http://react-day-picker.js.org/examples/input-from-to)).
-* New:
+- New:
   [`showOverlay`](http://react-day-picker.js.org/api/DayPickerInput#showOverlay)
   prop: shows the overlay at the initial rendering (useful for styling)
-* New: [`getInput`](http://react-day-picker.js.org/api/DayPickerInput#getInput)
+- New: [`getInput`](http://react-day-picker.js.org/api/DayPickerInput#getInput)
   and
   [`getDayPicker`](http://react-day-picker.js.org/api/DayPickerInput#getDayPicker)
   public methods
 
-- Changed: clicking the Today Button will set the input value to today
+* Changed: clicking the Today Button will set the input value to today
   ([#561](https://github.com/gpbl/react-day-picker/issues/561))
-- Changed: removed `fixedWeek` prop. Use `dayPickerProps ={{ fixedWeek: true }}`
+* Changed: removed `fixedWeek` prop. Use `dayPickerProps ={{ fixedWeek: true }}`
   to restore it.
 
-* Fixed: some modifiers were not passed down when using a custom `classNames`
+- Fixed: some modifiers were not passed down when using a custom `classNames`
   ([#517](https://github.com/gpbl/react-day-picker/issues/517),
   [#504](https://github.com/gpbl/react-day-picker/issues/504) by
   [tume](https://github.com/tume))
-* Fixed: focus behavior on Firefox
+- Fixed: focus behavior on Firefox
   ([#525](https://github.com/gpbl/react-day-picker/issues/525) by
   [martinmosko](https://github.com/martinmosko))
-* Fixed: value not updated when changed in some cases
+- Fixed: value not updated when changed in some cases
   ([#535](https://github.com/gpbl/react-day-picker/issues/535))
-* Fixed: localization bug when using multiple languages
+- Fixed: localization bug when using multiple languages
   ([#509](https://github.com/gpbl/react-day-picker/issues/509))
 
 ---
diff --git a/docs/src/pages/api/DayPickerInput.js b/docs/src/pages/api/DayPickerInput.js
index d464617be..50f480c25 100644
--- a/docs/src/pages/api/DayPickerInput.js
+++ b/docs/src/pages/api/DayPickerInput.js
@@ -13,17 +13,15 @@ export default () => (
     >{`import DayPickerInput from 'react-day-picker/DayPickerInput'`}</CodeBlock>
     <h2>API summary</h2>
     <h4>Component’s Props</h4>
-    <p>
-      <code>{`<DayPickerInput />`}</code> accepts all the props accepted by a
-      standard <code>{`<input/>`}</code>, plus the followings:
-    </p>
     <p>
       <a href="#classNames">classNames</a>,{' '}
       <a href="#clickUnselectsDay">clickUnselectsDay</a>,{' '}
       <a href="#component">component</a>,{' '}
       <a href="#dayPickerProps">dayPickerProps</a>, <a href="#format">format</a>,{' '}
       <a href="#hideOnDayClick">hideOnDayClick</a>,{' '}
+      <a href="#inputProps">inputProps</a>,{' '}
       <a href="#overlayComponent">overlayComponent</a>,{' '}
+      <a href="#placeholder">placeholder</a>,{' '}
       <a href="#showOverlay">showOverlay</a>
     </p>
     <h4>Event handlers</h4>
@@ -126,6 +124,18 @@ function MyDayPickerInput(props) {
         hideOnDayClick <code>boolean = true</code>
       </h3>
       <p>Hide the overlay when the user clicks on a day cell.</p>
+      <h3>
+        <Anchor id="inputProps" />
+        inputProps <code>Object</code>
+      </h3>
+      <p>
+        Additional props to add to the <code>input</code> component. The{' '}
+        <code>value</code> key is ignored: use the{' '}
+        <a href="#value">
+          <code>value</code>
+        </a>{' '}
+        prop instead.
+      </p>
 
       <h3>
         <Anchor id="overlayComponent" />
@@ -149,6 +159,13 @@ function MyDayPickerInput(props) {
       <p>
         See also <Link to="/examples/input-custom-overlay">this example</Link>.
       </p>
+      <h3>
+        <Anchor id="placeholder" />
+        placeholder <code>string</code>
+      </h3>
+      <p>
+        The placeholder to use for the <code>input</code> field.
+      </p>
       <h3>
         <Anchor id="showOverlay" />
         showOverlay <code>boolean = false</code>
diff --git a/src/DayPickerInput.js b/src/DayPickerInput.js
index c0cf3fa36..00f4d9857 100644
--- a/src/DayPickerInput.js
+++ b/src/DayPickerInput.js
@@ -13,6 +13,8 @@ export const HIDE_TIMEOUT = 100;
 export default class DayPickerInput extends React.Component {
   static propTypes = {
     value: PropTypes.string,
+    inputProps: PropTypes.object,
+    placeholder: PropTypes.string,
 
     format: PropTypes.oneOfType([
       PropTypes.arrayOf(PropTypes.string),
@@ -48,6 +50,7 @@ export default class DayPickerInput extends React.Component {
     hideOnDayClick: true,
     clickUnselectsDay: false,
     component: 'input',
+    inputProps: {},
     overlayComponent: ({ children, classNames }) => (
       <div className={classNames.overlayWrapper}>
         <div className={classNames.overlay}>{children}</div>
@@ -216,17 +219,17 @@ export default class DayPickerInput extends React.Component {
 
   handleInputClick(e) {
     this.showDayPicker();
-    if (this.props.onClick) {
+    if (this.props.inputProps.onClick) {
       e.persist();
-      this.props.onClick(e);
+      this.props.inputProps.onClick(e);
     }
   }
 
   handleInputFocus(e) {
     this.showDayPicker();
-    if (this.props.onFocus) {
+    if (this.props.inputProps.onFocus) {
       e.persist();
-      this.props.onFocus(e);
+      this.props.inputProps.onFocus(e);
     }
   }
 
@@ -238,17 +241,17 @@ export default class DayPickerInput extends React.Component {
     if (this.clickedInside) {
       this.blurTimeout = setTimeout(() => this.input.focus(), 0);
     }
-    if (this.props.onBlur) {
+    if (this.props.inputProps.onBlur) {
       e.persist();
-      this.props.onBlur(e);
+      this.props.inputProps.onBlur(e);
     }
   }
 
   handleInputChange(e) {
-    const { onChange, onDayChange, format } = this.props;
-    if (onChange) {
+    const { inputProps, onDayChange, format } = this.props;
+    if (inputProps.onChange) {
       e.persist();
-      onChange(e);
+      inputProps.onChange(e);
     }
     const { value } = e.target;
     const m = moment(value, format, true);
@@ -270,9 +273,9 @@ export default class DayPickerInput extends React.Component {
   handleInputKeyUp(e) {
     // Hide the overlay if the ESC key is pressed
     this.setState({ showOverlay: e.keyCode !== ESC });
-    if (this.props.onKeyUp) {
+    if (this.props.inputProps.onKeyUp) {
       e.persist();
-      this.props.onKeyUp(e);
+      this.props.inputProps.onKeyUp(e);
     }
   }
 
@@ -367,17 +370,6 @@ export default class DayPickerInput extends React.Component {
   }
 
   render() {
-    const {
-      component,
-      overlayComponent,
-      dayPickerProps,
-      format,
-      clickUnselectsDay,
-      hideOnDayClick,
-      onDayChange,
-      classNames,
-      ...inputProps
-    } = this.props;
     const Input = this.props.component;
     return (
       <div
@@ -386,7 +378,8 @@ export default class DayPickerInput extends React.Component {
       >
         <Input
           ref={el => (this.input = el)}
-          {...inputProps}
+          placeholder={this.props.placeholder}
+          {...this.props.inputProps}
           value={this.state.value}
           onChange={this.handleInputChange}
           onFocus={this.handleInputFocus}
diff --git a/src/Month.js b/src/Month.js
index e50e84518..82f987138 100644
--- a/src/Month.js
+++ b/src/Month.js
@@ -25,7 +25,7 @@ export default class Month extends Component {
 
     modifiersStyles: PropTypes.object,
 
-    showWeekdays: PropTypes.bool,
+    showWeekDays: PropTypes.bool,
     enableOutsideDays: PropTypes.bool,
 
     renderDay: PropTypes.func.isRequired,


Test patch:
diff --git a/test/daypickerinput/events.js b/test/daypickerinput/events.js
index 4dc0ab926..8dc46dd34 100644
--- a/test/daypickerinput/events.js
+++ b/test/daypickerinput/events.js
@@ -17,7 +17,7 @@ describe('DayPickerInput', () => {
       });
       it('should call `onClick` event handler', () => {
         const onClick = jest.fn();
-        const wrapper = mount(<DayPickerInput onClick={onClick} />);
+        const wrapper = mount(<DayPickerInput inputProps={{ onClick }} />);
         wrapper.find('input').simulate('click');
         expect(onClick).toHaveBeenCalledTimes(1);
       });
@@ -31,7 +31,7 @@ describe('DayPickerInput', () => {
       });
       it('should call `onFocus` event handler', () => {
         const onFocus = jest.fn();
-        const wrapper = mount(<DayPickerInput onFocus={onFocus} />);
+        const wrapper = mount(<DayPickerInput inputProps={{ onFocus }} />);
         wrapper.find('input').simulate('focus');
         expect(onFocus).toHaveBeenCalledTimes(1);
       });
@@ -46,7 +46,7 @@ describe('DayPickerInput', () => {
       });
       it('should call `onBlur` event handler', () => {
         const onBlur = jest.fn();
-        const wrapper = mount(<DayPickerInput onBlur={onBlur} />);
+        const wrapper = mount(<DayPickerInput inputProps={{ onBlur }} />);
         wrapper.find('input').simulate('blur');
         expect(onBlur).toHaveBeenCalledTimes(1);
       });
@@ -67,7 +67,7 @@ describe('DayPickerInput', () => {
     describe('change', () => {
       it('should call `onChange` event handler', () => {
         const onChange = jest.fn();
-        const wrapper = mount(<DayPickerInput onChange={onChange} />);
+        const wrapper = mount(<DayPickerInput inputProps={{ onChange }} />);
         wrapper.find('input').simulate('change');
         expect(onChange).toHaveBeenCalledTimes(1);
       });
@@ -147,7 +147,7 @@ describe('DayPickerInput', () => {
       });
       it('should call `onKeyUp` event handler', () => {
         const onKeyUp = jest.fn();
-        const wrapper = mount(<DayPickerInput onKeyUp={onKeyUp} />);
+        const wrapper = mount(<DayPickerInput inputProps={{ onKeyUp }} />);
         wrapper.find('input').simulate('keyup');
         expect(onKeyUp).toHaveBeenCalledTimes(1);
       });