@khanacademy/wonder-blocks-clickable
Advanced tools
Comparing version 2.3.3 to 2.4.0
# @khanacademy/wonder-blocks-clickable | ||
## 2.4.0 | ||
### Minor Changes | ||
- ceb111df: ClickableBehavior no longer has tabIndex 0 by default. It must be passed in. | ||
## 2.3.3 | ||
@@ -4,0 +10,0 @@ |
@@ -49,4 +49,3 @@ import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/objectWithoutPropertiesLoose'; | ||
onKeyDown: () => void 0, | ||
onKeyUp: () => void 0, | ||
tabIndex: 0 | ||
onKeyUp: () => void 0 | ||
}; | ||
@@ -355,3 +354,4 @@ const keyCodes = { | ||
onFocus: this.handleFocus, | ||
onBlur: this.handleBlur | ||
onBlur: this.handleBlur, | ||
tabIndex: this.props.tabIndex | ||
}) : { | ||
@@ -371,3 +371,3 @@ onClick: this.handleClick, | ||
onBlur: this.handleBlur, | ||
tabIndex: 0 | ||
tabIndex: this.props.tabIndex | ||
}; | ||
@@ -374,0 +374,0 @@ childrenProps.rel = this.props.rel || (this.props.target === "_blank" ? "noopener noreferrer" : undefined); |
@@ -166,6 +166,3 @@ module.exports = | ||
onKeyDown: () => void 0, | ||
onKeyUp: () => void 0, | ||
// Clickable components should still be tabbable so they can | ||
// be used as anchors. | ||
tabIndex: 0 | ||
onKeyUp: () => void 0 | ||
}; | ||
@@ -229,3 +226,7 @@ const keyCodes = { | ||
* return ( | ||
* <ClickableBehavior disabled={props.disabled} onClick={props.onClick}> | ||
* <ClickableBehavior | ||
* disabled={props.disabled} | ||
* onClick={props.onClick} | ||
* tabIndex={0} | ||
* > | ||
* {({hovered}, childrenProps) => ( | ||
@@ -585,3 +586,4 @@ * <RoundRect | ||
onFocus: this.handleFocus, | ||
onBlur: this.handleBlur | ||
onBlur: this.handleBlur, | ||
tabIndex: this.props.tabIndex | ||
} : { | ||
@@ -601,5 +603,3 @@ onClick: this.handleClick, | ||
onBlur: this.handleBlur, | ||
// We set tabIndex to 0 so that users can tab to clickable | ||
// things that aren't buttons or anchors. | ||
tabIndex: 0 | ||
tabIndex: this.props.tabIndex | ||
}; // When the link is set to open in a new window, we want to set some | ||
@@ -606,0 +606,0 @@ // `rel` attributes. This is to ensure that the links we're sending folks |
{ | ||
"name": "@khanacademy/wonder-blocks-clickable", | ||
"version": "2.3.3", | ||
"version": "2.4.0", | ||
"design": "v1", | ||
@@ -5,0 +5,0 @@ "description": "Clickable component for Wonder-Blocks.", |
@@ -17,2 +17,11 @@ import clickableArgtypes from "./clickable.argtypes.js"; | ||
}, | ||
tabIndex: { | ||
control: {type: "number"}, | ||
description: `Used to indicate the tab order of an element. | ||
Use 0 to make an element focusable, and use -1 to make an | ||
element non-focusable via keyboard navigation.`, | ||
table: { | ||
type: {summary: "number"}, | ||
}, | ||
}, | ||
/** | ||
@@ -19,0 +28,0 @@ * States |
@@ -6,3 +6,3 @@ // @flow | ||
import {getClickableBehavior} from "@khanacademy/wonder-blocks-clickable"; | ||
import {View} from "@khanacademy/wonder-blocks-core"; | ||
import {View, addStyle} from "@khanacademy/wonder-blocks-core"; | ||
import Color from "@khanacademy/wonder-blocks-color"; | ||
@@ -76,2 +76,83 @@ import Spacing from "@khanacademy/wonder-blocks-spacing"; | ||
export const WrappingButton: StoryComponentType = (args) => { | ||
const ClickableBehavior = getClickableBehavior(); | ||
const StyledButton = addStyle("button"); | ||
return ( | ||
<ClickableBehavior {...args}> | ||
{(state, childrenProps) => { | ||
const {pressed, hovered, focused} = state; | ||
return ( | ||
<StyledButton | ||
style={[ | ||
styles.clickable, | ||
styles.newButton, | ||
hovered && styles.hovered, | ||
focused && styles.focused, | ||
pressed && styles.pressed, | ||
]} | ||
{...childrenProps} | ||
> | ||
This is an element wrapped with ClickableBehavior | ||
</StyledButton> | ||
); | ||
}} | ||
</ClickableBehavior> | ||
); | ||
}; | ||
WrappingButton.parameters = { | ||
chromatic: { | ||
// we don't need screenshots because this story only displays the | ||
// resting/default state. | ||
disableSnapshot: true, | ||
}, | ||
docs: { | ||
storyDescription: `This is an example of a \`<ClickableBehavior>\` | ||
wrapping a button. Since buttons have a built in tabIndex, | ||
a tabIndex does not need to be added to \`<ClickableBehavior>\` | ||
here.`, | ||
}, | ||
}; | ||
export const WithTabIndex: StoryComponentType = () => { | ||
const ClickableBehavior = getClickableBehavior(); | ||
return ( | ||
<ClickableBehavior role="button" tabIndex={0}> | ||
{(state, childrenProps) => { | ||
const {pressed, hovered, focused} = state; | ||
return ( | ||
<View | ||
style={[ | ||
styles.clickable, | ||
hovered && styles.hovered, | ||
focused && styles.focused, | ||
pressed && styles.pressed, | ||
]} | ||
{...childrenProps} | ||
> | ||
This is an element wrapped with ClickableBehavior | ||
</View> | ||
); | ||
}} | ||
</ClickableBehavior> | ||
); | ||
}; | ||
WithTabIndex.parameters = { | ||
chromatic: { | ||
// we don't need screenshots because this story only displays the | ||
// resting/default state. | ||
disableSnapshot: true, | ||
}, | ||
docs: { | ||
storyDescription: `A \`<ClickableBehavior>\` element does not have | ||
a tabIndex by default, as many elements it could wrap may have | ||
their own built in tabIndex attribute, such as buttons. If this | ||
is not the case, a tabIndex should be passed in using the | ||
\`tabIndex\` prop.`, | ||
}, | ||
}; | ||
const styles = StyleSheet.create({ | ||
@@ -83,2 +164,7 @@ clickable: { | ||
}, | ||
newButton: { | ||
border: "none", | ||
backgroundColor: Color.white, | ||
width: "100%", | ||
}, | ||
hovered: { | ||
@@ -85,0 +171,0 @@ textDecoration: "underline", |
@@ -285,3 +285,3 @@ /* eslint-disable testing-library/prefer-user-event */ | ||
test("tabIndex should be 0", () => { | ||
test("should not have a tabIndex if one is not passed in", () => { | ||
// Arrange | ||
@@ -303,10 +303,14 @@ // Act | ||
const button = screen.getByTestId("test-button-1"); | ||
expect(button).toHaveAttribute("tabIndex", "0"); | ||
expect(button).not.toHaveAttribute("tabIndex"); | ||
}); | ||
test("tabIndex should be 0 even for disabled components", () => { | ||
test("should have the tabIndex that is passed in", () => { | ||
// Arrange | ||
// Act | ||
render( | ||
<ClickableBehavior disabled={true} onClick={(e) => {}}> | ||
<ClickableBehavior | ||
disabled={false} | ||
onClick={(e) => {}} | ||
tabIndex={1} | ||
> | ||
{(state, childrenProps) => { | ||
@@ -324,5 +328,51 @@ return ( | ||
const button = screen.getByTestId("test-button-2"); | ||
expect(button).toHaveAttribute("tabIndex", "0"); | ||
expect(button).toHaveAttribute("tabIndex", "1"); | ||
}); | ||
test("should have the tabIndex that is passed in even if disabled", () => { | ||
// Arrange | ||
// Act | ||
render( | ||
<ClickableBehavior disabled={true} onClick={(e) => {}} tabIndex={1}> | ||
{(state, childrenProps) => { | ||
return ( | ||
<button data-test-id="test-button-3" {...childrenProps}> | ||
Label | ||
</button> | ||
); | ||
}} | ||
</ClickableBehavior>, | ||
); | ||
// Assert | ||
const button = screen.getByTestId("test-button-3"); | ||
expect(button).toHaveAttribute("tabIndex", "1"); | ||
}); | ||
test("should make non-interactive children keyboard focusable if tabIndex 0 is passed", () => { | ||
// Arrange | ||
render( | ||
<ClickableBehavior | ||
disabled={false} | ||
onClick={(e) => {}} | ||
tabIndex={1} | ||
> | ||
{(state, childrenProps) => { | ||
return ( | ||
<div data-test-id="test-div-1" {...childrenProps}> | ||
Label | ||
</div> | ||
); | ||
}} | ||
</ClickableBehavior>, | ||
); | ||
// Act | ||
const button = screen.getByTestId("test-div-1"); | ||
userEvent.tab(); | ||
// Assert | ||
expect(button).toHaveFocus(); | ||
}); | ||
it("does not change state if disabled", () => { | ||
@@ -329,0 +379,0 @@ const onClick = jest.fn(); |
@@ -92,2 +92,9 @@ // @flow | ||
/** | ||
* Used to indicate the tab order of an element. | ||
* Use 0 to make an element focusable, and use -1 to make an | ||
* element non-focusable via keyboard navigation. | ||
*/ | ||
tabIndex?: number, | ||
/** | ||
* A function to be executed `onclick`. | ||
@@ -210,3 +217,3 @@ */ | ||
onBlur: (e: SyntheticFocusEvent<>) => mixed, | ||
tabIndex: number, | ||
tabIndex?: number, | ||
rel?: string, | ||
@@ -227,5 +234,2 @@ |}; | ||
onKeyUp: () => void 0, | ||
// Clickable components should still be tabbable so they can | ||
// be used as anchors. | ||
tabIndex: 0, | ||
}; | ||
@@ -292,3 +296,7 @@ | ||
* return ( | ||
* <ClickableBehavior disabled={props.disabled} onClick={props.onClick}> | ||
* <ClickableBehavior | ||
* disabled={props.disabled} | ||
* onClick={props.onClick} | ||
* tabIndex={0} | ||
* > | ||
* {({hovered}, childrenProps) => ( | ||
@@ -621,2 +629,3 @@ * <RoundRect | ||
onBlur: this.handleBlur, | ||
tabIndex: this.props.tabIndex, | ||
} | ||
@@ -637,5 +646,3 @@ : { | ||
onBlur: this.handleBlur, | ||
// We set tabIndex to 0 so that users can tab to clickable | ||
// things that aren't buttons or anchors. | ||
tabIndex: 0, | ||
tabIndex: this.props.tabIndex, | ||
}; | ||
@@ -642,0 +649,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
197673
5274