Skip to content

Commit 8d39207

Browse files
committed
Added Molecules / AccessibleList
1 parent 3bc28cc commit 8d39207

5 files changed

Lines changed: 156 additions & 0 deletions

File tree

src/constants/keyboard-keys.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const KeyboardKeys = {
2+
Comma: "Comma",
3+
DownArrow: "ArrowDown",
4+
End: "End",
5+
Enter: "Enter",
6+
Escape: "Escape",
7+
Home: "Home",
8+
LeftArrow: "ArrowLeft",
9+
RightArrow: "ArrowRight",
10+
Space: " ",
11+
Tab: "Tab",
12+
UpArrow: "ArrowUp",
13+
};
14+
15+
export { KeyboardKeys };

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export { HeadingPriority } from "./atoms/constants/heading-priority";
3838
export { IconSizes } from "./atoms/constants/icon-sizes";
3939
export { Icons } from "./atoms/constants/icons";
4040
export { InputTypes } from "./atoms/constants/input-types";
41+
export { KeyboardKeys } from "./constants/keyboard-keys";
4142
export { ParagraphSizes } from "./atoms/constants/paragraph-sizes";
4243
export { SvgIcons } from "./atoms/constants/svg-icons";
4344

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Faker from "faker";
2+
import { AccessibleList } from "./accessible-list";
3+
import React from "react";
4+
import { Button } from "../../atoms/buttons/button";
5+
import { ButtonSizes } from "../../atoms/constants/button-sizes";
6+
import { ButtonStyles } from "../../atoms/constants/button-styles";
7+
8+
export default {
9+
component: AccessibleList,
10+
title: "Molecules | Accessible List / Accessible List",
11+
};
12+
13+
export const accessibleListDefault = () => (
14+
<AccessibleList focusFirstItem={true}>
15+
<Button style={ButtonStyles.Primary} size={ButtonSizes.Medium}>
16+
{Faker.lorem.words(5)}
17+
</Button>
18+
</AccessibleList>
19+
);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// import React from "react";
2+
// import { render } from "@testing-library/react";
3+
// import { AccessibleList } from "./accessible-list";
4+
5+
describe("AccessibleList", () => {
6+
test.skip("TODO - https://github.com/AndcultureCode/AndcultureCode.JavaScript.React.Components/issues/19", () => {});
7+
});
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import * as React from "react";
2+
import { useEffect } from "react";
3+
import { KeyboardKeys } from "../../constants/keyboard-keys";
4+
5+
// -----------------------------------------------------------------------------------------
6+
// #region Interfaces
7+
// -----------------------------------------------------------------------------------------
8+
9+
interface AccessibleListProps {
10+
focusFirstItem: boolean;
11+
onEsc?: () => void;
12+
}
13+
14+
// #endregion Interfaces
15+
16+
// -----------------------------------------------------------------------------------------
17+
// #region Component
18+
// -----------------------------------------------------------------------------------------
19+
20+
/**
21+
* Applies accessible keyboard functionality to a list of elements. For example, arrow key movement
22+
* between items.
23+
*/
24+
const AccessibleList: React.FunctionComponent<AccessibleListProps> = (
25+
props: React.PropsWithChildren<AccessibleListProps>
26+
) => {
27+
const [current, setCurrent] = React.useState<number>(0);
28+
const refArray: HTMLElement[] = [];
29+
30+
useEffect(() => {
31+
const element = refArray[current];
32+
if (element == null || !props.focusFirstItem) {
33+
return;
34+
}
35+
36+
element.focus();
37+
}, [refArray, current, props.focusFirstItem]);
38+
39+
const handleKeyDown = (e: KeyboardEvent) => {
40+
if (
41+
e.key === KeyboardKeys.DownArrow &&
42+
current === refArray.length - 1
43+
) {
44+
e.preventDefault();
45+
setCurrent(0);
46+
return;
47+
}
48+
49+
if (e.key === KeyboardKeys.UpArrow && current === 0) {
50+
e.preventDefault();
51+
setCurrent(refArray.length - 1);
52+
return;
53+
}
54+
55+
if (
56+
e.key === KeyboardKeys.DownArrow &&
57+
current !== refArray.length - 1
58+
) {
59+
e.preventDefault();
60+
61+
setCurrent(current + 1);
62+
return;
63+
}
64+
65+
if (e.key === KeyboardKeys.UpArrow && current !== 0) {
66+
e.preventDefault();
67+
68+
setCurrent(current - 1);
69+
return;
70+
}
71+
72+
if (e.key === KeyboardKeys.Escape) {
73+
e.preventDefault();
74+
setCurrent(0);
75+
76+
if (props.onEsc != null) {
77+
props.onEsc();
78+
}
79+
return;
80+
}
81+
};
82+
83+
const renderChildren = () => {
84+
let validElementIndex = 0;
85+
return React.Children.map(props.children, (child: React.ReactNode) => {
86+
if (!React.isValidElement(child)) {
87+
return child;
88+
}
89+
90+
return React.cloneElement(child, {
91+
...child.props,
92+
onClick: () => {
93+
if (child.props.onClick != null) {
94+
child.props.onClick();
95+
}
96+
},
97+
onKeyDown: handleKeyDown,
98+
ref: (el: HTMLElement) => (refArray[validElementIndex++] = el),
99+
});
100+
});
101+
};
102+
103+
return <React.Fragment>{renderChildren()}</React.Fragment>;
104+
};
105+
106+
// #endregion Component
107+
108+
// -----------------------------------------------------------------------------------------
109+
// #region Exports
110+
// -----------------------------------------------------------------------------------------
111+
112+
export { AccessibleList };
113+
114+
// #endregion Exports

0 commit comments

Comments
 (0)