@openedx/paragon 23.1.0 → 23.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/core.css +4 -4
  2. package/dist/core.css.map +1 -1
  3. package/dist/hooks/useArrowKeyNavigation.d.ts +10 -0
  4. package/dist/hooks/useArrowKeyNavigation.js +8 -11
  5. package/dist/hooks/useArrowKeyNavigation.js.map +1 -1
  6. package/dist/hooks/useIndexOfLastVisibleChild.d.ts +12 -0
  7. package/dist/hooks/useIndexOfLastVisibleChild.js +16 -15
  8. package/dist/hooks/useIndexOfLastVisibleChild.js.map +1 -1
  9. package/dist/hooks/useIsVisible.d.ts +3 -0
  10. package/dist/hooks/useIsVisible.js +1 -1
  11. package/dist/hooks/useIsVisible.js.map +1 -1
  12. package/dist/hooks/useToggle.d.ts +12 -0
  13. package/dist/hooks/useToggle.js +7 -18
  14. package/dist/hooks/useToggle.js.map +1 -1
  15. package/dist/hooks/useWindowSize.d.ts +6 -0
  16. package/dist/hooks/useWindowSize.js.map +1 -1
  17. package/dist/index.d.ts +5 -5
  18. package/dist/index.js +5 -5
  19. package/dist/light.css +4 -4
  20. package/dist/light.css.map +1 -1
  21. package/package.json +1 -1
  22. package/src/hooks/tests/{useIndexOfLastVisibleChild.test.jsx → useIndexOfLastVisibleChild.test.tsx} +2 -2
  23. package/src/hooks/tests/{useToggle.test.jsx → useToggle.test.tsx} +2 -1
  24. package/src/hooks/{useArrowKeyNavigation.jsx → useArrowKeyNavigation.tsx} +37 -13
  25. package/src/hooks/{useIndexOfLastVisibleChild.jsx → useIndexOfLastVisibleChild.tsx} +18 -16
  26. package/src/hooks/{useIsVisible.jsx → useIsVisible.tsx} +6 -3
  27. package/src/hooks/useToggle.tsx +38 -0
  28. package/src/hooks/{useWindowSize.jsx → useWindowSize.tsx} +7 -2
  29. package/src/index.d.ts +5 -5
  30. package/src/index.js +5 -5
  31. package/styles/css/core/custom-media-breakpoints.css +2 -2
  32. package/styles/css/core/variables.css +2 -2
  33. package/styles/css/themes/light/utility-classes.css +2 -2
  34. package/styles/css/themes/light/variables.css +2 -2
  35. package/tokens/style-dictionary.js +2 -2
  36. package/src/hooks/useToggle.jsx +0 -37
  37. /package/src/hooks/tests/{useWindowSize.test.jsx → useWindowSize.test.tsx} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openedx/paragon",
3
- "version": "23.1.0",
3
+ "version": "23.2.0",
4
4
  "description": "Accessible, responsive UI component library based on Bootstrap.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -12,8 +12,8 @@ window.ResizeObserver = window.ResizeObserver
12
12
  }));
13
13
 
