@khanacademy/wonder-blocks-clickable 2.3.3 → 2.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @khanacademy/wonder-blocks-clickable
2
2
 
3
+ ## 2.4.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 4c682709: handleClick no longer redundantly triggers on mouseup
8
+
9
+ ## 2.4.0
10
+
11
+ ### Minor Changes
12
+
13
+ - ceb111df: ClickableBehavior no longer has tabIndex 0 by default. It must be passed in.
14
+
3
15
  ## 2.3.3
4
16
 
5
17
  ### Patch Changes
package/dist/es/index.js CHANGED
@@ -41,13 +41,11 @@ const disabledHandlers = {
41
41
  onMouseLeave: () => void 0,
42
42
  onMouseDown: () => void 0,
43
43
  onMouseUp: () => void 0,
44
- onDragStart: () => void 0,
45
44
  onTouchStart: () => void 0,
46
45
  onTouchEnd: () => void 0,
47
46
  onTouchCancel: () => void 0,
48
47
  onKeyDown: () => void 0,
49
- onKeyUp: () => void 0,
50
- tabIndex: 0
48
+ onKeyUp: () => void 0
51
49
  };
52
50
  const keyCodes = {
53
51
  enter: 13,
@@ -92,12 +90,7 @@ class ClickableBehavior extends React.Component {
92
90
  };
93
91
 
94
92
  this.handleMouseEnter = e => {
95
- if (e.buttons === 1) {
96
- this.dragging = true;
97
- this.setState({
98
- pressed: true
99
- });
100
- } else if (!this.waitingForClick) {
93
+ if (!this.waitingForClick) {
101
94
  this.setState({
102
95
  hovered: true
103
96
  });
@@ -106,7 +99,6 @@ class ClickableBehavior extends React.Component {
106
99
 
107
100
  this.handleMouseLeave = () => {
108
101
  if (!this.waitingForClick) {
109
- this.dragging = false;
110
102
  this.setState({
111
103
  hovered: false,
112
104
  pressed: false,
@@ -122,22 +114,12 @@ class ClickableBehavior extends React.Component {
122
114
  };
123
115
 
124
116
  this.handleMouseUp = e => {
125
- if (this.dragging) {
126
- this.dragging = false;
127
- this.handleClick(e);
128
- }
129
-
130
117
  this.setState({
131
118
  pressed: false,
132
119
  focused: false
133
120
  });
134
121
  };
135
122
 
136
- this.handleDragStart = e => {
137
- this.dragging = true;
138
- e.preventDefault();
139
- };
140
-
141
123
  this.handleTouchStart = () => {
142
124
  this.setState({
143
125
  pressed: true
@@ -227,7 +209,6 @@ class ClickableBehavior extends React.Component {
227
209
  this.state = startState;
228
210
  this.waitingForClick = false;
229
211
  this.enterClick = false;
230
- this.dragging = false;
231
212
  }
232
213
 
233
214
  navigateOrReset(shouldNavigate) {
@@ -351,14 +332,14 @@ class ClickableBehavior extends React.Component {
351
332
  render() {
352
333
  const childrenProps = this.props.disabled ? _extends({}, disabledHandlers, {
353
334
  onFocus: this.handleFocus,
354
- onBlur: this.handleBlur
335
+ onBlur: this.handleBlur,
336
+ tabIndex: this.props.tabIndex
355
337
  }) : {
356
338
  onClick: this.handleClick,
357
339
  onMouseEnter: this.handleMouseEnter,
358
340
  onMouseLeave: this.handleMouseLeave,
359
341
  onMouseDown: this.handleMouseDown,
360
342
  onMouseUp: this.handleMouseUp,
361
- onDragStart: this.handleDragStart,
362
343
  onTouchStart: this.handleTouchStart,
363
344
  onTouchEnd: this.handleTouchEnd,
364
345
  onTouchCancel: this.handleTouchCancel,
@@ -366,7 +347,7 @@ class ClickableBehavior extends React.Component {
366
347
  onKeyUp: this.handleKeyUp,
367
348
  onFocus: this.handleFocus,
368
349
  onBlur: this.handleBlur,
369
- tabIndex: 0
350
+ tabIndex: this.props.tabIndex
370
351
  };
371
352
  childrenProps.rel = this.props.rel || (this.props.target === "_blank" ? "noopener noreferrer" : undefined);
372
353
  const {
package/dist/index.js CHANGED
@@ -158,15 +158,11 @@ const disabledHandlers = {
158
158
  onMouseLeave: () => void 0,
159
159
  onMouseDown: () => void 0,
160
160
  onMouseUp: () => void 0,
161
- onDragStart: () => void 0,
162
161
  onTouchStart: () => void 0,
163
162
  onTouchEnd: () => void 0,
164
163
  onTouchCancel: () => void 0,
165
164
  onKeyDown: () => void 0,
166
- onKeyUp: () => void 0,
167
- // Clickable components should still be tabbable so they can
168
- // be used as anchors.
169
- tabIndex: 0
165
+ onKeyUp: () => void 0
170
166
  };
171
167
  const keyCodes = {
172
168
  enter: 13,
@@ -200,7 +196,7 @@ const startState = {
200
196
  * 3. Keyup (spacebar/enter) -> focus state
201
197
  *
202
198
  * Warning: The event handlers returned (onClick, onMouseEnter, onMouseLeave,
203
- * onMouseDown, onMouseUp, onDragStart, onTouchStart, onTouchEnd, onTouchCancel,
199
+ * onMouseDown, onMouseUp, onTouchStart, onTouchEnd, onTouchCancel,
204
200
  * onKeyDown, onKeyUp, onFocus, onBlur, tabIndex) should be passed on to the
205
201
  * component that has the ClickableBehavior. You cannot override these handlers
206
202
  * without potentially breaking the functionality of ClickableBehavior.
@@ -225,7 +221,11 @@ const startState = {
225
221
  * const ClickableBehavior = getClickableBehavior();
226
222
  *
227
223
  * return (
228
- * <ClickableBehavior disabled={props.disabled} onClick={props.onClick}>
224
+ * <ClickableBehavior
225
+ * disabled={props.disabled}
226
+ * onClick={props.onClick}
227
+ * tabIndex={0}
228
+ * >
229
229
  * {({hovered}, childrenProps) => (
230
230
  * <RoundRect
231
231
  * textcolor="white"
@@ -292,13 +292,7 @@ class ClickableBehavior extends react__WEBPACK_IMPORTED_MODULE_0__["Component"]
292
292
  };
293
293
 
294
294
  this.handleMouseEnter = e => {
295
- // When the left button is pressed already, we want it to be pressed
296
- if (e.buttons === 1) {
297
- this.dragging = true;
298
- this.setState({
299
- pressed: true
300
- });
301
- } else if (!this.waitingForClick) {
295
+ if (!this.waitingForClick) {
302
296
  this.setState({
303
297
  hovered: true
304
298
  });
@@ -307,7 +301,6 @@ class ClickableBehavior extends react__WEBPACK_IMPORTED_MODULE_0__["Component"]
307
301
 
308
302
  this.handleMouseLeave = () => {
309
303
  if (!this.waitingForClick) {
310
- this.dragging = false;
311
304
  this.setState({
312
305
  hovered: false,
313
306
  pressed: false,
@@ -323,22 +316,12 @@ class ClickableBehavior extends react__WEBPACK_IMPORTED_MODULE_0__["Component"]
323
316
  };
324
317
 
325
318
  this.handleMouseUp = e => {
326
- if (this.dragging) {
327
- this.dragging = false;
328
- this.handleClick(e);
329
- }
330
-
331
319
  this.setState({
332
320
  pressed: false,
333
321
  focused: false
334
322
  });
335
323
  };
336
324
 
337
- this.handleDragStart = e => {
338
- this.dragging = true;
339
- e.preventDefault();
340
- };
341
-
342
325
  this.handleTouchStart = () => {
343
326
  this.setState({
344
327
  pressed: true
@@ -434,7 +417,6 @@ class ClickableBehavior extends react__WEBPACK_IMPORTED_MODULE_0__["Component"]
434
417
  this.state = startState;
435
418
  this.waitingForClick = false;
436
419
  this.enterClick = false;
437
- this.dragging = false;
438
420
  }
439
421
 
440
422
  navigateOrReset(shouldNavigate) {
@@ -580,14 +562,14 @@ class ClickableBehavior extends react__WEBPACK_IMPORTED_MODULE_0__["Component"]
580
562
  const childrenProps = this.props.disabled ? { ...disabledHandlers,
581
563
  // Keep these handlers for keyboard accessibility.
582
564
  onFocus: this.handleFocus,
583
- onBlur: this.handleBlur
565
+ onBlur: this.handleBlur,
566
+ tabIndex: this.props.tabIndex
584
567
  } : {
585
568
  onClick: this.handleClick,
586
569
  onMouseEnter: this.handleMouseEnter,
587
570
  onMouseLeave: this.handleMouseLeave,
588
571
  onMouseDown: this.handleMouseDown,
589
572
  onMouseUp: this.handleMouseUp,
590
- onDragStart: this.handleDragStart,
591
573
  onTouchStart: this.handleTouchStart,
592
574
  onTouchEnd: this.handleTouchEnd,
593
575
  onTouchCancel: this.handleTouchCancel,
@@ -595,9 +577,7 @@ class ClickableBehavior extends react__WEBPACK_IMPORTED_MODULE_0__["Component"]
595
577
  onKeyUp: this.handleKeyUp,
596
578
  onFocus: this.handleFocus,
597
579
  onBlur: this.handleBlur,
598
- // We set tabIndex to 0 so that users can tab to clickable
599
- // things that aren't buttons or anchors.
600
- tabIndex: 0
580
+ tabIndex: this.props.tabIndex
601
581
  }; // When the link is set to open in a new window, we want to set some
602
582
  // `rel` attributes. This is to ensure that the links we're sending folks
603
583
  // to can't hijack the existing page. These defaults can be overriden
@@ -1136,19 +1116,16 @@ function _extends() {
1136
1116
  module.exports = _extends = Object.assign ? Object.assign.bind() : function (target) {
1137
1117
  for (var i = 1; i < arguments.length; i++) {
1138
1118
  var source = arguments[i];
1139
-
1140
1119
  for (var key in source) {
1141
1120
  if (Object.prototype.hasOwnProperty.call(source, key)) {
1142
1121
  target[key] = source[key];
1143
1122
  }
1144
1123
  }
1145
1124
  }
1146
-
1147
1125
  return target;
1148
1126
  }, module.exports.__esModule = true, module.exports["default"] = module.exports;
1149
1127
  return _extends.apply(this, arguments);
1150
1128
  }
1151
-
1152
1129
  module.exports = _extends, module.exports.__esModule = true, module.exports["default"] = module.exports;
1153
1130
 
1154
1131
  /***/ }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-clickable",
3
- "version": "2.3.3",
3
+ "version": "2.4.1",
4
4
  "design": "v1",
5
5
  "description": "Clickable component for Wonder-Blocks.",
6
6
  "main": "dist/index.js",
@@ -14,6 +14,15 @@ export default {
14
14
  },
15
15
  },
16
16
  },
17
+ tabIndex: {
18
+ control: {type: "number"},
19
+ description: `Used to indicate the tab order of an element.
20
+ Use 0 to make an element focusable, and use -1 to make an
21
+ element non-focusable via keyboard navigation.`,
22
+ table: {
23
+ type: {summary: "number"},
24
+ },
25
+ },
17
26
  /**
18
27
  * States
19
28
  */
@@ -3,7 +3,7 @@ import * as React from "react";
3
3
  import {StyleSheet} from "aphrodite";
4
4
 
5
5
  import {getClickableBehavior} from "@khanacademy/wonder-blocks-clickable";
6
- import {View} from "@khanacademy/wonder-blocks-core";
6
+ import {View, addStyle} from "@khanacademy/wonder-blocks-core";
7
7
  import Color from "@khanacademy/wonder-blocks-color";
8
8
  import Spacing from "@khanacademy/wonder-blocks-spacing";
9
9
 
@@ -72,12 +72,98 @@ Default.parameters = {
72
72
  },
73
73
  };
74
74
 
75
+ export const WrappingButton: StoryComponentType = (args) => {
76
+ const ClickableBehavior = getClickableBehavior();
77
+ const StyledButton = addStyle("button");
78
+
79
+ return (
80
+ <ClickableBehavior {...args}>
81
+ {(state, childrenProps) => {
82
+ const {pressed, hovered, focused} = state;
83
+ return (
84
+ <StyledButton
85
+ style={[
86
+ styles.clickable,
87
+ styles.newButton,
88
+ hovered && styles.hovered,
89
+ focused && styles.focused,
90
+ pressed && styles.pressed,
91
+ ]}
92
+ {...childrenProps}
93
+ >
94
+ This is an element wrapped with ClickableBehavior
95
+ </StyledButton>
96
+ );
97
+ }}
98
+ </ClickableBehavior>
99
+ );
100
+ };
101
+
102
+ WrappingButton.parameters = {
103
+ chromatic: {
104
+ // we don't need screenshots because this story only displays the
105
+ // resting/default state.
106
+ disableSnapshot: true,
107
+ },
108
+ docs: {
109
+ storyDescription: `This is an example of a \`<ClickableBehavior>\`
110
+ wrapping a button. Since buttons have a built in tabIndex,
111
+ a tabIndex does not need to be added to \`<ClickableBehavior>\`
112
+ here.`,
113
+ },
114
+ };
115
+
116
+ export const WithTabIndex: StoryComponentType = () => {
117
+ const ClickableBehavior = getClickableBehavior();
118
+
119
+ return (
120
+ <ClickableBehavior role="button" tabIndex={0}>
121
+ {(state, childrenProps) => {
122
+ const {pressed, hovered, focused} = state;
123
+ return (
124
+ <View
125
+ style={[
126
+ styles.clickable,
127
+ hovered && styles.hovered,
128
+ focused && styles.focused,
129
+ pressed && styles.pressed,
130
+ ]}
131
+ {...childrenProps}
132
+ >
133
+ This is an element wrapped with ClickableBehavior
134
+ </View>
135
+ );
136
+ }}
137
+ </ClickableBehavior>
138
+ );
139
+ };
140
+
141
+ WithTabIndex.parameters = {
142
+ chromatic: {
143
+ // we don't need screenshots because this story only displays the
144
+ // resting/default state.
145
+ disableSnapshot: true,
146
+ },
147
+ docs: {
148
+ storyDescription: `A \`<ClickableBehavior>\` element does not have
149
+ a tabIndex by default, as many elements it could wrap may have
150
+ their own built in tabIndex attribute, such as buttons. If this
151
+ is not the case, a tabIndex should be passed in using the
152
+ \`tabIndex\` prop.`,
153
+ },
154
+ };
155
+
75
156
  const styles = StyleSheet.create({
76
157
  clickable: {
77
158
  cursor: "pointer",
78
159
  padding: Spacing.medium_16,
79
160
  textAlign: "center",
80
161
  },
162
+ newButton: {
163
+ border: "none",
164
+ backgroundColor: Color.white,
165
+ width: "100%",
166
+ },
81
167
  hovered: {
82
168
  textDecoration: "underline",
83
169
  backgroundColor: Color.blue,
@@ -66,6 +66,13 @@ export const Default: StoryComponentType = (args) => (
66
66
  </Clickable>
67
67
  );
68
68
 
69
+ Default.args = {
70
+ onClick: () => {
71
+ // eslint-disable-next-line no-alert
72
+ alert("Click!");
73
+ },
74
+ };
75
+
69
76
  export const Basic: StoryComponentType = () => (
70
77
  <View style={styles.centerText}>
71
78
  <Clickable
@@ -64,7 +64,7 @@ describe("ClickableBehavior", () => {
64
64
  expect(onClick).toHaveBeenCalled();
65
65
  });
66
66
 
67
- it("changes only hovered state on mouse enter/leave", () => {
67
+ it("changes hovered state on mouse enter/leave", () => {
68
68
  const onClick = jest.fn();
69
69
  render(
70
70
  <ClickableBehavior disabled={false} onClick={(e) => onClick(e)}>
@@ -82,7 +82,7 @@ describe("ClickableBehavior", () => {
82
82
  expect(button).not.toHaveTextContent("hovered");
83
83
  });
84
84
 
85
- it("changes only pressed state on mouse enter/leave while dragging", () => {
85
+ it("changes hovered state on mouse enter while dragging", () => {
86
86
  const onClick = jest.fn();
87
87
  render(
88
88
  <ClickableBehavior disabled={false} onClick={(e) => onClick(e)}>
@@ -93,18 +93,34 @@ describe("ClickableBehavior", () => {
93
93
  </ClickableBehavior>,
94
94
  );
95
95
  const button = screen.getByRole("button");
96
+ expect(button).not.toHaveTextContent("hovered");
97
+ expect(button).not.toHaveTextContent("pressed");
98
+
99
+ fireEvent.mouseEnter(button, {buttons: 1});
100
+ expect(button).not.toHaveTextContent("pressed");
101
+ expect(button).toHaveTextContent("hovered");
102
+ });
103
+
104
+ it("changes pressed and hover states on mouse leave while dragging", () => {
105
+ const onClick = jest.fn();
106
+ render(
107
+ <ClickableBehavior disabled={false} onClick={(e) => onClick(e)}>
108
+ {(state, childrenProps) => {
109
+ const label = labelForState(state);
110
+ return <button {...childrenProps}>{label}</button>;
111
+ }}
112
+ </ClickableBehavior>,
113
+ );
114
+ const button = screen.getByRole("button");
115
+ expect(button).not.toHaveTextContent("hovered");
96
116
  expect(button).not.toHaveTextContent("pressed");
97
117
 
98
118
  fireEvent.mouseDown(button);
99
- fireEvent.dragStart(button);
100
- fireEvent.mouseMove(button);
101
119
  expect(button).toHaveTextContent("pressed");
102
120
 
103
121
  fireEvent.mouseLeave(button);
122
+ expect(button).not.toHaveTextContent("hovered");
104
123
  expect(button).not.toHaveTextContent("pressed");
105
-
106
- fireEvent.mouseEnter(button, {buttons: 1});
107
- expect(button).toHaveTextContent("pressed");
108
124
  });
109
125
 
110
126
  it("changes pressed state on mouse down/up", () => {
@@ -282,7 +298,7 @@ describe("ClickableBehavior", () => {
282
298
  expect(button).not.toHaveTextContent("focused");
283
299
  });
284
300
 
285
- test("tabIndex should be 0", () => {
301
+ test("should not have a tabIndex if one is not passed in", () => {
286
302
  // Arrange
287
303
  // Act
288
304
  render(
@@ -299,14 +315,18 @@ describe("ClickableBehavior", () => {
299
315
 
300
316
  // Assert
301
317
  const button = screen.getByTestId("test-button-1");
302
- expect(button).toHaveAttribute("tabIndex", "0");
318
+ expect(button).not.toHaveAttribute("tabIndex");
303
319
  });
304
320
 
305
- test("tabIndex should be 0 even for disabled components", () => {
321
+ test("should have the tabIndex that is passed in", () => {
306
322
  // Arrange
307
323
  // Act
308
324
  render(
309
- <ClickableBehavior disabled={true} onClick={(e) => {}}>
325
+ <ClickableBehavior
326
+ disabled={false}
327
+ onClick={(e) => {}}
328
+ tabIndex={1}
329
+ >
310
330
  {(state, childrenProps) => {
311
331
  return (
312
332
  <button data-test-id="test-button-2" {...childrenProps}>
@@ -319,7 +339,53 @@ describe("ClickableBehavior", () => {
319
339
 
320
340
  // Assert
321
341
  const button = screen.getByTestId("test-button-2");
322
- expect(button).toHaveAttribute("tabIndex", "0");
342
+ expect(button).toHaveAttribute("tabIndex", "1");
343
+ });
344
+
345
+ test("should have the tabIndex that is passed in even if disabled", () => {
346
+ // Arrange
347
+ // Act
348
+ render(
349
+ <ClickableBehavior disabled={true} onClick={(e) => {}} tabIndex={1}>
350
+ {(state, childrenProps) => {
351
+ return (
352
+ <button data-test-id="test-button-3" {...childrenProps}>
353
+ Label
354
+ </button>
355
+ );
356
+ }}
357
+ </ClickableBehavior>,
358
+ );
359
+
360
+ // Assert
361
+ const button = screen.getByTestId("test-button-3");
362
+ expect(button).toHaveAttribute("tabIndex", "1");
363
+ });
364
+
365
+ test("should make non-interactive children keyboard focusable if tabIndex 0 is passed", () => {
366
+ // Arrange
367
+ render(
368
+ <ClickableBehavior
369
+ disabled={false}
370
+ onClick={(e) => {}}
371
+ tabIndex={1}
372
+ >
373
+ {(state, childrenProps) => {
374
+ return (
375
+ <div data-test-id="test-div-1" {...childrenProps}>
376
+ Label
377
+ </div>
378
+ );
379
+ }}
380
+ </ClickableBehavior>,
381
+ );
382
+
383
+ // Act
384
+ const button = screen.getByTestId("test-div-1");
385
+ userEvent.tab();
386
+
387
+ // Assert
388
+ expect(button).toHaveFocus();
323
389
  });
324
390
 
325
391
  it("does not change state if disabled", () => {
@@ -713,7 +779,17 @@ describe("ClickableBehavior", () => {
713
779
  expect(onClick).toHaveBeenCalledTimes(expectedNumberTimesCalled);
714
780
  });
715
781
 
716
- it("calls onClick on mouseup when the mouse was dragging", () => {
782
+ // The following two tests involve click behavior when dragging.
783
+ // Here are some notable related actions that cannot be tested using
784
+ // existing jest/RTL events since these click types are handled
785
+ // by browsers but aren't registered as clicks by RTL/jest:
786
+ // 1. Mousedown in the button, drag within the button, and mouseup
787
+ // in the button (mouse doesn't leave the button at any point).
788
+ // This should result in a successful click.
789
+ // 2. Mouse down in the button, drag out of the button (don't let go),
790
+ // drag back into the button, and mouseup inside the button.
791
+ // This should result in a successful click.
792
+ it("does not call onClick on mouseup when the mouse presses inside and drags away", () => {
717
793
  const onClick = jest.fn();
718
794
  render(
719
795
  <ClickableBehavior disabled={false} onClick={(e) => onClick(e)}>
@@ -725,19 +801,25 @@ describe("ClickableBehavior", () => {
725
801
 
726
802
  const button = screen.getByRole("button");
727
803
  fireEvent.mouseDown(button);
728
- fireEvent.dragStart(button);
729
804
  fireEvent.mouseLeave(button);
730
805
  fireEvent.mouseUp(button);
731
806
  expect(onClick).toHaveBeenCalledTimes(0);
807
+ });
732
808
 
733
- fireEvent.mouseDown(button);
734
- fireEvent.dragStart(button);
735
- fireEvent.mouseUp(button);
736
- expect(onClick).toHaveBeenCalledTimes(1);
809
+ it("does not call onClick on mouseup when the mouse presses outside and drags in", () => {
810
+ const onClick = jest.fn();
811
+ render(
812
+ <ClickableBehavior disabled={false} onClick={(e) => onClick(e)}>
813
+ {(state, childrenProps) => {
814
+ return <button {...childrenProps}>Label</button>;
815
+ }}
816
+ </ClickableBehavior>,
817
+ );
737
818
 
819
+ const button = screen.getByRole("button");
738
820
  fireEvent.mouseEnter(button, {buttons: 1});
739
821
  fireEvent.mouseUp(button);
740
- expect(onClick).toHaveBeenCalledTimes(2);
822
+ expect(onClick).toHaveBeenCalledTimes(0);
741
823
  });
742
824
 
743
825
  it("doesn't trigger enter key when browser doesn't stop the click", () => {
@@ -88,6 +88,13 @@ type CommonProps = {|
88
88
 
89
89
  skipClientNav?: boolean,
90
90
 
91
+ /**
92
+ * Used to indicate the tab order of an element.
93
+ * Use 0 to make an element focusable, and use -1 to make an
94
+ * element non-focusable via keyboard navigation.
95
+ */
96
+ tabIndex?: number,
97
+
91
98
  /**
92
99
  * A function to be executed `onclick`.
93
100
  */
@@ -198,7 +205,6 @@ export type ChildrenProps = {|
198
205
  onMouseLeave: () => mixed,
199
206
  onMouseDown: () => mixed,
200
207
  onMouseUp: (e: SyntheticMouseEvent<>) => mixed,
201
- onDragStart: (e: SyntheticMouseEvent<>) => mixed,
202
208
  onTouchStart: () => mixed,
203
209
  onTouchEnd: () => mixed,
204
210
  onTouchCancel: () => mixed,
@@ -206,7 +212,7 @@ export type ChildrenProps = {|
206
212
  onKeyUp: (e: SyntheticKeyboardEvent<>) => mixed,
207
213
  onFocus: (e: SyntheticFocusEvent<>) => mixed,
208
214
  onBlur: (e: SyntheticFocusEvent<>) => mixed,
209
- tabIndex: number,
215
+ tabIndex?: number,
210
216
  rel?: string,
211
217
  |};
212
218
 
@@ -216,15 +222,11 @@ const disabledHandlers = {
216
222
  onMouseLeave: () => void 0,
217
223
  onMouseDown: () => void 0,
218
224
  onMouseUp: () => void 0,
219
- onDragStart: () => void 0,
220
225
  onTouchStart: () => void 0,
221
226
  onTouchEnd: () => void 0,
222
227
  onTouchCancel: () => void 0,
223
228
  onKeyDown: () => void 0,
224
229
  onKeyUp: () => void 0,
225
- // Clickable components should still be tabbable so they can
226
- // be used as anchors.
227
- tabIndex: 0,
228
230
  };
229
231
 
230
232
  const keyCodes = {
@@ -261,7 +263,7 @@ const startState: ClickableState = {
261
263
  * 3. Keyup (spacebar/enter) -> focus state
262
264
  *
263
265
  * Warning: The event handlers returned (onClick, onMouseEnter, onMouseLeave,
264
- * onMouseDown, onMouseUp, onDragStart, onTouchStart, onTouchEnd, onTouchCancel,
266
+ * onMouseDown, onMouseUp, onTouchStart, onTouchEnd, onTouchCancel,
265
267
  * onKeyDown, onKeyUp, onFocus, onBlur, tabIndex) should be passed on to the
266
268
  * component that has the ClickableBehavior. You cannot override these handlers
267
269
  * without potentially breaking the functionality of ClickableBehavior.
@@ -286,7 +288,11 @@ const startState: ClickableState = {
286
288
  * const ClickableBehavior = getClickableBehavior();
287
289
  *
288
290
  * return (
289
- * <ClickableBehavior disabled={props.disabled} onClick={props.onClick}>
291
+ * <ClickableBehavior
292
+ * disabled={props.disabled}
293
+ * onClick={props.onClick}
294
+ * tabIndex={0}
295
+ * >
290
296
  * {({hovered}, childrenProps) => (
291
297
  * <RoundRect
292
298
  * textcolor="white"
@@ -322,7 +328,6 @@ export default class ClickableBehavior extends React.Component<
322
328
  > {
323
329
  waitingForClick: boolean;
324
330
  enterClick: boolean;
325
- dragging: boolean;
326
331
 
327
332
  static defaultProps: DefaultProps = {
328
333
  disabled: false,
@@ -348,7 +353,6 @@ export default class ClickableBehavior extends React.Component<
348
353
  this.state = startState;
349
354
  this.waitingForClick = false;
350
355
  this.enterClick = false;
351
- this.dragging = false;
352
356
  }
353
357
 
354
358
  navigateOrReset(shouldNavigate: boolean) {
@@ -505,18 +509,13 @@ export default class ClickableBehavior extends React.Component<
505
509
  };
506
510
 
507
511
  handleMouseEnter: (e: SyntheticMouseEvent<>) => void = (e) => {
508
- // When the left button is pressed already, we want it to be pressed
509
- if (e.buttons === 1) {
510
- this.dragging = true;
511
- this.setState({pressed: true});
512
- } else if (!this.waitingForClick) {
512
+ if (!this.waitingForClick) {
513
513
  this.setState({hovered: true});
514
514
  }
515
515
  };
516
516
 
517
517
  handleMouseLeave: () => void = () => {
518
518
  if (!this.waitingForClick) {
519
- this.dragging = false;
520
519
  this.setState({hovered: false, pressed: false, focused: false});
521
520
  }
522
521
  };
@@ -526,18 +525,9 @@ export default class ClickableBehavior extends React.Component<
526
525
  };
527
526
 
528
527
  handleMouseUp: (e: SyntheticMouseEvent<>) => void = (e) => {
529
- if (this.dragging) {
530
- this.dragging = false;
531
- this.handleClick(e);
532
- }
533
528
  this.setState({pressed: false, focused: false});
534
529
  };
535
530
 
536
- handleDragStart: (e: SyntheticMouseEvent<>) => void = (e) => {
537
- this.dragging = true;
538
- e.preventDefault();
539
- };
540
-
541
531
  handleTouchStart: () => void = () => {
542
532
  this.setState({pressed: true});
543
533
  };
@@ -614,6 +604,7 @@ export default class ClickableBehavior extends React.Component<
614
604
  // Keep these handlers for keyboard accessibility.
615
605
  onFocus: this.handleFocus,
616
606
  onBlur: this.handleBlur,
607
+ tabIndex: this.props.tabIndex,
617
608
  }
618
609
  : {
619
610
  onClick: this.handleClick,
@@ -621,7 +612,6 @@ export default class ClickableBehavior extends React.Component<
621
612
  onMouseLeave: this.handleMouseLeave,
622
613
  onMouseDown: this.handleMouseDown,
623
614
  onMouseUp: this.handleMouseUp,
624
- onDragStart: this.handleDragStart,
625
615
  onTouchStart: this.handleTouchStart,
626
616
  onTouchEnd: this.handleTouchEnd,
627
617
  onTouchCancel: this.handleTouchCancel,
@@ -629,9 +619,7 @@ export default class ClickableBehavior extends React.Component<
629
619
  onKeyUp: this.handleKeyUp,
630
620
  onFocus: this.handleFocus,
631
621
  onBlur: this.handleBlur,
632
- // We set tabIndex to 0 so that users can tab to clickable
633
- // things that aren't buttons or anchors.
634
- tabIndex: 0,
622
+ tabIndex: this.props.tabIndex,
635
623
  };
636
624
 
637
625
  // When the link is set to open in a new window, we want to set some