Skip to content

Commit

Permalink
feat(useDisclosure): adds useDisclosure hook and test (#9006)
Browse files Browse the repository at this point in the history
* feat(useDisclosure): adds useDisclosure hook and test

* feat(useDisclosure): adds useDisclosure hook and test - remove ref
  • Loading branch information
andreancardona authored Jul 5, 2021
1 parent 8ecc657 commit 6e405a2
Show file tree
Hide file tree
Showing 2 changed files with 178 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* Copyright IBM Corp. 2016, 2018
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import { cleanup, render, screen } from '@testing-library/react';
import React from 'react';
import userEvent, { specialChars } from '@testing-library/user-event';
import { useDisclosure } from '../index.js';
import '@testing-library/jest-dom';

describe('useDisclosure', () => {
afterEach(cleanup);

// https://www.w3.org/TR/wai-aria-practices-1.1/#keyboard-interaction-8
it('should toggle visibility when the button is clicked', () => {
function TestComponent() {
const { buttonProps, contentProps, open } = useDisclosure('testid');
return (
<>
<button type="button" {...buttonProps}>
trigger
</button>
<div {...contentProps} hidden={!open}>
content
</div>
</>
);
}

render(<TestComponent />);

const content = screen.getByText('content');
expect(content).not.toBeVisible();

const trigger = screen.getByText('trigger');

userEvent.tab();
expect(trigger).toHaveFocus();

userEvent.click(document.activeElement);
expect(content).toBeVisible();

userEvent.click(document.activeElement);
expect(content).not.toBeVisible();
});

it('should toggle visibility when the button is focused and Enter or Space is pressed', () => {
function TestComponent() {
const { buttonProps, contentProps, open } = useDisclosure('testid');
return (
<>
<button type="button" {...buttonProps}>
trigger
</button>
<div {...contentProps} hidden={!open}>
content
</div>
</>
);
}

render(<TestComponent />);

const trigger = screen.getByText('trigger');

userEvent.type(trigger, `${specialChars.space}`);
expect(trigger).toHaveFocus();

userEvent.type(trigger, `${specialChars.enter}`);
expect(trigger).toHaveFocus();
});

// https://www.w3.org/TR/wai-aria-practices-1.1/#wai-aria-roles-states-and-properties-8
it('should set `aria-expanded` to match the visibility of the content', () => {
function TestComponent() {
const { buttonProps, contentProps, open } = useDisclosure('testid');
return (
<>
<button type="button" {...buttonProps}>
trigger
</button>
<div {...contentProps} hidden={!open}>
content
</div>
</>
);
}

render(<TestComponent />);

const trigger = screen.getByRole('button');
expect(trigger).toHaveAttribute('aria-expanded', 'false');

userEvent.click(trigger);

expect(trigger).toHaveAttribute('aria-expanded', 'true');
});

it('should set `aria-controls` to match the id of the content', () => {
function TestComponent() {
const { buttonProps, contentProps, open } = useDisclosure('testid');
return (
<>
<button type="button" {...buttonProps}>
trigger
</button>
<div {...contentProps} hidden={!open}>
content
</div>
</>
);
}

render(<TestComponent />);

const content = screen.getByText('content');
const trigger = screen.getByText('trigger');
const contentId = content.id;

expect(trigger).toHaveAttribute('aria-controls', contentId);
});

it('should set `id` on the content', () => {
function TestComponent() {
const { buttonProps, contentProps, open } = useDisclosure('testid');
return (
<>
<button type="button" {...buttonProps}>
trigger
</button>
<div {...contentProps} hidden={!open}>
content
</div>
</>
);
}

render(<TestComponent />);

const content = screen.getByText('content');

expect(content).toHaveAttribute('id');
});
});
31 changes: 31 additions & 0 deletions packages/react/src/components/Disclosure/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright IBM Corp. 2016, 2018
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import { useState } from 'react';

function useDisclosure(id) {
const [open, setOpen] = useState(false);

const buttonProps = {
'aria-controls': id,
'aria-expanded': open,
onClick() {
setOpen(!open);
},
};
const contentProps = {
id,
};

return {
buttonProps,
contentProps,
open,
};
}

export { useDisclosure };

0 comments on commit 6e405a2

Please sign in to comment.