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

Issue:
Move interactive element handlers into a child of the container
This code in `DayPicker.js`:

```jsx
<div
  {...this.props.containerProps}
  ref={el => (this.dayPicker = el)}
  role="application"
  lang={this.props.locale}
  className={className}
  tabIndex={this.props.canChangeMonth && this.props.tabIndex}
  onKeyDown={this.handleKeyDown}
  onFocus={this.props.onFocus}
  onBlur={this.props.onBlur}
>
    {this.renderNavbar()}
    {this.renderMonths()}
</div>
```

is causing the eslint rule [jsx-a11y/no-noninteractive-element-interactions](https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-noninteractive-element-interactions.md) to fail because the `<div>` has a `role="application"` with element handlers in it. The correct HTML would be instead:

```jsx
<div
  {...this.props.containerProps}
  className={className}
  ref={el => (this.dayPicker = el)}
  role="application"
  lang={this.props.locale}
>
  <div
    tabIndex={this.props.canChangeMonth && this.props.tabIndex}
    onKeyDown={this.handleKeyDown}
    onFocus={this.props.onFocus}
    onBlur={this.props.onBlur}
  >
    {this.renderNavbar()}
    {this.renderMonths()}
  </div>
</div>
```

This change will break the current CSS (and the css class names passed to`classNames`).

- [x] move the element handlers into a child div
- [x] update tests to use the child for handling events
- [x] add a new css class (e.g. `DayPicker-wrapper`) to the `classNames`
- [x] update style.css to replicate the current design

Pull request:
Fix interactive elements
Closes #392 

