@codecademy/gamut 67.6.4 → 67.6.5-alpha.9b8a7f.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 (31) hide show
  1. package/dist/ConnectedForm/ConnectedFormGroup.d.ts +8 -2
  2. package/dist/ConnectedForm/ConnectedFormGroup.js +1 -1
  3. package/dist/ConnectedForm/ConnectedInputs/ConnectedCheckbox.js +2 -0
  4. package/dist/Form/__tests__/testUtils.d.ts +22 -0
  5. package/dist/Form/elements/FormGroupLabel.d.ts +2 -2
  6. package/dist/Form/elements/FormGroupLabel.js +10 -3
  7. package/dist/Form/inputs/Checkbox.d.ts +7 -0
  8. package/dist/Form/inputs/Checkbox.js +27 -11
  9. package/dist/Form/inputs/Radio.d.ts +9 -5
  10. package/dist/Form/inputs/Radio.js +13 -10
  11. package/dist/Form/styles/Radio-styles.d.ts +0 -3
  12. package/dist/Form/styles/Radio-styles.js +0 -6
  13. package/dist/Form/styles/shared-system-props.d.ts +7 -0
  14. package/dist/Form/styles/shared-system-props.js +11 -0
  15. package/dist/GridForm/GridFormInputGroup/GridFormRadioGroupInput/index.js +2 -0
  16. package/dist/GridForm/GridFormInputGroup/__fixtures__/renderers.d.ts +4 -0
  17. package/dist/GridForm/types.d.ts +7 -2
  18. package/dist/Tip/InfoTip/InfoTipButton.js +5 -2
  19. package/dist/Tip/InfoTip/index.d.ts +27 -2
  20. package/dist/Tip/InfoTip/index.js +50 -67
  21. package/dist/Tip/InfoTip/type-utils.d.ts +35 -0
  22. package/dist/Tip/InfoTip/type-utils.js +72 -0
  23. package/dist/Tip/__tests__/helpers.d.ts +5 -26
  24. package/dist/Tip/shared/FloatingTip.js +3 -3
  25. package/dist/Tip/shared/InlineTip.js +4 -1
  26. package/dist/Tip/shared/types.d.ts +1 -1
  27. package/dist/Tip/shared/utils.d.ts +19 -0
  28. package/dist/Tip/shared/utils.js +104 -0
  29. package/package.json +2 -2
  30. package/dist/Tip/InfoTip/elements.d.ts +0 -12
  31. package/dist/Tip/InfoTip/elements.js +0 -9