14
14
  function TestComponent() {
15
- const [containerElementRef, setContainerElementRef] = React.useState(null);
16
- const overflowElementRef = React.useRef(null);
15
+ const [containerElementRef, setContainerElementRef] = React.useState<HTMLDivElement | null>(null);
16
+ const overflowElementRef = React.useRef<HTMLDivElement>(null);
17
17
  const indexOfLastVisibleChild = useIndexOfLastVisibleChild(containerElementRef, overflowElementRef.current);
18
18
 
19
19
  return (
@@ -4,6 +4,7 @@ import { render, screen } from '@testing-library/react';
4
4
  import userEvent from '@testing-library/user-event';
5
5
 
6
6
  import { useToggle } from '../..';
7
+ import { ToggleHandlers } from '../useToggle';
7
8
 
8
9
  const TOGGLE_IS_ON = 'on';
9
10
  const TOGGLE_IS_OFF = 'off';
@@ -19,7 +20,7 @@ const resetHandlerMocks = () => {
19
20
  };
20
21
 
21
22
  // eslint-disable-next-line react/prop-types
22
- function FakeComponent({ defaultIsOn, handlers }) {
23
+ function FakeComponent({ defaultIsOn, handlers }: { defaultIsOn: boolean, handlers: ToggleHandlers }) {
23
24
  const [isOn, setOn, setOff, toggle] = useToggle(defaultIsOn, handlers);
24
25
 
25
26
  return (
@@ -1,16 +1,24 @@
1
1
  import { useRef, useEffect } from 'react';
2
2
 
3
- /**
4
- * A React hook to enable arrow key navigation on a component.
5
- */
3
+ interface HandleEnterArgs {
4
+ event: KeyboardEvent;
5
+ currentIndex: number;
6
+ activeElement: HTMLElement;
7
+ }
6
8
 
7
- function handleEnter({ event, currentIndex, activeElement }) {
9
+ function handleEnter({ event, currentIndex, activeElement }: HandleEnterArgs) {
8
10
  if (currentIndex === -1) { return; }
9
11
  activeElement.click();
10
12
  event.preventDefault();
11
13
  }
12
14
 
13
- function handleArrowKey({ event, currentIndex, availableElements }) {
15
+ interface HandleArrowKeyArgs {
16
+ event: KeyboardEvent;
17
+ currentIndex: number;
18
+ availableElements: NodeListOf<HTMLElement>;
19
+ }
20
+
21
+ function handleArrowKey({ event, currentIndex, availableElements }: HandleArrowKeyArgs) {
14
22
  // If the focus isn't in the container, focus on the first thing
15
23
  if (currentIndex === -1) { availableElements[0].focus(); }
16
24
 
@@ -36,6 +44,13 @@ function handleArrowKey({ event, currentIndex, availableElements }) {
36
44
  event.preventDefault();
37
45
  }
38
46
 
47
+ interface HandleEventsArgs {
48
+ event: KeyboardEvent;
49
+ ignoredKeys?: string[];
50
+ parentNode: HTMLElement | undefined;
51
+ selectors?: string;
52
+ }
53
+
39
54
  /**
40
55
  * Implement arrow key navigation for the given parentNode
41
56
  */
@@ -44,7 +59,7 @@ function handleEvents({
44
59
  ignoredKeys = [],
45
60
  parentNode,
46
61
  selectors = 'a,button,input',
47
- }) {
62
+ }: HandleEventsArgs) {
48
63
  if (!parentNode) { return; }
49
64
 
50
65
  const { key } = event;
@@ -60,7 +75,7 @@ function handleEvents({
60
75
  if (!parentNode.contains(activeElement)) { return; }
61
76
 
62
77
  // Get the list of elements we're allowed to scroll through
63
- const availableElements = parentNode.querySelectorAll(selectors);
78
+ const availableElements = parentNode.querySelectorAll<HTMLElement>(selectors);
64
79
 
65
80
  // No elements are available to loop through.
66
81
  if (!availableElements.length) { return; }
@@ -70,18 +85,27 @@ function handleEvents({
70
85
  (availableElement) => availableElement === activeElement,
71
86
  );
72
87
 
73
- if (key === 'Enter') {
74
- handleEnter({ event, currentIndex, activeElement });
88
+ if (key === 'Enter' && activeElement) {
89
+ handleEnter({ event, currentIndex, activeElement: activeElement as HTMLElement });
75
90
  }
76
91
  handleArrowKey({ event, currentIndex, availableElements });
77
92
  }
78
93
 
79
- export default function useArrowKeyNavigation(props) {
80
- const { selectors, ignoredKeys } = props || {};
81
- const parentNode = useRef();
94
+ export interface ArrowKeyNavProps {
95
+ /** e.g. 'a,button,input' */
96
+ selectors?: string;
97
+ ignoredKeys?: string[];
98
+ }
99
+
100
+ /**
101
+ * A React hook to enable arrow key navigation on a component.
102
+ */
103
+ export default function useArrowKeyNavigation(props: ArrowKeyNavProps = {}) {
104
+ const { selectors, ignoredKeys } = props;
105
+ const parentNode = useRef<HTMLElement>();
82
106
 
83
107
  useEffect(() => {
84
- const eventHandler = (event) => {
108
+ const eventHandler = (event: KeyboardEvent) => {
85
109
  handleEvents({
86
110
  event, ignoredKeys, parentNode: parentNode.current, selectors,
87
111
  });
@@ -5,25 +5,32 @@ import { useLayoutEffect, useState } from 'react';
5
5
  * that fits within its bounding rectangle. This is done by summing the widths
6
6
  * of the children until they exceed the width of the container.
7
7
  *
8
- * @param {Element} containerElementRef - container element
9
- * @param {Element} overflowElementRef - overflow element
10
- *
11
8
  * The hook returns the index of the last visible child.
9
+ *
10
+ * @param containerElementRef - container element
11
+ * @param overflowElementRef - overflow element
12
12
  */
13
- const useIndexOfLastVisibleChild = (containerElementRef, overflowElementRef) => {
13
+ const useIndexOfLastVisibleChild = (
14
+ containerElementRef: HTMLElement | null,
15
+ overflowElementRef: HTMLElement | null,
16
+ ): number => {
14
17
  const [indexOfLastVisibleChild, setIndexOfLastVisibleChild] = useState(-1);
15
18
 
16
19
  useLayoutEffect(() => {
20
+ if (!containerElementRef) {
21
+ return undefined;
22
+ }
23
+
17
24
  function updateLastVisibleChildIndex() {
18
25
  // Get array of child nodes from NodeList form
19
- const childNodesArr = Array.prototype.slice.call(containerElementRef.children);
26
+ const childNodesArr = Array.prototype.slice.call(containerElementRef!.children);
20
27
  const { nextIndexOfLastVisibleChild } = childNodesArr
21
28
  // filter out the overflow element
22
29
  .filter(childNode => childNode !== overflowElementRef)
23
30
  // sum the widths to find the last visible element's index
24
31
  .reduce((acc, childNode, index) => {
25
32
  acc.sumWidth += childNode.getBoundingClientRect().width;
26
- if (acc.sumWidth <= containerElementRef.getBoundingClientRect().width) {
33
+ if (acc.sumWidth <= containerElementRef!.getBoundingClientRect().width) {
27
34
  acc.nextIndexOfLastVisibleChild = index;
28
35
  }
29
36
  return acc;
@@ -32,23 +39,18 @@ const useIndexOfLastVisibleChild = (containerElementRef, overflowElementRef) =>
32
39
  // sometimes we'll show a dropdown with one item in it when it would fit,
33
40
  // but allowing this case dramatically simplifies the calculations we need
34
41
  // to do above.
35
- sumWidth: overflowElementRef ? overflowElementRef.getBoundingClientRect().width : 0,
42
+ sumWidth: overflowElementRef?.getBoundingClientRect().width ?? 0,
36
43
  nextIndexOfLastVisibleChild: -1,
37
44
  });
38
45
 
39
46
  setIndexOfLastVisibleChild(nextIndexOfLastVisibleChild);
40
47
  }
41
48
 
42
- if (containerElementRef) {
43
- updateLastVisibleChildIndex();
44
-
45
- const resizeObserver = new ResizeObserver(() => updateLastVisibleChildIndex());
46
- resizeObserver.observe(containerElementRef);
47
-
48
- return () => resizeObserver.disconnect();
49
- }
49
+ updateLastVisibleChildIndex();
50
50
 
51
- return undefined;
51
+ const resizeObserver = new ResizeObserver(() => updateLastVisibleChildIndex());
52
+ resizeObserver.observe(containerElementRef);
53
+ return () => resizeObserver.disconnect();
52
54
  }, [containerElementRef, overflowElementRef]);
53
55
 
54
56
  return indexOfLastVisibleChild;
@@ -1,7 +1,10 @@
1
- import { useRef, useState, useEffect } from 'react';
1
+ import React, { useRef, useState, useEffect } from 'react';
2
2
 
3
- const useIsVisible = (defaultIsVisible = true) => {
4
- const sentinelRef = useRef();
3
+ const useIsVisible = (defaultIsVisible = true): [
4
+ isVisible: boolean,
5
+ sentinelRef: React.MutableRefObject<HTMLElement | null>,
6
+ ] => {
7
+ const sentinelRef = useRef<HTMLElement | null>(null);
5
8
  const [isVisible, setIsVisible] = useState(defaultIsVisible);
6
9
 
7
10
  useEffect(() => {
@@ -0,0 +1,38 @@
1
+ import { useState, useCallback } from 'react';
2
+
3
+ export type Toggler = [
4
+ isOn: boolean,
5
+ setOn: () => void,
6
+ setOff: () => void,
7
+ toggle: () => void,
8
+ ];
9
+
10
+ export interface ToggleHandlers {
11
+ handleToggleOn?: () => void;
12
+ handleToggleOff?: () => void;
13
+ handleToggle?: (newStatus: boolean) => void;
14
+ }
15
+
16
+ export default function useToggle(defaultIsOn = false, handlers: ToggleHandlers = {}): Toggler {
17
+ const { handleToggleOn, handleToggleOff, handleToggle } = handlers;
18
+ const [isOn, setIsOn] = useState(defaultIsOn);
19
+
20
+ const setOn = useCallback(() => {
21
+ setIsOn(true);
22
+ handleToggleOn?.();
23
+ handleToggle?.(true);
24
+ }, [handleToggleOn, handleToggle]);
25
+
26
+ const setOff = useCallback(() => {
27
+ setIsOn(false);
28
+ handleToggleOff?.();
29
+ handleToggle?.(false);
30
+ }, [handleToggleOff, handleToggle]);
31
+
32
+ const toggle = useCallback(() => {
33
+ const doToggle = isOn ? setOff : setOn;
34
+ doToggle();
35
+ }, [isOn, setOn, setOff]);
36
+
37
+ return [isOn, setOn, setOff, toggle];
38
+ }
@@ -1,9 +1,14 @@
1
1
  import { useState, useLayoutEffect } from 'react';
2
2
 
3
- function useWindowSize() {
3
+ export interface WindowSizeData {
4
+ width: number | undefined;
5
+ height: number | undefined;
6
+ }
7
+
8
+ function useWindowSize(): WindowSizeData {
4
9
  // Initialize state with undefined width/height so server and client renders match
5
10
  // Learn more here: https://joshwcomeau.com/react/the-perils-of-rehydration/
6
- const [windowSize, setWindowSize] = useState({
11
+ const [windowSize, setWindowSize] = useState<WindowSizeData>({
7
12
  width: undefined,
8
13
  height: undefined,
9
14
  });
package/src/index.d.ts CHANGED
@@ -18,6 +18,11 @@ export { default as ModalLayer } from './Modal/ModalLayer';
18
18
  export { default as Overlay, OverlayTrigger } from './Overlay';
19
19
  export { default as Portal } from './Modal/Portal';
20
20
  export { default as Tooltip } from './Tooltip';
21
+ export { default as useWindowSize, type WindowSizeData } from './hooks/useWindowSize';
22
+ export { default as useToggle, type Toggler, type ToggleHandlers } from './hooks/useToggle';
23
+ export { default as useArrowKeyNavigation, type ArrowKeyNavProps } from './hooks/useArrowKeyNavigation';
24
+ export { default as useIndexOfLastVisibleChild } from './hooks/useIndexOfLastVisibleChild';
25
+ export { default as useIsVisible } from './hooks/useIsVisible';
21
26
 
22
27
  // // // // // // // // // // // // // // // // // // // // // // // // // // //
23
28
  // Things that don't have types
@@ -187,11 +192,6 @@ export const Sticky: any; // from './Sticky';
187
192
  export const SelectableBox: any; // from './SelectableBox';
188
193
  export const breakpoints: any; // from './utils/breakpoints';
189
194
  export const Variant: any; // from './utils/constants';
190
- export const useWindowSize: any; // from './hooks/useWindowSize';
191
- export const useToggle: any; // from './hooks/useToggle';
192
- export const useArrowKeyNavigation: any; // from './hooks/useArrowKeyNavigation';
193
- export const useIndexOfLastVisibleChild: any; // from './hooks/useIndexOfLastVisibleChild';
194
- export const useIsVisible: any; // from './hooks/useIsVisible';
195
195
  export const
196
196
  OverflowScrollContext: any,
197
197
  OverflowScroll: any,
package/src/index.js CHANGED
@@ -18,6 +18,11 @@ export { default as ModalLayer } from './Modal/ModalLayer';
18
18
  export { default as Overlay, OverlayTrigger } from './Overlay';
19
19
  export { default as Portal } from './Modal/Portal';
20
20
  export { default as Tooltip } from './Tooltip';
21
+ export { default as useWindowSize } from './hooks/useWindowSize';
22
+ export { default as useToggle } from './hooks/useToggle';
23
+ export { default as useArrowKeyNavigation } from './hooks/useArrowKeyNavigation';
24
+ export { default as useIndexOfLastVisibleChild } from './hooks/useIndexOfLastVisibleChild';
25
+ export { default as useIsVisible } from './hooks/useIsVisible';
21
26
 
22
27
  // // // // // // // // // // // // // // // // // // // // // // // // // // //
23
28
  // Things that don't have types
@@ -159,11 +164,6 @@ export { default as Sticky } from './Sticky';
159
164
  export { default as SelectableBox } from './SelectableBox';
160
165
  export { default as breakpoints } from './utils/breakpoints';
161
166
  export { default as Variant } from './utils/constants';
162
- export { default as useWindowSize } from './hooks/useWindowSize';
163
- export { default as useToggle } from './hooks/useToggle';
164
- export { default as useArrowKeyNavigation } from './hooks/useArrowKeyNavigation';
165
- export { default as useIndexOfLastVisibleChild } from './hooks/useIndexOfLastVisibleChild';
166
- export { default as useIsVisible } from './hooks/useIsVisible';
167
167
  export {
168
168
  OverflowScrollContext,
169
169
  OverflowScroll,
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Do not edit directly, this file was auto-generated. while transforming design tokens.
3
- * See <root>/tokens/README.md for more details.
2
+ * Do not edit directly, this file was auto-generated.
3
+ * See <PARAGON_ROOT>/tokens/README.md for more details.
4
4
  */
5
5
 
6
6
  @custom-media --pgn-size-breakpoint-min-width-xs (min-width: 0px);
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Do not edit directly, this file was auto-generated. while transforming design tokens.
3
- * See <root>/tokens/README.md for more details.
2
+ * Do not edit directly, this file was auto-generated.
3
+ * See <PARAGON_ROOT>/tokens/README.md for more details.
4
4
  */
5
5
 
6
6
  :root {
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Do not edit directly, this file was auto-generated. while transforming design tokens.
3
- * See <root>/tokens/README.md for more details.
2
+ * Do not edit directly, this file was auto-generated.
3
+ * See <PARAGON_ROOT>/tokens/README.md for more details.
4
4
  */
5
5
 
6
6
  .bg-accent-a {
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Do not edit directly, this file was auto-generated. while transforming design tokens.
3
- * See <root>/tokens/README.md for more details.
2
+ * Do not edit directly, this file was auto-generated.
3
+ * See <PARAGON_ROOT>/tokens/README.md for more details.
4
4
  */
5
5
 
6
6
  :root {
@@ -410,8 +410,8 @@ const initializeStyleDictionary = async ({ themes }) => {
410
410
  StyleDictionary.registerFileHeader({
411
411
  name: 'customFileHeader',
412
412
  fileHeader: (defaultMessage) => [
413
- `${defaultMessage} while transforming design tokens.`,
414
- 'See <root>/tokens/README.md for more details.',
413
+ `${defaultMessage}`,
414
+ 'See <PARAGON_ROOT>/tokens/README.md for more details.',
415
415
  ],
416
416
  });
417
417
 
@@ -1,37 +0,0 @@
1
- import { useState, useCallback } from 'react';
2
-
3
- export default function useToggle(defaultIsOn, handlers = {}) {
4
- const { handleToggleOn, handleToggleOff, handleToggle } = handlers;
5
- const [isOn, setIsOn] = useState(defaultIsOn || false);
6
-
7
- const setOn = useCallback(() => {
8
- setIsOn(true);
9
- // istanbul ignore else
10
- if (handleToggleOn) {
11
- handleToggleOn();
12
- }
13
- // istanbul ignore else
14
- if (handleToggle) {
15
- handleToggle(true);
16
- }
17
- }, [handleToggleOn, handleToggle]);
18
-
19
- const setOff = useCallback(() => {
20
- setIsOn(false);
21
- // istanbul ignore else
22
- if (handleToggleOff) {
23
- handleToggleOff();
24
- }
25
- // istanbul ignore else
26
- if (handleToggle) {
27
- handleToggle(false);
28
- }
29
- }, [handleToggleOff, handleToggle]);
30
-
31
- const toggle = useCallback(() => {
32
- const doToggle = isOn ? setOff : setOn;
33
- doToggle();
34
- }, [isOn, setOn, setOff]);
35
-
36
- return [isOn, setOn, setOff, toggle];
37
- }