Skip to content

Commit

Permalink
feat: Add baseId option to useRoverState and derivative hooks
Browse files Browse the repository at this point in the history
  • Loading branch information
diegohaz committed Nov 14, 2019
1 parent 50fd7df commit 0c2094a
Show file tree
Hide file tree
Showing 18 changed files with 340 additions and 96 deletions.
2 changes: 1 addition & 1 deletion packages/reakit/src/Id/IdState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { unstable_IdContext } from "./IdProvider";

export type unstable_IdState = {
/**
* @private
* ID that will serve as a base for all the items IDs.
*/
baseId: string;
/**
Expand Down
5 changes: 4 additions & 1 deletion packages/reakit/src/Provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
SystemProviderProps,
SystemProvider
} from "reakit-system/SystemProvider";
import { unstable_IdProvider as NewIdProvider } from "./Id/IdProvider";

export type ProviderProps = IdProviderProps & Partial<SystemProviderProps>;

Expand All @@ -14,7 +15,9 @@ export function Provider({
}: ProviderProps) {
return (
<IdProvider unstable_prefix={prefix}>
<SystemProvider unstable_system={system}>{children}</SystemProvider>
<NewIdProvider prefix={prefix}>
<SystemProvider unstable_system={system}>{children}</SystemProvider>
</NewIdProvider>
</IdProvider>
);
}
6 changes: 5 additions & 1 deletion packages/reakit/src/Radio/__tests__/RadioState-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ import { useRadioState } from "../RadioState";
expect.addSnapshotSerializer(jestSerializerStripFunctions);

test("initial state", () => {
const { result } = renderHook(() => useRadioState());
const { result } = renderHook(() => useRadioState({ baseId: "base" }));
expect(result.current).toMatchInlineSnapshot(`
Object {
"baseId": "base",
"currentId": null,
"loop": true,
"orientation": undefined,
"state": undefined,
"stops": Array [],
"unstable_idCountRef": Object {
"current": 0,
},
"unstable_moves": 0,
"unstable_pastId": null,
}
Expand Down
41 changes: 21 additions & 20 deletions packages/reakit/src/Rover/Rover.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import * as React from "react";
import { createComponent } from "reakit-system/createComponent";
import { createHook } from "reakit-system/createHook";
import { useId } from "reakit-utils/useId";
import { createOnKeyDown } from "reakit-utils/createOnKeyDown";
import { warning } from "reakit-utils/warning";
import { mergeRefs } from "reakit-utils/mergeRefs";
import { hasFocusWithin } from "reakit-utils/hasFocusWithin";
import { useAllCallbacks } from "reakit-utils/useAllCallbacks";
import {
TabbableOptions,
TabbableHTMLProps,
useTabbable
} from "../Tabbable/Tabbable";
import {
unstable_useId,
unstable_IdOptions,
unstable_IdHTMLProps
} from "../Id/Id";
import { RoverStateReturn, useRoverState } from "./RoverState";

export type RoverOptions = TabbableOptions &
unstable_IdOptions &
Pick<Partial<RoverStateReturn>, "orientation" | "unstable_moves"> &
Pick<
RoverStateReturn,
Expand All @@ -33,13 +39,13 @@ export type RoverOptions = TabbableOptions &
stopId?: string;
};

export type RoverHTMLProps = TabbableHTMLProps;
export type RoverHTMLProps = TabbableHTMLProps & unstable_IdHTMLProps;

export type RoverProps = RoverOptions & RoverHTMLProps;

export const useRover = createHook<RoverOptions, RoverHTMLProps>({
name: "Rover",
compose: useTabbable,
compose: [useTabbable, unstable_useId],
useState: useRoverState,
keys: ["stopId"],

Expand All @@ -48,13 +54,13 @@ export const useRover = createHook<RoverOptions, RoverHTMLProps>({
{
ref: htmlRef,
tabIndex: htmlTabIndex = 0,
onFocus: htmlOnFocus,
onKeyDown: htmlOnKeyDown,
...htmlProps
}
) {
const ref = React.useRef<HTMLElement>(null);
const id = useId("rover-");
const stopId = options.stopId || htmlProps.id || id;
const stopId = options.stopId || htmlProps.id || options.id;

const trulyDisabled = options.disabled && !options.focusable;
const noFocused = options.currentId == null;
Expand All @@ -63,7 +69,7 @@ export const useRover = createHook<RoverOptions, RoverHTMLProps>({
const shouldTabIndex = focused || (isFirst && noFocused);

React.useEffect(() => {
if (trulyDisabled) return undefined;
if (trulyDisabled || !stopId) return undefined;
options.register && options.register(stopId, ref);
return () => options.unregister && options.unregister(stopId);
}, [stopId, trulyDisabled, options.register, options.unregister]);
Expand All @@ -83,20 +89,14 @@ export const useRover = createHook<RoverOptions, RoverHTMLProps>({
}
}, [focused, options.unstable_moves]);

React.useEffect(() => {
if (!ref.current) return undefined;

// this is already focused, so we move silently
const onFocus = () => options.move(stopId, true);

// https://github.com/facebook/react/issues/11387#issuecomment-524113945
ref.current.addEventListener("focus", onFocus, true);
return () => {
if (ref.current) {
ref.current.removeEventListener("focus", onFocus, true);
}
};
}, [options.move, stopId]);
const onFocus = React.useCallback(
(event: React.FocusEvent) => {
if (!stopId || !event.currentTarget.contains(event.target)) return;
// this is already focused, so we move silently
options.move(stopId, true);
},
[options.move, stopId]
);

const onKeyDown = React.useMemo(
() =>
Expand Down Expand Up @@ -132,6 +132,7 @@ export const useRover = createHook<RoverOptions, RoverHTMLProps>({
ref: mergeRefs(ref, htmlRef),
id: stopId,
tabIndex: shouldTabIndex ? htmlTabIndex : -1,
onFocus: useAllCallbacks(onFocus, htmlOnFocus),
onKeyDown,
...htmlProps
};
Expand Down
35 changes: 26 additions & 9 deletions packages/reakit/src/Rover/RoverState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,19 @@ import {
SealedInitialState,
useSealedState
} from "reakit-utils/useSealedState";
import {
unstable_IdState,
unstable_IdActions,
unstable_IdInitialState,
unstable_useIdState
} from "../Id/IdState";

type Stop = {
id: string;
ref: React.RefObject<HTMLElement>;
};

export type RoverState = {
export type RoverState = unstable_IdState & {
/**
* Defines the orientation of the rover list.
*/
Expand Down Expand Up @@ -42,7 +48,7 @@ export type RoverState = {
loop: boolean;
};

export type RoverActions = {
export type RoverActions = unstable_IdActions & {
/**
* Registers the element ID and ref in the roving tab index list.
*/
Expand Down Expand Up @@ -83,9 +89,8 @@ export type RoverActions = {
unstable_orientate: (orientation: RoverState["orientation"]) => void;
};

export type RoverInitialState = Partial<
Pick<RoverState, "orientation" | "currentId" | "loop">
>;
export type RoverInitialState = unstable_IdInitialState &
Partial<Pick<RoverState, "orientation" | "currentId" | "loop">>;

export type RoverStateReturn = RoverState & RoverActions;

Expand All @@ -103,7 +108,12 @@ type RoverAction =
orientation?: RoverState["orientation"];
};

function reducer(state: RoverState, action: RoverAction): RoverState {
type RoverReducerState = Omit<RoverState, keyof unstable_IdState>;

function reducer(
state: RoverReducerState,
action: RoverAction
): RoverReducerState {
const {
stops,
currentId,
Expand Down Expand Up @@ -253,9 +263,12 @@ function reducer(state: RoverState, action: RoverAction): RoverState {
export function useRoverState(
initialState: SealedInitialState<RoverInitialState> = {}
): RoverStateReturn {
const { orientation, currentId = null, loop = false } = useSealedState(
initialState
);
const {
orientation,
currentId = null,
loop = false,
...sealed
} = useSealedState(initialState);
const [state, dispatch] = React.useReducer(reducer, {
orientation,
stops: [],
Expand All @@ -265,7 +278,10 @@ export function useRoverState(
loop
});

const idState = unstable_useIdState(sealed);

return {
...idState,
...state,
register: React.useCallback(
(id, ref) => dispatch({ type: "register", id, ref }),
Expand All @@ -292,6 +308,7 @@ export function useRoverState(
}

const keys: Array<keyof RoverStateReturn> = [
...unstable_useIdState.__keys,
"orientation",
"stops",
"currentId",
Expand Down
8 changes: 1 addition & 7 deletions packages/reakit/src/Rover/__tests__/Rover-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,6 @@ import * as React from "react";
import { render } from "@testing-library/react";
import { Rover } from "../Rover";

jest.mock("reakit-utils/useId", () => {
return {
useId: jest.fn(() => "rover")
};
});

const props: Parameters<typeof Rover>[0] = {
stopId: "rover",
stops: [],
Expand Down Expand Up @@ -39,7 +33,7 @@ test("render", () => {

test("render without state props", () => {
// @ts-ignore
const { baseElement } = render(<Rover>rover</Rover>);
const { baseElement } = render(<Rover id="rover">rover</Rover>);
expect(baseElement).toMatchInlineSnapshot(`
<body>
<div>
Expand Down

0 comments on commit 0c2094a

Please sign in to comment.