Patch:
diff --git a/docs/DayPickerAPI.md b/docs/DayPickerAPI.md
index bf23d226e..aae9f6b90 100644
--- a/docs/DayPickerAPI.md
+++ b/docs/DayPickerAPI.md
@@ -71,6 +71,7 @@ The object expects the following keys:
 
 ```js
   container,            // The container element
+  wrapper,              // The wrapper element, used for keyboard interaction
   interactionDisabled,  // Added to the container when there's no interaction with the calendar
 
   navBar,         // The navigation bar with the arrows to switch between months
diff --git a/src/DayPicker.js b/src/DayPicker.js
index 525359da3..4197dedf0 100644
--- a/src/DayPicker.js
+++ b/src/DayPicker.js
@@ -496,22 +496,23 @@ export default class DayPicker extends Component {
     }
 
     return (
-      // To make this eslint rule work again we need to cut a major release as it would
-      // result a breaking change: https://github.com/gpbl/react-day-picker/issues/392
-      // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
       <div
         {...this.props.containerProps}
+        className={className}
         ref={el => (this.dayPicker = el)}
         role="application"
         lang={this.props.locale}
-        className={className}
-        tabIndex={this.props.canChangeMonth && this.props.tabIndex}
-        onKeyDown={this.handleKeyDown}
-        onFocus={this.props.onFocus}
-        onBlur={this.props.onBlur}
       >
-        {this.renderNavbar()}
-        {this.renderMonths()}
+        <div
+          className={classNames.wrapper}
+          tabIndex={this.props.canChangeMonth && this.props.tabIndex}
+          onKeyDown={this.handleKeyDown}
+          onFocus={this.props.onFocus}
+          onBlur={this.props.onBlur}
+        >
+          {this.renderNavbar()}
+          {this.renderMonths()}
+        </div>
       </div>
     );
   }
diff --git a/src/classNames.js b/src/classNames.js
index b165705a9..2c34a08bf 100644
--- a/src/classNames.js
+++ b/src/classNames.js
@@ -2,6 +2,7 @@
 
 export default {
   container: 'DayPicker',
+  wrapper: 'DayPicker-wrapper',
   interactionDisabled: 'DayPicker--interactionDisabled',
   month: 'DayPicker-Month',
   navBar: 'DayPicker-NavBar',
diff --git a/src/style.css b/src/style.css
index 77f749f2a..04c154cb4 100644
--- a/src/style.css
+++ b/src/style.css
@@ -1,13 +1,16 @@
 /* DayPicker styles */
 
 .DayPicker {
+}
+
+.DayPicker-wrapper {
   display: flex;
   flex-wrap: wrap;
   justify-content: center;
   position: relative;
-  padding: 1rem 0;
   user-select: none;
   flex-direction: row;
+  padding: 1rem 0;
 }
 
 .DayPicker-Month {
diff --git a/types/index.d.ts b/types/index.d.ts
index 43410f021..998d1b386 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -80,6 +80,7 @@ declare namespace DayPicker {
 
   interface ClassNames {
     container: string;
+    wrapper: string;
     interactionDisabled: string;
     navBar: string;
     navButtonPrev: string;


Test patch:
diff --git a/test/daypicker/events.js b/test/daypicker/events.js
index b16263f55..44040f2de 100644
--- a/test/daypicker/events.js
+++ b/test/daypicker/events.js
@@ -199,7 +199,7 @@ describe('DayPicker’s events handlers', () => {
   it('should call `onKeyDown` event handler', () => {
     const handleKeyDown = jest.fn();
     const wrapper = mount(<DayPicker onKeyDown={handleKeyDown} />);
-    wrapper.simulate('keyDown');
+    wrapper.find('.DayPicker-wrapper').simulate('keyDown');
     expect(handleKeyDown.mock.calls[0][0]).toBeInstanceOf(SyntheticEvent);
   });
   it('should call `onKeyDown` also when changing month is disabled', () => {
@@ -207,7 +207,7 @@ describe('DayPicker’s events handlers', () => {
     const wrapper = mount(
       <DayPicker onKeyDown={handleKeyDown} canChangeMonth={false} />
     );
-    wrapper.simulate('keyDown');
+    wrapper.find('.DayPicker-wrapper').simulate('keyDown');
     expect(handleKeyDown.mock.calls[0][0]).toBeInstanceOf(SyntheticEvent);
   });
   it('should display the current month when clicking the today button', () => {
diff --git a/test/daypicker/navigation.js b/test/daypicker/navigation.js
index 2999d46c8..f6222062c 100644
--- a/test/daypicker/navigation.js
+++ b/test/daypicker/navigation.js
@@ -95,7 +95,9 @@ describe('DayPicker’s navigation', () => {
   it('should call `showNextMonth()` when the RIGHT key is pressed', () => {
     const wrapper = mount(<DayPicker />);
     const showNextMonth = jest.spyOn(wrapper.instance(), 'showNextMonth');
-    wrapper.simulate('keyDown', { keyCode: keys.RIGHT });
+    wrapper
+      .find('.DayPicker-wrapper')
+      .simulate('keyDown', { keyCode: keys.RIGHT });
     expect(showNextMonth).toHaveBeenCalledTimes(1);
     showNextMonth.mockReset();
   });
@@ -105,21 +107,27 @@ describe('DayPicker’s navigation', () => {
       wrapper.instance(),
       'showPreviousMonth'
     );
-    wrapper.simulate('keyDown', { keyCode: keys.LEFT });
+    wrapper
+      .find('.DayPicker-wrapper')
+      .simulate('keyDown', { keyCode: keys.LEFT });
     expect(showPreviousMonth).toHaveBeenCalledTimes(1);
     showPreviousMonth.mockReset();
   });
   it('should call `showPreviousYear()` when the UP key is pressed', () => {
     const wrapper = mount(<DayPicker />);
     const showPreviousYear = jest.spyOn(wrapper.instance(), 'showPreviousYear');
-    wrapper.simulate('keyDown', { keyCode: keys.UP });
+    wrapper
+      .find('.DayPicker-wrapper')
+      .simulate('keyDown', { keyCode: keys.UP });
     expect(showPreviousYear).toHaveBeenCalledTimes(1);
     showPreviousYear.mockReset();
   });
   it('should call `showNextYear()` when the DOWN key is pressed', () => {
     const wrapper = mount(<DayPicker />);
     const showNextYear = jest.spyOn(wrapper.instance(), 'showNextYear');
-    wrapper.simulate('keyDown', { keyCode: keys.DOWN });
+    wrapper
+      .find('.DayPicker-wrapper')
+      .simulate('keyDown', { keyCode: keys.DOWN });
     expect(showNextYear).toHaveBeenCalledTimes(1);
     showNextYear.mockReset();
   });
diff --git a/test/daypicker/rendering.js b/test/daypicker/rendering.js
index 6e7882c2f..e9a1ff9f7 100644
--- a/test/daypicker/rendering.js
+++ b/test/daypicker/rendering.js
@@ -105,7 +105,7 @@ describe('DayPicker’s rendering', () => {
   });
   it('should use the given tabIndex', () => {
     const wrapper = shallow(<DayPicker tabIndex={-1} />);
-    expect(wrapper).toHaveProp('tabIndex', -1);
+    expect(wrapper.find('.DayPicker-wrapper')).toHaveProp('tabIndex', -1);
   });
   it('should spread props to the container', () => {
     const wrapper = shallow(
@@ -119,8 +119,8 @@ describe('DayPicker’s rendering', () => {
     const wrapper = mount(
       <DayPicker onFocus={handleFocus} onBlur={handleBlur} />
     );
-    wrapper.simulate('focus');
-    wrapper.simulate('blur');
+    wrapper.find('.DayPicker-wrapper').simulate('focus');
+    wrapper.find('.DayPicker-wrapper').simulate('blur');
     expect(handleBlur).toHaveBeenCalledTimes(1);
     expect(handleFocus).toHaveBeenCalledTimes(1);
   });