Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix clickable tests (add interaction tests for focus and few behaviour tests) #609

Merged
merged 9 commits into from
Mar 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/__tests__/interactions-helper.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { within, userEvent } from "@storybook/testing-library";
import { waitFor } from "@testing-library/react";
import { getTestId, ELEMENT_TYPES as types } from "../utils/test-utils";
import { getTestId, ELEMENT_TYPES as types, NAVIGATIONS_COMMANDS } from "../utils/test-utils";
import { expect } from "@storybook/jest";

export const ELEMENT_TYPES = types;
Expand Down Expand Up @@ -84,6 +84,18 @@ export const typeText = async (element, text, waitForDebounceMs = 250) => {
return result;
};

export const pressNavigationKey = async (command = NAVIGATIONS_COMMANDS.TAB, waitForDebounceMs = 0) => {
let promise =
command === NAVIGATIONS_COMMANDS.TAB
? userEvent.tab()
: userEvent.keyboard(command, {
delay: 50
});
const result = await promise;
await delay(waitForDebounceMs);
return result;
};

export function delay(timeout) {
return new Promise(resolve => {
if (!timeout) return resolve();
Expand Down
4 changes: 3 additions & 1 deletion src/components/Clickable/Clickable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import cx from "classnames";
import useMergeRefs from "hooks/useMergeRefs";
import { BEMClass } from "helpers/bem-helper";
import { useKeyboardButtonPressedFunc } from "hooks/useKeyboardButtonPressedFunc";
import { ELEMENT_TYPES } from "utils/test-utils";
import "./Clickable.scss";

const CSS_BASE_CLASS = "monday-style-clickable";
Expand Down Expand Up @@ -41,11 +42,12 @@ const Clickable = forwardRef(
disabled,
[bemHelper({ state: "disable-text-selection" })]: !enableTextSelection
})}
data-testid={ELEMENT_TYPES.CLICKABLE}
role={role}
onClick={disabled ? undefined : onClick}
id={id}
onKeyDown={disabled ? undefined : onKeyDown}
tabIndex={tabIndex}
tabIndex={disabled ? -1 : tabIndex}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix bug - we allowed focus on disabled clickable element

aria-label={ariaLabel}
aria-hidden={ariaHidden}
onMouseDown={onMouseDown}
Expand Down
23 changes: 21 additions & 2 deletions src/components/Clickable/__stories__/Clickable.stories.mdx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import Clickable from "../Clickable";
import { Flex } from "components";
import { ArgsTable, Story, Canvas, Meta } from "@storybook/addon-docs";
import { createComponentTemplate } from "../../../storybook/functions/create-component-story";
import "./clickable.stories.scss";
import {
BUTTON,
HIDDEN_TEXT,
ICON_BUTTON
} from "../../../storybook/components/related-components/component-description-map";
import { statesPlaySuite } from "../__tests__/clickable-interactions-tests";
import "./clickable.stories.scss";

<Meta
title="Accessibility/Clickable"
Expand All @@ -33,6 +34,24 @@ An accessibility helper component, this component simulates a button on non butt
## Props
<ArgsTable of={ Clickable } />

## Variants
### States
Clickable component supports two different states: regular state and disabled state.
The state only affects the component functionality (a user cannot interact with a disabled clickable component) and not the component appearance.
You can use the component className and style props to change the component appearance.
<Canvas>
<Story name="States" play={statesPlaySuite}>
<Flex gap={Flex.gaps.MEDIUM}>
<Clickable className="monday-story-clickable_first-element" onClick={() => alert("clicked")} ariaLabel="clickable button">
<div>Regular clickable element</div>
</Clickable>
<Clickable className="monday-story-clickable_disabled-element" onClick={() => alert("clicked")} disabled ariaLabel="disabled clickable button">
<div>Disabled clickable element</div>
</Clickable>
</Flex>
</Story>
</Canvas>

## Usage
<UsageGuidelines guidelines={[
"When you can't use button but want need to provide keyboard intractability",
Expand Down
18 changes: 14 additions & 4 deletions src/components/Clickable/__stories__/clickable.stories.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
@mixin clickable-element {
padding: var(--spacing-small);
border-radius: var(--border-radius-small);
color: var(--primary-text-color);
width: 40%;
}

.monday-story-clickable {
&_first-element {
padding: var(--spacing-small);
border-radius: var(--border-radius-small);
@include clickable-element;
background-color: var(--primary-selected-color);
color: var(--primary-text-color);
width: 40%;
}

&_disabled-element {
@include clickable-element;
background-color: var(--disabled-background-color);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
exports[`renders correctly with empty props 1`] = `
<div
className="monday-style-clickable monday-style-clickable--disable-text-selection"
data-testid="clickable"
onClick={[Function]}
onKeyDown={[Function]}
onMouseDown={[Function]}
Expand All @@ -14,6 +15,7 @@ exports[`renders correctly with empty props 1`] = `
exports[`renders correctly with props 1`] = `
<div
className="monday-style-clickable monday-style_tests-class"
data-testid="clickable"
onClick={[Function]}
onKeyDown={[Function]}
onMouseDown={[Function]}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { expect } from "@storybook/jest";
import {
getByLabelText,
pressNavigationKey,
interactionSuite,
resetFocus
} from "../../../__tests__/interactions-helper";
import { NAVIGATIONS_COMMANDS } from "utils/test-utils";

async function states_onClickTabFocusElementTest(canvas) {
const CLICKABLE_LABEL = "clickable button";
const CLICKABLE_DISABLED_LABEL = "disabled clickable button";
const clickableElement = await getByLabelText(canvas, CLICKABLE_LABEL);
const disabledClickableElement = await getByLabelText(canvas, CLICKABLE_DISABLED_LABEL);
await pressNavigationKey(NAVIGATIONS_COMMANDS.TAB);
expect(document.activeElement).toEqual(clickableElement);
await pressNavigationKey(NAVIGATIONS_COMMANDS.TAB);
expect(document.activeElement).not.toEqual(disabledClickableElement);
expect(canvas).not.toContain(document.activeElement);
}

export const statesPlaySuite = interactionSuite({
tests: [states_onClickTabFocusElementTest],
afterEach: async () => {
await resetFocus();
}
});
50 changes: 50 additions & 0 deletions src/components/Clickable/__tests__/clickable-tests.jest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import { fireEvent, render, cleanup } from "@testing-library/react";
import { ELEMENT_TYPES } from "../../../__tests__/interactions-helper";
import Clickable from "../Clickable";
import userEvent from "@testing-library/user-event";

const renderComponent = props => {
return render(<Clickable {...props} />);
};

describe("Clickable tests", () => {
afterEach(() => {
cleanup();
});

it("should call the onClick callback when clicked", () => {
const onClick = jest.fn();
const { getByTestId } = renderComponent({ onClick });
const component = getByTestId(ELEMENT_TYPES.CLICKABLE);
fireEvent.click(component);
expect(onClick.mock.calls.length).toBe(1);
});

it("should call the onClick callback when focused and enter pressed", () => {
const onClick = jest.fn();
const { getByTestId } = renderComponent({ onClick });
const component = getByTestId(ELEMENT_TYPES.CLICKABLE);
component.focus();
userEvent.keyboard("{enter}");
expect(onClick.mock.calls.length).toBe(1);
});

it("should call the onClick callback when focused and space pressed", () => {
const onClick = jest.fn();
const { getByTestId } = renderComponent({ onClick });
const component = getByTestId(ELEMENT_TYPES.CLICKABLE);
component.focus();
userEvent.keyboard("{space}");
expect(onClick.mock.calls.length).toBe(1);
});

describe("a11y", () => {
it("should add the label", () => {
const ariaLabel = "Lable Name";
const { getByLabelText } = renderComponent({ ariaLabel });
const element = getByLabelText(ariaLabel);
expect(element).toBeTruthy();
});
});
});
Loading