@khanacademy/wonder-blocks-clickable 2.2.6 → 2.2.7

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,13 @@
1
1
  # @khanacademy/wonder-blocks-clickable
2
2
 
3
+ ## 2.2.7
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [5f4a4297]
8
+ - Updated dependencies [2b96fd59]
9
+ - @khanacademy/wonder-blocks-core@4.3.2
10
+
3
11
  ## 2.2.6
4
12
 
5
13
  ### Patch Changes
package/dist/es/index.js CHANGED
@@ -453,7 +453,7 @@ class Clickable extends React.Component {
453
453
 
454
454
  const ClickableBehavior = getClickableBehavior(href, skipClientNav, router);
455
455
 
456
- const getStyle = state => [styles.reset, styles.link, !hideDefaultFocusRing && state.focused && (light ? styles.focusedLight : styles.focused), style];
456
+ const getStyle = state => [styles.reset, styles.link, !hideDefaultFocusRing && state.focused && (light ? styles.focusedLight : styles.focused), disabled && styles.disabled, style];
457
457
 
458
458
  if (beforeNav) {
459
459
  return React.createElement(ClickableBehavior, {
@@ -517,10 +517,22 @@ const styles = StyleSheet.create({
517
517
  cursor: "pointer"
518
518
  },
519
519
  focused: {
520
- outline: `solid 2px ${Color.blue}`
520
+ ":focus": {
521
+ outline: `solid 2px ${Color.blue}`
522
+ }
521
523
  },
522
524
  focusedLight: {
523
525
  outline: `solid 2px ${Color.white}`
526
+ },
527
+ disabled: {
528
+ color: Color.offBlack32,
529
+ cursor: "not-allowed",
530
+ ":focus": {
531
+ outline: "none"
532
+ },
533
+ ":focus-visible": {
534
+ outline: `solid 2px ${Color.blue}`
535
+ }
524
536
  }
525
537
  });
526
538
 
package/dist/index.js CHANGED
@@ -200,18 +200,19 @@ const startState = {
200
200
  * 3. Keyup (spacebar/enter) -> focus state
201
201
  *
202
202
  * Warning: The event handlers returned (onClick, onMouseEnter, onMouseLeave,
203
- * onMouseDown, onMouseUp, onDragStart, onTouchStart, onTouchEnd, onTouchCancel, onKeyDown,
204
- * onKeyUp, onFocus, onBlur, tabIndex) should be passed on to the component
205
- * that has the ClickableBehavior. You cannot override these handlers without
206
- * potentially breaking the functionality of ClickableBehavior.
203
+ * onMouseDown, onMouseUp, onDragStart, onTouchStart, onTouchEnd, onTouchCancel,
204
+ * onKeyDown, onKeyUp, onFocus, onBlur, tabIndex) should be passed on to the
205
+ * component that has the ClickableBehavior. You cannot override these handlers
206
+ * without potentially breaking the functionality of ClickableBehavior.
207
207
  *
208
- * There are internal props triggerOnEnter and triggerOnSpace that can be set
209
- * to false if one of those keys shouldn't count as a click on this component.
210
- * Be careful about setting those to false -- make certain that the component
208
+ * There are internal props triggerOnEnter and triggerOnSpace that can be set to
209
+ * false if one of those keys shouldn't count as a click on this component. Be
210
+ * careful about setting those to false -- make certain that the component
211
211
  * shouldn't process that key.
212
212
  *
213
- * See [this document](https://docs.google.com/document/d/1DG5Rg2f0cawIL5R8UqnPQpd7pbdObk8OyjO5ryYQmBM/edit#)
214
- * for a more thorough explanation of expected behaviors and potential cavaets.
213
+ * See [this
214
+ document](https://docs.google.com/document/d/1DG5Rg2f0cawIL5R8UqnPQpd7pbdObk8OyjO5ryYQmBM/edit#)
215
+ for a more thorough explanation of expected behaviors and potential cavaets.
215
216
  *
216
217
  * `ClickableBehavior` accepts a function as `children` which is passed state
217
218
  * and an object containing event handlers and some other props. The `children`
@@ -219,32 +220,30 @@ const startState = {
219
220
  *
220
221
  * Example:
221
222
  *
222
- * ```js
223
- * class MyClickableComponent extends React.Component<Props> {
224
- * render(): React.Node {
225
- * const ClickableBehavior = getClickableBehavior();
226
- * return <ClickableBehavior
227
- * disabled={this.props.disabled}
228
- * onClick={this.props.onClick}
229
- * >
230
- * {({hovered}, childrenProps) =>
231
- * <RoundRect
232
- * textcolor='white'
233
- * backgroundColor={hovered ? 'red' : 'blue'}}
234
- * {...childrenProps}
235
- * >
236
- * {this.props.children}
237
- * </RoundRect>
238
- * }
239
- * </ClickableBehavior>
240
- * }
223
+ * ```jsx
224
+ * function MyClickableComponent(props: Props) {
225
+ * const ClickableBehavior = getClickableBehavior();
226
+ *
227
+ * return (
228
+ * <ClickableBehavior disabled={props.disabled} onClick={props.onClick}>
229
+ * {({hovered}, childrenProps) => (
230
+ * <RoundRect
231
+ * textcolor="white"
232
+ * backgroundColor={hovered ? "red" : "blue"}
233
+ * {...childrenProps}
234
+ * >
235
+ * {props.children}
236
+ * </RoundRect>
237
+ * )}
238
+ * </ClickableBehavior>
239
+ * );
241
240
  * }
242
241
  * ```
243
242
  *
244
- * This follows a pattern called [Function as Child Components]
245
- * (https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9).
243
+ * This follows a pattern called [Function as Child
244
+ * Components](https://medium.com/merrickchristensen/function-as-child-components-5f3920a9ace9).
246
245
  *
247
- * WARNING: Do not use this component directly, use getClickableBehavior
246
+ * **WARNING:** Do not use this component directly, use getClickableBehavior
248
247
  * instead. getClickableBehavior takes three arguments (href, directtNav, and
249
248
  * router) and returns either the default ClickableBehavior or a react-router
250
249
  * aware version.
@@ -252,9 +251,9 @@ const startState = {
252
251
  * The react-router aware version is returned if `router` is a react-router-dom
253
252
  * router, `skipClientNav` is not `true`, and `href` is an internal URL.
254
253
  *
255
- * The `router` can be accessed via __RouterContext (imported from 'react-router')
256
- * from a component rendered as a descendant of a BrowserRouter.
257
- * See https://reacttraining.com/react-router/web/guides/basic-components.
254
+ * The `router` can be accessed via __RouterContext (imported from
255
+ 'react-router') from a component rendered as a descendant of a BrowserRouter.
256
+ See https://reacttraining.com/react-router/web/guides/basic-components.
258
257
  */
259
258
 
260
259
  class ClickableBehavior extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
@@ -619,69 +618,6 @@ ClickableBehavior.defaultProps = {
619
618
 
620
619
  /***/ }),
621
620
  /* 3 */
622
- /***/ (function(module, __webpack_exports__, __webpack_require__) {
623
-
624
- "use strict";
625
- /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getClickableBehavior; });
626
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
627
- /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
628
- /* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
629
- /* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_router_dom__WEBPACK_IMPORTED_MODULE_1__);
630
- /* harmony import */ var _components_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2);
631
- /* harmony import */ var _is_client_side_url_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1);
632
- /**
633
- * Returns either the default ClickableBehavior or a react-router aware version.
634
- *
635
- * The react-router aware version is returned if `router` is a react-router-dom
636
- * router, `skipClientNav` is not `true`, and `href` is an internal URL.
637
- *
638
- * The `router` can be accessed via __RouterContext (imported from 'react-router')
639
- * from a component rendered as a descendant of a BrowserRouter.
640
- * See https://reacttraining.com/react-router/web/guides/basic-components.
641
- */
642
-
643
-
644
-
645
-
646
- const ClickableBehaviorWithRouter = Object(react_router_dom__WEBPACK_IMPORTED_MODULE_1__["withRouter"])(_components_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"]);
647
- function getClickableBehavior(
648
- /**
649
- * The URL to navigate to.
650
- */
651
- href,
652
- /**
653
- * Should we skip using the react router and go to the page directly.
654
- */
655
- skipClientNav,
656
- /**
657
- * router object added to the React context object by react-router-dom.
658
- */
659
- router) {
660
- if (router && skipClientNav !== true && href && Object(_is_client_side_url_js__WEBPACK_IMPORTED_MODULE_3__[/* isClientSideUrl */ "a"])(href)) {
661
- // We cast to `any` here since the type of ClickableBehaviorWithRouter
662
- // is slightly different from the return type of this function.
663
- // TODO(WB-1037): Always return the wrapped version once all routes have
664
- // been ported to the app-shell in webapp.
665
- return ClickableBehaviorWithRouter;
666
- }
667
-
668
- return _components_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"];
669
- }
670
-
671
- /***/ }),
672
- /* 4 */
673
- /***/ (function(module, exports) {
674
-
675
- module.exports = require("react-router-dom");
676
-
677
- /***/ }),
678
- /* 5 */
679
- /***/ (function(module, exports) {
680
-
681
- module.exports = require("@khanacademy/wonder-blocks-core");
682
-
683
- /***/ }),
684
- /* 6 */
685
621
  /***/ (function(module, exports) {
686
622
 
687
623
  module.exports =
@@ -1135,6 +1071,69 @@ function (module, __webpack_exports__, __webpack_require__) {
1135
1071
  /******/
1136
1072
  ]);
1137
1073
 
1074
+ /***/ }),
1075
+ /* 4 */
1076
+ /***/ (function(module, __webpack_exports__, __webpack_require__) {
1077
+
1078
+ "use strict";
1079
+ /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "a", function() { return getClickableBehavior; });
1080
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(0);
1081
+ /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1082
+ /* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
1083
+ /* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_router_dom__WEBPACK_IMPORTED_MODULE_1__);
1084
+ /* harmony import */ var _components_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(2);
1085
+ /* harmony import */ var _is_client_side_url_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1);
1086
+ /**
1087
+ * Returns either the default ClickableBehavior or a react-router aware version.
1088
+ *
1089
+ * The react-router aware version is returned if `router` is a react-router-dom
1090
+ * router, `skipClientNav` is not `true`, and `href` is an internal URL.
1091
+ *
1092
+ * The `router` can be accessed via __RouterContext (imported from 'react-router')
1093
+ * from a component rendered as a descendant of a BrowserRouter.
1094
+ * See https://reacttraining.com/react-router/web/guides/basic-components.
1095
+ */
1096
+
1097
+
1098
+
1099
+
1100
+ const ClickableBehaviorWithRouter = Object(react_router_dom__WEBPACK_IMPORTED_MODULE_1__["withRouter"])(_components_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"]);
1101
+ function getClickableBehavior(
1102
+ /**
1103
+ * The URL to navigate to.
1104
+ */
1105
+ href,
1106
+ /**
1107
+ * Should we skip using the react router and go to the page directly.
1108
+ */
1109
+ skipClientNav,
1110
+ /**
1111
+ * router object added to the React context object by react-router-dom.
1112
+ */
1113
+ router) {
1114
+ if (router && skipClientNav !== true && href && Object(_is_client_side_url_js__WEBPACK_IMPORTED_MODULE_3__[/* isClientSideUrl */ "a"])(href)) {
1115
+ // We cast to `any` here since the type of ClickableBehaviorWithRouter
1116
+ // is slightly different from the return type of this function.
1117
+ // TODO(WB-1037): Always return the wrapped version once all routes have
1118
+ // been ported to the app-shell in webapp.
1119
+ return ClickableBehaviorWithRouter;
1120
+ }
1121
+
1122
+ return _components_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_2__[/* default */ "a"];
1123
+ }
1124
+
1125
+ /***/ }),
1126
+ /* 5 */
1127
+ /***/ (function(module, exports) {
1128
+
1129
+ module.exports = require("react-router-dom");
1130
+
1131
+ /***/ }),
1132
+ /* 6 */
1133
+ /***/ (function(module, exports) {
1134
+
1135
+ module.exports = require("@khanacademy/wonder-blocks-core");
1136
+
1138
1137
  /***/ }),
1139
1138
  /* 7 */
1140
1139
  /***/ (function(module, __webpack_exports__, __webpack_require__) {
@@ -1145,15 +1144,15 @@ function (module, __webpack_exports__, __webpack_require__) {
1145
1144
  /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
1146
1145
  /* harmony import */ var aphrodite__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
1147
1146
  /* harmony import */ var aphrodite__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(aphrodite__WEBPACK_IMPORTED_MODULE_1__);
1148
- /* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
1147
+ /* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
1149
1148
  /* harmony import */ var react_router_dom__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(react_router_dom__WEBPACK_IMPORTED_MODULE_2__);
1150
1149
  /* harmony import */ var react_router__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9);
1151
1150
  /* harmony import */ var react_router__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(react_router__WEBPACK_IMPORTED_MODULE_3__);
1152
- /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(5);
1151
+ /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6);
1153
1152
  /* harmony import */ var _khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODULE_4__);
1154
- /* harmony import */ var _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(6);
1153
+ /* harmony import */ var _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(3);
1155
1154
  /* harmony import */ var _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(_khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_5__);
1156
- /* harmony import */ var _util_get_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3);
1155
+ /* harmony import */ var _util_get_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(4);
1157
1156
  /* harmony import */ var _util_is_client_side_url_js__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(1);
1158
1157
  function _extends() { _extends = Object.assign || 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); }
1159
1158
 
@@ -1171,11 +1170,20 @@ const StyledLink = Object(_khanacademy_wonder_blocks_core__WEBPACK_IMPORTED_MODU
1171
1170
  /**
1172
1171
  * A component to turn any custom component into a clickable one.
1173
1172
  *
1174
- * Works by wrapping ClickableBehavior around the child element and styling the
1175
- * child appropriately and encapsulates routing logic which can be customized.
1176
- * Expects a function which returns an element as it's child.
1173
+ * Works by wrapping `ClickableBehavior` around the child element and styling
1174
+ * the child appropriately and encapsulates routing logic which can be
1175
+ * customized. Expects a function which returns an element as its child.
1176
+ *
1177
+ * Clickable allows your components to:
1178
+ *
1179
+ * - Handle mouse / touch / keyboard events
1180
+ * - Match the standard behavior of the given role
1181
+ * - Apply custom styles based on pressed / focused / hovered state
1182
+ * - Perform Client Side Navigation when href is passed and the component is a
1183
+ * descendent of a react-router Router.
1184
+ *
1185
+ * ### Usage
1177
1186
  *
1178
- * Example usage:
1179
1187
  * ```jsx
1180
1188
  * <Clickable onClick={() => alert("You clicked me!")}>
1181
1189
  * {({hovered, focused, pressed}) =>
@@ -1244,7 +1252,7 @@ class Clickable extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
1244
1252
  } = this.props;
1245
1253
  const ClickableBehavior = Object(_util_get_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_6__[/* default */ "a"])(href, skipClientNav, router);
1246
1254
 
1247
- const getStyle = state => [styles.reset, styles.link, !hideDefaultFocusRing && state.focused && (light ? styles.focusedLight : styles.focused), style];
1255
+ const getStyle = state => [styles.reset, styles.link, !hideDefaultFocusRing && state.focused && (light ? styles.focusedLight : styles.focused), disabled && styles.disabled, style];
1248
1256
 
1249
1257
  if (beforeNav) {
1250
1258
  return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0__["createElement"](ClickableBehavior, {
@@ -1321,10 +1329,22 @@ const styles = aphrodite__WEBPACK_IMPORTED_MODULE_1__["StyleSheet"].create({
1321
1329
  cursor: "pointer"
1322
1330
  },
1323
1331
  focused: {
1324
- outline: `solid 2px ${_khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_5___default.a.blue}`
1332
+ ":focus": {
1333
+ outline: `solid 2px ${_khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_5___default.a.blue}`
1334
+ }
1325
1335
  },
1326
1336
  focusedLight: {
1327
1337
  outline: `solid 2px ${_khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_5___default.a.white}`
1338
+ },
1339
+ disabled: {
1340
+ color: _khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_5___default.a.offBlack32,
1341
+ cursor: "not-allowed",
1342
+ ":focus": {
1343
+ outline: "none"
1344
+ },
1345
+ ":focus-visible": {
1346
+ outline: `solid 2px ${_khanacademy_wonder_blocks_color__WEBPACK_IMPORTED_MODULE_5___default.a.blue}`
1347
+ }
1328
1348
  }
1329
1349
  });
1330
1350
 
@@ -1352,7 +1372,7 @@ __webpack_require__.r(__webpack_exports__);
1352
1372
  /* harmony import */ var _components_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
1353
1373
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ClickableBehavior", function() { return _components_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_1__["a"]; });
1354
1374
 
1355
- /* harmony import */ var _util_get_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
1375
+ /* harmony import */ var _util_get_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(4);
1356
1376
  /* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "getClickableBehavior", function() { return _util_get_clickable_behavior_js__WEBPACK_IMPORTED_MODULE_2__["a"]; });
1357
1377
 
1358
1378
  /* harmony import */ var _util_is_client_side_url_js__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/wonder-blocks-clickable",
3
- "version": "2.2.6",
3
+ "version": "2.2.7",
4
4
  "design": "v1",
5
5
  "description": "Clickable component for Wonder-Blocks.",
6
6
  "main": "dist/index.js",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "dependencies": {
18
18
  "@babel/runtime": "^7.16.3",
19
- "@khanacademy/wonder-blocks-core": "^4.3.1"
19
+ "@khanacademy/wonder-blocks-core": "^4.3.2"
20
20
  },
21
21
  "peerDependencies": {
22
22
  "aphrodite": "^1.2.5",
@@ -0,0 +1,152 @@
1
+ import {Meta, Story, Canvas} from "@storybook/addon-docs";
2
+ import {StyleSheet} from "aphrodite";
3
+
4
+ import Clickable from "@khanacademy/wonder-blocks-clickable";
5
+ import Color from "@khanacademy/wonder-blocks-color";
6
+ import {View} from "@khanacademy/wonder-blocks-core";
7
+ import Icon, {icons} from "@khanacademy/wonder-blocks-icon";
8
+ import Spacing from "@khanacademy/wonder-blocks-spacing";
9
+
10
+ <Meta
11
+ title="Clickable / Clickable / Accessibility"
12
+ component={Clickable}
13
+ parameters={{
14
+ previewTabs: {
15
+ canvas: {hidden: true},
16
+ },
17
+ viewMode: "docs",
18
+ chromatic: {
19
+ // Disables chromatic testing for these stories.
20
+ disableSnapshot: true,
21
+ },
22
+ }}
23
+ />
24
+
25
+ # Accessibility
26
+
27
+ ## Keyboard interactions
28
+
29
+ | Key | Action |
30
+ | --- | --- |
31
+ | Enter or Space | Activates the clickable element |
32
+
33
+
34
+ ## Roles
35
+
36
+ | Component | Role | Usage |
37
+ | --- | --- | --- |
38
+ | `<Clickable onClick={} />` | button | A clickable button element |
39
+ | `<Clickable href="/math" skipClientNav={true} />` | link | A clickable anchor element |
40
+ | `<Clickable href="/math" />` | link | A clickable anchor element (using `react-router`'s Link) |
41
+
42
+ ## Attributes
43
+ | Attribute | Usage |
44
+ | --- | --- |
45
+ | tabindex="0" | Includes the clickable element in the tab sequence. |
46
+ | aria-disabled="true" | Indicates that the element is perceivable but disabled. |
47
+ | aria-label="value" | Defines a string value that labels the clickable element. Use it in case the clickable element doesn't include any descriptive text (e.g. Icons, images) |
48
+
49
+ ## Examples
50
+
51
+ ### Labeling
52
+
53
+ `Clickable` has an `ariaLabel` prop that sets the component's accessible name.
54
+ ariaLabel should be passed when using graphical elements to let screen reader
55
+ users know the purpose of the clickable element.
56
+
57
+ _NOTE:_ If the clickable element is not graphical, it's best to avoid using `ariaLabel` as the text content of the element itself, which is read by default, should ideally be descriptive enough to not need to manually pass in the label.
58
+
59
+ This is an example of a component with an accessible label:
60
+
61
+ <Canvas>
62
+ <Story name="Labeling">
63
+ <View>
64
+ <Clickable onClick={()=>{}} aria-label="More information about this subject">
65
+ {({hovered, focused, pressed}) => (
66
+ <Icon icon={icons.info} />
67
+ )}
68
+ </Clickable>
69
+ </View>
70
+ </Story>
71
+ </Canvas>
72
+
73
+ ### Disabled state
74
+
75
+ Clickable does not need an `aria-disabled` attribute, if it also has a
76
+ `disabled` component prop. We internally take care of defining the behavior so
77
+ users can use these type of controls (including Screen Readers). By defining the
78
+ internal behavior we can ensure that the component is accessible via Keyboard
79
+ but not interactable/operatable.
80
+
81
+ <Canvas>
82
+ <Story name="Disabled state">
83
+ <Clickable
84
+ onClick={(e) => console.log("Hello, world!")}
85
+ disabled={true}
86
+ >
87
+ {({hovered, focused, pressed}) => (
88
+ "This is a disabled clickable element"
89
+ )}
90
+ </Clickable>
91
+ </Story>
92
+ </Canvas>
93
+
94
+ ### Keyboard navigation
95
+
96
+ Clickable adds support to keyboard navigation and setting ARIA attributes. This
97
+ way, your components are accessible and emulate better the browser's behavior.
98
+
99
+ **NOTE:** If you want to navigate to an external URL and/or reload the window,
100
+ make sure to use `href` and `skipClientNav={true}.
101
+
102
+ <Canvas>
103
+ <Story name="Keyboard navigation">
104
+ <View>
105
+ <Clickable role="tab" aria-controls="panel-1" id="tab-1">
106
+ {({hovered, focused, pressed}) => (
107
+ <View
108
+ style={[
109
+ styles.resting,
110
+ hovered && styles.hovered,
111
+ focused && styles.focused,
112
+ pressed && styles.pressed,
113
+ ]}
114
+ >
115
+ <Body>Open School Info</Body>
116
+ </View>
117
+ )}
118
+ </Clickable>
119
+ <View
120
+ id="panel-1"
121
+ role="tabpanel"
122
+ tabindex="0"
123
+ aria-labelledby="tab-1"
124
+ style={styles.panel}
125
+ >
126
+ This is the information for the school.
127
+ </View>
128
+ </View>
129
+ </Story>
130
+ </Canvas>
131
+
132
+ export const styles = StyleSheet.create({
133
+ resting: {
134
+ boxShadow: `inset 0px 0px 1px 1px ${Color.lightBlue}`,
135
+ padding: Spacing.xSmall_8,
136
+ },
137
+ hovered: {
138
+ textDecoration: "underline",
139
+ backgroundColor: Color.blue,
140
+ color: Color.white,
141
+ },
142
+ pressed: {
143
+ color: Color.darkBlue,
144
+ },
145
+ focused: {
146
+ outline: `solid 4px ${Color.lightBlue}`,
147
+ },
148
+ panel: {
149
+ padding: Spacing.medium_16,
150
+ boxShadow: `inset 0px 0px 0 1px ${Color.offBlack8}`,
151
+ }
152
+ });
@@ -0,0 +1,55 @@
1
+ import clickableArgtypes from "./clickable.argtypes.js";
2
+
3
+ export default {
4
+ children: {
5
+ description:
6
+ "A function that returns the a React `Element`. The function is passed an object with three boolean properties: hovered, focused, and pressed, and a `childrenProps` argument that contains all the event handlers that should be passed to the React `Element` itself.",
7
+ type: {
8
+ required: true,
9
+ },
10
+ table: {
11
+ type: {
12
+ summary:
13
+ "(state: ClickableState, childrenProps: ChildrenProps) => React.Node",
14
+ },
15
+ },
16
+ },
17
+ /**
18
+ * States
19
+ */
20
+ disabled: {
21
+ ...clickableArgtypes.disabled,
22
+ description:
23
+ "Whether the component is disabled.\n\n" +
24
+ "If the component is disabled, this component will return handlers that do nothing.",
25
+ },
26
+ /**
27
+ * Events
28
+ */
29
+ onClick: {
30
+ ...clickableArgtypes.onClick,
31
+ description:
32
+ "An onClick function which ClickableBehavior can execute when clicked.",
33
+ },
34
+ onkeyDown: clickableArgtypes.onkeyDown,
35
+ onKeyUp: clickableArgtypes.onKeyUp,
36
+ /**
37
+ * Navigation
38
+ */
39
+ skipClientNav: clickableArgtypes.skipClientNav,
40
+ rel: clickableArgtypes.rel,
41
+ target: clickableArgtypes.target,
42
+ href: {
43
+ ...clickableArgtypes.href,
44
+ description:
45
+ "Optional `href` which `ClickableBehavior` should direct to, uses client-side routing by default if react-router is present.\n\n" +
46
+ "For keyboard navigation, the default is that both an enter and space press would also navigate to this location. See the triggerOnEnter and triggerOnSpace props for more details",
47
+ },
48
+ beforeNav: clickableArgtypes.beforeNav,
49
+ safeWithNav: clickableArgtypes.safeWithNav,
50
+
51
+ /**
52
+ * Accessibility
53
+ */
54
+ role: clickableArgtypes.role,
55
+ };