@@ -15,4 +15,108 @@ export const escapeKeyPressHandler = event => {
15
15
  export const isElementVisible = element => {
16
16
  if (!(element instanceof HTMLElement)) return false;
17
17
  return element.checkVisibility?.() ?? true;
18
+ };
19
+
20
+ /**
21
+ * Check if a floating element (modal, dialog, popover, overlay, etc.) is actually open and blocking.
22
+ *
23
+ * A floating element is considered "open" and blocking if:
24
+ * 1. It's a <dialog> element with the open attribute (always blocking per HTML spec), OR
25
+ * 2. It has role="alertdialog" (always blocking per ARIA spec), OR
26
+ * 3. It has role="dialog" AND:
27
+ * - It's not aria-hidden="true", AND
28
+ * - It doesn't have aria-expanded="false" (for collapsible dialogs), AND
29
+ * - It's actually visible (not just in DOM), AND
30
+ * - It has aria-modal="true" (indicates blocking modal per ARIA spec)
31
+ *
32
+ * Non-blocking popovers and collapsible dialogs without aria-modal="true" are not considered
33
+ * blocking and should not prevent InfoTip from closing.
34
+ *
35
+ * @param element - The DOM element to check
36
+ * @returns `true` if the floating element is actually open and blocking, `false` otherwise
37
+ */
38
+ export const isFloatingElementOpen = element => {
39
+ if (!isElementVisible(element)) return false;
40
+
41
+ /**
42
+ * Native <dialog> elements are always blocking when open.
43
+ * Per HTML spec, dialog elements with the open attribute are modal.
44
+ */
45
+ if (element instanceof HTMLDialogElement) {
46
+ return element.open === true;
47
+ }
48
+
49
+ /**
50
+ * Elements with role="dialog" or role="alertdialog".
51
+ * Per ARIA spec, role="alertdialog" is always modal (blocking).
52
+ * role="dialog" requires aria-modal="true" to be blocking.
53
+ */
54
+ const role = element.getAttribute('role');
55
+ if (role !== 'dialog' && role !== 'alertdialog') {
56
+ return false;
57
+ }
58
+
59
+ /**
60
+ * Cache attribute values to avoid multiple DOM reads.
61
+ * Check aria-hidden first as it's a common exclusion case.
62
+ */
63
+ const ariaHidden = element.getAttribute('aria-hidden');
64
+ if (ariaHidden === 'true') {
65
+ return false;
66
+ }
67
+
68
+ /**
69
+ * Check for collapsible dialogs (like help menus).
70
+ * If aria-expanded exists and is false, the dialog is closed.
71
+ * These dialogs stay in DOM but are collapsed when closed.
72
+ */
73
+ const ariaExpanded = element.getAttribute('aria-expanded');
74
+ if (ariaExpanded === 'false') {
75
+ return false;
76
+ }
77
+
78
+ /**
79
+ * Per ARIA spec, role="alertdialog" is always modal (blocking).
80
+ * At this point, we've already verified:
81
+ * - The element is visible (not hidden via CSS)
82
+ * - It's not aria-hidden="true"
83
+ * - It's not aria-expanded="false"
84
+ * So if it's an alertdialog, it's open and blocking.
85
+ * Handle alertdialog here to avoid expensive DOM queries for dialog elements.
86
+ */
87
+ if (role === 'alertdialog') {
88
+ return true;
89
+ }
90
+
91
+ /**
92
+ * For role="dialog", check if any child button with aria-expanded indicates the dialog is closed.
93
+ * Some dialogs use a toggle button pattern where the button's aria-expanded
94
+ * reflects the dialog's state.
95
+ * Only perform this expensive querySelector operation after all other checks pass.
96
+ */
97
+ const toggleButton = element.querySelector('button[aria-expanded], [role="button"][aria-expanded]');
98
+ if (toggleButton && toggleButton.getAttribute('aria-expanded') === 'false') {
99
+ return false;
100
+ }
101
+
102
+ /**
103
+ * For role="dialog", check aria-modal attribute.
104
+ * aria-modal="true" indicates a blocking modal (Modal, Dialog).
105
+ * aria-modal="false" or absence indicates non-blocking.
106
+ */
107
+ const ariaModal = element.getAttribute('aria-modal');
108
+ if (ariaModal === 'true') {
109
+ return true;
110
+ }
111
+ if (ariaModal === 'false') {
112
+ // Explicitly non-modal, should not block
113
+ return false;
114
+ }
115
+
116
+ /**
117
+ * Elements with role="dialog" but without aria-modal="true" are non-blocking.
118
+ * These include collapsible dialogs (help menus) and popovers (Popover, InfoTip, Tooltip)
119
+ * that use role="dialog". They should not prevent InfoTip from closing.
120
+ */
121
+ return false;
18
122
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codecademy/gamut",
3
3
  "description": "Styleguide & Component library for Codecademy",
4
- "version": "67.6.4",
4
+ "version": "67.6.5-alpha.9b8a7f.0",
5
5
  "author": "Codecademy Engineering <dev@codecademy.com>",
6
6
  "dependencies": {
7
7
  "@codecademy/gamut-icons": "9.54.1",
@@ -56,5 +56,5 @@
56
56
  "dist/**/[A-Z]**/[A-Z]*.js",
57
57
  "dist/**/[A-Z]**/index.js"
58
58
  ],
59
- "gitHead": "38f5368f7245ab5a33464954c842405231edcd16"
59
+ "gitHead": "4d6cef4180d4e78cbb90cdcc4bc33aad124f9b93"
60
60
  }
@@ -1,12 +0,0 @@
1
- /// <reference types="react" />
2
- export declare const ScreenreaderNavigableText: import("@emotion/styled").StyledComponent<(((Omit<{
3
- theme?: import("@emotion/react").Theme | undefined;
4
- as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
5
- } & import("../..").TextTruncateProps & Pick<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "slot" | "style" | "title" | "dir" | "children" | "className" | "aria-hidden" | "onAnimationStart" | "onDragStart" | "onDragEnd" | "onDrag" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "autoFocus" | "contentEditable" | "contextMenu" | "draggable" | "hidden" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "content" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onResize" | "onResizeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDragCapture" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | keyof import("react").ClassAttributes<HTMLSpanElement>>, "ref"> | Omit<{
6
- theme?: import("@emotion/react").Theme | undefined;
7
- as?: import("react").ElementType<any, keyof import("react").JSX.IntrinsicElements> | undefined;
8
- } & import("../..").TextNoTruncateProps & Pick<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "slot" | "style" | "title" | "dir" | "children" | "className" | "aria-hidden" | "onAnimationStart" | "onDragStart" | "onDragEnd" | "onDrag" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "autoFocus" | "contentEditable" | "contextMenu" | "draggable" | "hidden" | "id" | "lang" | "nonce" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "content" | "datatype" | "inlist" | "prefix" | "property" | "rel" | "resource" | "rev" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-braillelabel" | "aria-brailleroledescription" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colindextext" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-description" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowindextext" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onResize" | "onResizeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDragCapture" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerLeave" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | keyof import("react").ClassAttributes<HTMLSpanElement>>, "ref">) & import("react").RefAttributes<HTMLSpanElement>) & {
9
- theme?: import("@emotion/react").Theme | undefined;
10
- }) & {
11
- theme?: import("@emotion/react").Theme | undefined;
12
- }, {}, {}>;
@@ -1,9 +0,0 @@
1
- import _styled from "@emotion/styled/base";
2
- import { css } from '@codecademy/gamut-styles';
3
- import { Text } from '../../Typography';
4
- export const ScreenreaderNavigableText = /*#__PURE__*/_styled(Text, {
5
- target: "e1rvjfdo0",
6
- label: "ScreenreaderNavigableText"
7
- })(css({
8
- position: 'absolute'
9
- }), process.env.NODE_ENV === "production" ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9UaXAvSW5mb1RpcC9lbGVtZW50cy50c3giXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBS3lDIiwiZmlsZSI6Ii4uLy4uLy4uL3NyYy9UaXAvSW5mb1RpcC9lbGVtZW50cy50c3giLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjc3MgfSBmcm9tICdAY29kZWNhZGVteS9nYW11dC1zdHlsZXMnO1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnO1xuXG5pbXBvcnQgeyBUZXh0IH0gZnJvbSAnLi4vLi4vVHlwb2dyYXBoeSc7XG5cbmV4cG9ydCBjb25zdCBTY3JlZW5yZWFkZXJOYXZpZ2FibGVUZXh0ID0gc3R5bGVkKFRleHQpKFxuICBjc3MoeyBwb3NpdGlvbjogJ2Fic29sdXRlJyB9KVxuKTtcbiJdfQ== */");