clickable-box
Advanced tools
Comparing version 0.1.5 to 0.1.6
@@ -151,20 +151,30 @@ 'use strict'; | ||
onClick = _this$props.onClick, | ||
onKeyPress = _this$props.onKeyPress; // Run user supplied `onKeyPress` first if there is one | ||
onKeyPress = _this$props.onKeyPress; | ||
if (typeof onKeyPress === "function") { | ||
onKeyPress(event); // Prevent `onClick` from running in the rare case that the user has a custom `onKeyPress` | ||
// that contains `event.preventDefault()`. | ||
if (event.isDefaultPrevented()) { | ||
return; | ||
} | ||
} | ||
switch (event.key) { | ||
case " ": | ||
onClick(event); | ||
// If space is pressed and both `onKeyPress` and `onClick` exist, only | ||
// run `onKeyPress`. | ||
if (onClick && onKeyPress) { | ||
onKeyPress(event); | ||
} else if (onKeyPress) { | ||
onKeyPress(event); | ||
} else if (onClick) { | ||
onClick(event); | ||
} | ||
break; | ||
case "Enter": | ||
onClick(event); | ||
// `onKeyPress` should run first. | ||
if (onKeyPress) { | ||
onKeyPress(event); // Prevent `onClick` from running in the rare case that the user has | ||
// a custom `onKeyPress` that contains `event.preventDefault()`. | ||
if (event.isDefaultPrevented()) { | ||
return; | ||
} | ||
} | ||
if (onClick) onClick(event); | ||
break; | ||
@@ -190,6 +200,15 @@ | ||
var isActiveButton = !disabled && !!onClick; | ||
return React.createElement(Component, _extends({ | ||
tabIndex: isActiveButton ? tabIndex : -1, | ||
role: isActiveButton ? "button" : undefined, | ||
onKeyPress: isActiveButton ? this.onKeyPress : undefined, | ||
return React.createElement(Component // Don't set `tabIndex` if `disabled`. We do set it though even if | ||
// `onClick` is not provided so that it mimics the behavior of a native | ||
// `button`. We also prevent the user from passing in their own | ||
// `tabIndex` in the case that it is disabled. This is better than a | ||
// `-1` because `-1` will make the element focusable but not reachable | ||
// via keyboard navigation. | ||
, _extends({ | ||
tabIndex: !disabled ? tabIndex : undefined // Always have `role="button"`, even if it is disabled. Combined with | ||
// `aria-disabled`, screen readers will announce this the same as | ||
// a native `button` element. | ||
, | ||
role: "button", | ||
onKeyPress: !disabled ? this.onKeyPress : undefined, | ||
onClick: isActiveButton ? onClick : undefined, | ||
@@ -196,0 +215,0 @@ "aria-disabled": !isActiveButton, |
{ | ||
"name": "clickable-box", | ||
"description": "Add `onClick` to HTML elements without sacrificing accessibility.", | ||
"version": "0.1.5", | ||
"version": "0.1.6", | ||
"author": "Daniel O'Connor <daniel@danoc.me>", | ||
@@ -6,0 +6,0 @@ "license": "MIT", |
@@ -14,19 +14,26 @@ import React from "react"; | ||
// Run user supplied `onKeyPress` first if there is one | ||
if (typeof onKeyPress === "function") { | ||
onKeyPress(event); | ||
// Prevent `onClick` from running in the rare case that the user has a custom `onKeyPress` | ||
// that contains `event.preventDefault()`. | ||
if (event.isDefaultPrevented()) { | ||
return; | ||
} | ||
} | ||
switch (event.key) { | ||
case " ": | ||
onClick(event); | ||
// If space is pressed and both `onKeyPress` and `onClick` exist, only | ||
// run `onKeyPress`. | ||
if (onClick && onKeyPress) { | ||
onKeyPress(event); | ||
} else if (onKeyPress) { | ||
onKeyPress(event); | ||
} else if (onClick) { | ||
onClick(event); | ||
} | ||
break; | ||
case "Enter": | ||
onClick(event); | ||
// `onKeyPress` should run first. | ||
if (onKeyPress) { | ||
onKeyPress(event); | ||
// Prevent `onClick` from running in the rare case that the user has | ||
// a custom `onKeyPress` that contains `event.preventDefault()`. | ||
if (event.isDefaultPrevented()) { | ||
return; | ||
} | ||
} | ||
if (onClick) onClick(event); | ||
break; | ||
@@ -57,5 +64,14 @@ default: | ||
<Component | ||
tabIndex={isActiveButton ? tabIndex : -1} | ||
role={isActiveButton ? "button" : undefined} | ||
onKeyPress={isActiveButton ? this.onKeyPress : undefined} | ||
// Don't set `tabIndex` if `disabled`. We do set it though even if | ||
// `onClick` is not provided so that it mimics the behavior of a native | ||
// `button`. We also prevent the user from passing in their own | ||
// `tabIndex` in the case that it is disabled. This is better than a | ||
// `-1` because `-1` will make the element focusable but not reachable | ||
// via keyboard navigation. | ||
tabIndex={!disabled ? tabIndex : undefined} | ||
// Always have `role="button"`, even if it is disabled. Combined with | ||
// `aria-disabled`, screen readers will announce this the same as | ||
// a native `button` element. | ||
role="button" | ||
onKeyPress={!disabled ? this.onKeyPress : undefined} | ||
onClick={isActiveButton ? onClick : undefined} | ||
@@ -62,0 +78,0 @@ aria-disabled={!isActiveButton} |
@@ -13,6 +13,2 @@ import React from "react"; | ||
const validEnterPress = { | ||
charCode: charCode.enter | ||
}; | ||
test("renders into document", () => { | ||
@@ -77,2 +73,12 @@ const children = "duckduck"; | ||
test("sets role attribute", () => { | ||
const children = "duckduck"; | ||
const { getByText } = render( | ||
<ClickableBox onClick={() => {}}>{children}</ClickableBox> | ||
); | ||
expect(getByText(children).getAttribute("role")).toBe("button"); | ||
}); | ||
describe("events", () => { | ||
@@ -90,19 +96,19 @@ test("fires event when clicked on", () => { | ||
test("fires event when enter is pressed", () => { | ||
const handleClick = jest.fn(); | ||
test("fires `onClick` when enter is pressed", () => { | ||
const onClick = jest.fn(); | ||
const { getByText } = render( | ||
<ClickableBox onClick={handleClick}>Submit</ClickableBox> | ||
<ClickableBox onClick={onClick}>Submit</ClickableBox> | ||
); | ||
fireEvent.keyPress(getByText("Submit"), validEnterPress); | ||
fireEvent.keyPress(getByText("Submit"), { charCode: charCode.enter }); | ||
expect(handleClick).toHaveBeenCalledTimes(1); | ||
expect(onClick).toHaveBeenCalledTimes(1); | ||
}); | ||
test("fires event when space is pressed", () => { | ||
const handleClick = jest.fn(); | ||
test("fires `onClick` when space is pressed", () => { | ||
const onClick = jest.fn(); | ||
const { getByText } = render( | ||
<ClickableBox onClick={handleClick}>Submit</ClickableBox> | ||
<ClickableBox onClick={onClick}>Submit</ClickableBox> | ||
); | ||
@@ -114,11 +120,37 @@ | ||
expect(handleClick).toHaveBeenCalledTimes(1); | ||
expect(onClick).toHaveBeenCalledTimes(1); | ||
}); | ||
test("runs a passed in `onKeyPress` as well as `onClick` if a valid key is pressed", () => { | ||
const handleClick = jest.fn(); | ||
test("fires `onKeyPress` when space is pressed", () => { | ||
const onKeyPress = jest.fn(); | ||
const { getByText } = render( | ||
<ClickableBox onClick={handleClick} onKeyPress={onKeyPress}> | ||
<ClickableBox onKeyPress={onKeyPress}>Submit</ClickableBox> | ||
); | ||
fireEvent.keyPress(getByText("Submit"), { | ||
charCode: charCode.space | ||
}); | ||
expect(onKeyPress).toHaveBeenCalledTimes(1); | ||
}); | ||
test("fires `onKeyPress` when enter is pressed", () => { | ||
const onKeyPress = jest.fn(); | ||
const { getByText } = render( | ||
<ClickableBox onKeyPress={onKeyPress}>Submit</ClickableBox> | ||
); | ||
fireEvent.keyPress(getByText("Submit"), { charCode: charCode.enter }); | ||
expect(onKeyPress).toHaveBeenCalledTimes(1); | ||
}); | ||
test("runs a passed in `onKeyPress` as well as `onClick` if enter key is pressed", () => { | ||
const onClick = jest.fn(); | ||
const onKeyPress = jest.fn(); | ||
const { getByText } = render( | ||
<ClickableBox onClick={onClick} onKeyPress={onKeyPress}> | ||
Submit | ||
@@ -128,8 +160,26 @@ </ClickableBox> | ||
fireEvent.keyPress(getByText("Submit"), validEnterPress); | ||
fireEvent.keyPress(getByText("Submit"), { charCode: charCode.enter }); | ||
expect(handleClick).toHaveBeenCalledTimes(1); | ||
expect(onClick).toHaveBeenCalledTimes(1); | ||
expect(onKeyPress).toHaveBeenCalledTimes(1); | ||
}); | ||
test("runs only `onKeyPress` if space key is presssed even though `onClick` is provided", () => { | ||
const onClick = jest.fn(); | ||
const onKeyPress = jest.fn(); | ||
const { getByText } = render( | ||
<ClickableBox onClick={onClick} onKeyPress={onKeyPress}> | ||
Submit | ||
</ClickableBox> | ||
); | ||
fireEvent.keyPress(getByText("Submit"), { | ||
charCode: charCode.space | ||
}); | ||
expect(onClick).toHaveBeenCalledTimes(0); | ||
expect(onKeyPress).toHaveBeenCalledTimes(1); | ||
}); | ||
test("does not run `onClick` if a valid key is pressed, the consumer passes in their on `onKeyPress`, and the consumer's `onKeyPress` prevents the event", () => { | ||
@@ -147,3 +197,3 @@ const handleClick = jest.fn(); | ||
fireEvent.keyPress(getByText("Submit"), validEnterPress); | ||
fireEvent.keyPress(getByText("Submit"), { charCode: charCode.enter }); | ||
@@ -158,3 +208,3 @@ expect(handleClick).toHaveBeenCalledTimes(0); | ||
const validKey = validEnterPress; | ||
const validKey = { charCode: charCode.enter }; | ||
@@ -192,10 +242,12 @@ const { getByText } = render( | ||
describe("disabled", () => { | ||
test("sets `tabIndex` to `-1`", () => { | ||
test("`tabIndex` is `null`", () => { | ||
const children = "duckduck"; | ||
const { getByText } = render( | ||
<ClickableBox disabled>{children}</ClickableBox> | ||
<ClickableBox disabled onClick={() => {}}> | ||
{children} | ||
</ClickableBox> | ||
); | ||
expect(getByText(children).getAttribute("tabIndex")).toBe("-1"); | ||
expect(getByText(children).getAttribute("tabIndex")).toBeNull(); | ||
}); | ||
@@ -208,3 +260,3 @@ | ||
// eslint-disable-next-line jsx-a11y/tabindex-no-positive | ||
<ClickableBox disabled tabIndex={123}> | ||
<ClickableBox disabled tabIndex={123} onClick={() => {}}> | ||
{children} | ||
@@ -214,3 +266,3 @@ </ClickableBox> | ||
expect(getByText(children).getAttribute("tabIndex")).toBe("-1"); | ||
expect(getByText(children).getAttribute("tabIndex")).toBeNull(); | ||
}); | ||
@@ -251,3 +303,5 @@ | ||
const { getByText } = render( | ||
<ClickableBox disabled>{children}</ClickableBox> | ||
<ClickableBox disabled onClick={() => {}}> | ||
{children} | ||
</ClickableBox> | ||
); | ||
@@ -262,3 +316,5 @@ | ||
const { getByText } = render( | ||
<ClickableBox disabled>{children}</ClickableBox> | ||
<ClickableBox disabled onClick={() => {}}> | ||
{children} | ||
</ClickableBox> | ||
); | ||
@@ -278,3 +334,3 @@ | ||
fireEvent.keyPress(getByText("Submit"), validEnterPress); | ||
fireEvent.keyPress(getByText("Submit"), { charCode: charCode.enter }); | ||
@@ -286,10 +342,2 @@ expect(onKeyPress).toHaveBeenCalledTimes(0); | ||
describe("`onClick` prop is not provided", () => { | ||
test("sets `tabIndex` to `-1`", () => { | ||
const children = "duckduck"; | ||
const { getByText } = render(<ClickableBox>{children}</ClickableBox>); | ||
expect(getByText(children).getAttribute("tabIndex")).toBe("-1"); | ||
}); | ||
test("does not error when space is pressed", () => { | ||
@@ -303,2 +351,8 @@ const { getByText } = render(<ClickableBox>Submit</ClickableBox>); | ||
test("does not error when enter is pressed", () => { | ||
const { getByText } = render(<ClickableBox>Submit</ClickableBox>); | ||
fireEvent.keyPress(getByText("Submit"), { charCode: charCode.enter }); | ||
}); | ||
test("does not error event when clicked on", () => { | ||
@@ -308,14 +362,2 @@ const { getByText } = render(<ClickableBox>Submit</ClickableBox>); | ||
}); | ||
test("does not run passed in `onKeyPress`", () => { | ||
const onKeyPress = jest.fn(); | ||
const { getByText } = render( | ||
<ClickableBox onKeyPress={onKeyPress}>Submit</ClickableBox> | ||
); | ||
fireEvent.keyPress(getByText("Submit"), validEnterPress); | ||
expect(onKeyPress).toHaveBeenCalledTimes(0); | ||
}); | ||
}); |
29059
621