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

Preparing for React 18 (useId) #342

Merged
merged 4 commits into from
Apr 19, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2
jobs:
install-job:
docker:
- image: circleci/node:dubnium
- image: circleci/node:erbium
working_directory: ~/repo
steps:
- checkout
Expand All @@ -24,7 +24,7 @@ jobs:

test-job:
docker:
- image: circleci/node:dubnium
- image: circleci/node:erbium

working_directory: ~/repo

Expand All @@ -36,7 +36,7 @@ jobs:

lint-job:
docker:
- image: circleci/node:dubnium
- image: circleci/node:erbium

working_directory: ~/repo

Expand All @@ -48,7 +48,7 @@ jobs:

build-job:
docker:
- image: circleci/node:dubnium
- image: circleci/node:erbium

working_directory: ~/repo

Expand All @@ -60,7 +60,7 @@ jobs:

coverage-job:
docker:
- image: circleci/node:dubnium
- image: circleci/node:erbium

working_directory: ~/repo

Expand All @@ -73,7 +73,7 @@ jobs:

integration-job:
docker:
- image: circleci/node:dubnium-browsers
- image: circleci/node:erbium-browsers

working_directory: ~/repo

Expand Down
21 changes: 11 additions & 10 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
module.exports = {
parser: "@typescript-eslint/parser",
parser: '@typescript-eslint/parser',
parserOptions: {
jsx: true,
},
plugins: ["react", "@typescript-eslint", "jsx-a11y"],
plugins: ['react', '@typescript-eslint', 'jsx-a11y'],
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:jsx-a11y/recommended",
'eslint:recommended',
'plugin:react/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:jsx-a11y/recommended',
],
settings: {
react: {
pragma: "React",
version: "detect",
pragma: 'React',
version: 'detect',
},
},
rules: {
"@typescript-eslint/no-non-null-assertion": "off",
"react/prop-types": "off",
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'react/prop-types': 'off',
},
};
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
lts/dubnium
lts/erbium
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,35 @@
> All notable changes to this project are documented in this file. This project
> adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [[v5.0.0]](https://github.com/springload/react-accessible-accordion/releases/tag/v5.0.0)

React Accessible Accordion now supports React 18 with its out-of-order streaming
feature.

The new out-of-order streaming feature required using React 18's
[`useId`](https://reactjs.org/blog/2022/03/29/react-v18.html#useid) hook. This
affects the DOM ids that RAA generates, changing from `accordion__heading-raa-0`
(React 16 and 17) to `accordion__heading-:r0:` (React 18). Although this change
shouldn't affect most users, if you have any code that selects ids with DOM APIs
such as `document.querySelector()` then the `:` characters will need escaping
with `\\` eg. `document.querySelector('#accordion__heading-\\:r0\\:')`.

When using older versions of React 16 or 17 the same DOM ids will be generated.

Because of this change in behaviour this is a major version upgrade.

## [[v4.0.0]](https://github.com/springload/react-accessible-accordion/releases/tag/v4.0.0)

Making `role="region"` optional on panels.

The new behaviour has no `role="region"` by default, and developers can opt-in
using the `region` prop as `<AccordionItemPanel region>`.

The previous behaviour had every panel as a `role="region"` which created an
excessive amount of regions for the screen reader.

Because of this change in behaviour this is a major version upgrade.

## [[v3.3.0]](https://github.com/springload/react-accessible-accordion/releases/tag/v3.3.0)

### Changed
Expand Down
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ to get in touch via our

## FAQs

### React 18?

RAA supports React 18, and the new out-of-order streaming feature. See the
CHANGELOG for details.

### Which design patterns does this component aim to solve?

Those described by the WAI ARIA spec's description of an 'accordion':
Expand All @@ -225,8 +230,8 @@ description, as written above. By "accordion-like", we mean components which
have collapsible items but require bespoke interactive mechanisms in order to
expand, collapse and 'disable' them. This includes (but is not limited to)
multi-step forms, like those seen in many cart/checkout flows, which we believe
require (other) complex markup in order to be considered 'accessible'.
This also includes disclosure widgets.
require (other) complex markup in order to be considered 'accessible'. This also
includes disclosure widgets.

### How do I disable an item?

Expand Down
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-accessible-accordion",
"version": "4.0.0",
"version": "5.0.0",
"description": "Accessible Accordion component for React",
"main": "dist/umd/index.js",
"module": "dist/es/index.js",
Expand Down Expand Up @@ -89,12 +89,12 @@
"@babel/preset-env": "^7.4.5",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.1.0",
"@testing-library/react": "^8.0.4",
"@testing-library/react": "12",
"@types/jest": "^24.0.15",
"@types/react": "^16.8.22",
"@types/react-dom": "^16.8.4",
"@types/react": "^18.0.5",
"@types/react-dom": "^18.0.1",
"@types/react-syntax-highlighter": "^11.0.4",
"@types/react-test-renderer": "^16.8.2",
"@types/react-test-renderer": "^18.0.0",
"@types/uuid": "^3.4.4",
"@typescript-eslint/eslint-plugin": "^3.6.0",
"@typescript-eslint/parser": "^3.6.0",
Expand All @@ -115,8 +115,8 @@
"mini-css-extract-plugin": "^0.7.0",
"prettier": "^2.0.5",
"puppeteer": "2.0.0",
"react": "16.8.0",
"react-dom": "16.8.0",
"react": "17",
"react-dom": "17",
"react-syntax-highlighter": "^12.2.1",
"react-test-renderer": "^16.8.6",
"rimraf": "^2.6.3",
Expand All @@ -133,8 +133,8 @@
"webpack-dev-server": "^3.2.1"
},
"peerDependencies": {
"react": "^16.3.2 || ^17.0.0",
"react-dom": "^16.3.3 || ^17.0.0"
"react": "^16.3.2 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.3.3 || ^17.0.0 || ^18.0.0"
},
"husky": {
"hooks": {
Expand Down
6 changes: 3 additions & 3 deletions src/components/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import * as React from 'react';
import { DivAttributes } from '../helpers/types';
import { Provider } from './AccordionContext';
import { UUID } from './ItemContext';
import { ID } from './ItemContext';

type AccordionProps = Pick<
DivAttributes,
Exclude<keyof DivAttributes, 'onChange'>
> & {
className?: string;
preExpanded?: UUID[];
preExpanded?: ID[];
allowMultipleExpanded?: boolean;
allowZeroExpanded?: boolean;
onChange?(args: UUID[]): void;
onChange?(args: ID[]): void;
};

const Accordion = ({
Expand Down
28 changes: 14 additions & 14 deletions src/components/AccordionContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,31 @@ import AccordionStore, {
InjectedHeadingAttributes,
InjectedPanelAttributes,
} from '../helpers/AccordionStore';
import { UUID } from './ItemContext';
import { ID } from './ItemContext';

export interface ProviderProps {
preExpanded?: UUID[];
preExpanded?: ID[];
allowMultipleExpanded?: boolean;
allowZeroExpanded?: boolean;
children?: React.ReactNode;
onChange?(args: UUID[]): void;
onChange?(args: ID[]): void;
}

type ProviderState = AccordionStore;

export interface AccordionContext {
allowMultipleExpanded: boolean;
allowZeroExpanded: boolean;
toggleExpanded(uuid: UUID): void;
isItemDisabled(uuid: UUID): boolean;
isItemExpanded(uuid: UUID): boolean;
toggleExpanded(uuid: ID): void;
isItemDisabled(uuid: ID): boolean;
isItemExpanded(uuid: ID): boolean;
getPanelAttributes(
uuid: UUID,
uuid: ID,
dangerouslySetExpanded?: boolean,
): InjectedPanelAttributes;
getHeadingAttributes(uuid: UUID): InjectedHeadingAttributes;
getHeadingAttributes(uuid: ID): InjectedHeadingAttributes;
getButtonAttributes(
uuid: UUID,
uuid: ID,
dangerouslySetExpanded?: boolean,
): InjectedButtonAttributes;
}
Expand All @@ -52,7 +52,7 @@ export class Provider extends React.PureComponent<
allowZeroExpanded: this.props.allowZeroExpanded,
});

toggleExpanded = (key: UUID): void => {
toggleExpanded = (key: ID): void => {
this.setState(
(state: Readonly<ProviderState>) => state.toggleExpanded(key),
() => {
Expand All @@ -63,16 +63,16 @@ export class Provider extends React.PureComponent<
);
};

isItemDisabled = (key: UUID): boolean => {
isItemDisabled = (key: ID): boolean => {
return this.state.isItemDisabled(key);
};

isItemExpanded = (key: UUID): boolean => {
isItemExpanded = (key: ID): boolean => {
return this.state.isItemExpanded(key);
};

getPanelAttributes = (
key: UUID,
key: ID,
dangerouslySetExpanded?: boolean,
): InjectedPanelAttributes => {
return this.state.getPanelAttributes(key, dangerouslySetExpanded);
Expand All @@ -84,7 +84,7 @@ export class Provider extends React.PureComponent<
};

getButtonAttributes = (
key: UUID,
key: ID,
dangerouslySetExpanded?: boolean,
): InjectedButtonAttributes => {
return this.state.getButtonAttributes(key, dangerouslySetExpanded);
Expand Down
8 changes: 4 additions & 4 deletions src/components/AccordionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import * as React from 'react';
import { useState } from 'react';
import DisplayName from '../helpers/DisplayName';
import { DivAttributes } from '../helpers/types';
import { assertValidHtmlId, nextUuid } from '../helpers/uuid';
import { assertValidHtmlId, useNextId } from '../helpers/id';
import {
Consumer as ItemConsumer,
ItemContext,
Provider as ItemProvider,
UUID,
ID,
} from './ItemContext';

type Props = DivAttributes & {
uuid?: UUID;
uuid?: ID;
activeClassName?: string;
dangerouslySetExpanded?: boolean;
};
Expand All @@ -23,7 +23,7 @@ const AccordionItem = ({
activeClassName,
...rest
}: Props): JSX.Element => {
const [instanceUuid] = useState<UUID>(nextUuid());
const [instanceUuid] = useState<ID>(useNextId());
const uuid = customUuid ?? instanceUuid;

const renderChildren = (itemContext: ItemContext): JSX.Element => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/AccordionItemButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from '../helpers/focus';
import keycodes from '../helpers/keycodes';
import { DivAttributes } from '../helpers/types';
import { assertValidHtmlId } from '../helpers/uuid';
import { assertValidHtmlId } from '../helpers/id';

import { Consumer as ItemConsumer, ItemContext } from './ItemContext';

Expand Down Expand Up @@ -91,7 +91,7 @@ type WrapperProps = Pick<
Exclude<keyof DivAttributes, keyof InjectedButtonAttributes>
>;

const AccordionItemButtonWrapper: React.SFC<WrapperProps> = (
const AccordionItemButtonWrapper: React.FC<WrapperProps> = (
props: WrapperProps,
): JSX.Element => (
<ItemConsumer>
Expand Down
4 changes: 2 additions & 2 deletions src/components/AccordionItemHeading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react';
import { InjectedHeadingAttributes } from '../helpers/AccordionStore';
import DisplayName from '../helpers/DisplayName';
import { DivAttributes } from '../helpers/types';
import { assertValidHtmlId } from '../helpers/uuid';
import { assertValidHtmlId } from '../helpers/id';

import { Consumer as ItemConsumer, ItemContext } from './ItemContext';

Expand Down Expand Up @@ -71,7 +71,7 @@ type WrapperProps = Pick<
Exclude<keyof DivAttributes, keyof InjectedHeadingAttributes>
>;

const AccordionItemHeadingWrapper: React.SFC<DivAttributes> = (
const AccordionItemHeadingWrapper: React.FC<DivAttributes> = (
props: WrapperProps,
): JSX.Element => (
<ItemConsumer>
Expand Down
8 changes: 6 additions & 2 deletions src/components/AccordionItemPanel.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ describe('AccordionItem', () => {
</Accordion>,
);
expect(
getByRole('region').getAttribute('aria-labelledby'),
getByRole('region', { hidden: true }).getAttribute(
'aria-labelledby',
),
).toBeTruthy();
});

Expand All @@ -101,7 +103,9 @@ describe('AccordionItem', () => {
const { getByText, queryByRole } = render(
<Accordion>
<AccordionItem>
<AccordionItemPanel region={false}>Hello World</AccordionItemPanel>
<AccordionItemPanel region={false}>
Hello World
</AccordionItemPanel>
</AccordionItem>
</Accordion>,
);
Expand Down