@lumx/react 2.2.20-alpha.xss.datatable → 2.2.21

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 (81) hide show
  1. package/esm/_internal/ClickAwayProvider.js +90 -12
  2. package/esm/_internal/ClickAwayProvider.js.map +1 -1
  3. package/esm/_internal/DatePickerField.js +18 -11
  4. package/esm/_internal/DatePickerField.js.map +1 -1
  5. package/esm/_internal/Dialog2.js +2 -2
  6. package/esm/_internal/Dialog2.js.map +1 -1
  7. package/esm/_internal/GenericBlock.js +90 -0
  8. package/esm/_internal/GenericBlock.js.map +1 -0
  9. package/esm/_internal/Lightbox2.js +2 -2
  10. package/esm/_internal/Lightbox2.js.map +1 -1
  11. package/esm/_internal/LinkPreview.js +22 -12
  12. package/esm/_internal/LinkPreview.js.map +1 -1
  13. package/esm/_internal/Popover2.js +21 -8
  14. package/esm/_internal/Popover2.js.map +1 -1
  15. package/esm/_internal/SelectMultiple.js +16 -4
  16. package/esm/_internal/SelectMultiple.js.map +1 -1
  17. package/esm/_internal/alert-dialog.js +2 -2
  18. package/esm/_internal/autocomplete.js +2 -1
  19. package/esm/_internal/autocomplete.js.map +1 -1
  20. package/esm/_internal/button.js +2 -1
  21. package/esm/_internal/button.js.map +1 -1
  22. package/esm/_internal/comment-block.js +2 -1
  23. package/esm/_internal/comment-block.js.map +1 -1
  24. package/esm/_internal/date-picker.js +3 -2
  25. package/esm/_internal/date-picker.js.map +1 -1
  26. package/esm/_internal/dialog.js +2 -2
  27. package/esm/_internal/dropdown.js +2 -1
  28. package/esm/_internal/dropdown.js.map +1 -1
  29. package/esm/_internal/expansion-panel.js +1 -1
  30. package/esm/_internal/generic-block.js +12 -0
  31. package/esm/_internal/generic-block.js.map +1 -0
  32. package/esm/_internal/lightbox.js +3 -2
  33. package/esm/_internal/lightbox.js.map +1 -1
  34. package/esm/_internal/popover.js +2 -1
  35. package/esm/_internal/popover.js.map +1 -1
  36. package/esm/_internal/select.js +2 -1
  37. package/esm/_internal/select.js.map +1 -1
  38. package/esm/_internal/side-navigation.js +2 -1
  39. package/esm/_internal/side-navigation.js.map +1 -1
  40. package/esm/_internal/slideshow.js +2 -1
  41. package/esm/_internal/slideshow.js.map +1 -1
  42. package/esm/_internal/text-field.js +2 -1
  43. package/esm/_internal/text-field.js.map +1 -1
  44. package/esm/_internal/tooltip.js +2 -1
  45. package/esm/_internal/tooltip.js.map +1 -1
  46. package/esm/_internal/type.js.map +1 -1
  47. package/esm/_internal/useFocusTrap.js +62 -78
  48. package/esm/_internal/useFocusTrap.js.map +1 -1
  49. package/esm/index.js +3 -2
  50. package/esm/index.js.map +1 -1
  51. package/package.json +5 -5
  52. package/src/components/date-picker/DatePickerField.tsx +15 -16
  53. package/src/components/date-picker/types.ts +2 -2
  54. package/src/components/dialog/Dialog.stories.tsx +57 -13
  55. package/src/components/dialog/Dialog.tsx +1 -1
  56. package/src/components/dialog/__snapshots__/Dialog.test.tsx.snap +82 -14
  57. package/src/components/generic-block/GenericBlock.stories.tsx +149 -0
  58. package/src/components/generic-block/GenericBlock.test.tsx +28 -0
  59. package/src/components/generic-block/GenericBlock.tsx +120 -0
  60. package/src/components/generic-block/__snapshots__/GenericBlock.test.tsx.snap +92 -0
  61. package/src/components/generic-block/index.ts +1 -0
  62. package/src/components/lightbox/Lightbox.tsx +1 -1
  63. package/src/components/link-preview/LinkPreview.test.tsx +50 -55
  64. package/src/components/link-preview/LinkPreview.tsx +43 -16
  65. package/src/components/popover/Popover.tsx +20 -4
  66. package/src/components/select/Select.stories.tsx +2 -0
  67. package/src/components/select/Select.tsx +11 -1
  68. package/src/components/select/SelectMultiple.stories.tsx +2 -0
  69. package/src/components/select/SelectMultiple.tsx +11 -1
  70. package/src/components/select/constants.ts +2 -0
  71. package/src/components/table/__snapshots__/Table.test.tsx.snap +5 -0
  72. package/src/hooks/useCallbackOnEscape.ts +21 -13
  73. package/src/hooks/useFocusTrap.ts +68 -51
  74. package/src/index.ts +1 -0
  75. package/src/stories/generated/GenericBlock/Demos.stories.tsx +6 -0
  76. package/src/utils/focus/getFirstAndLastFocusable.test.ts +6 -0
  77. package/src/utils/focus/getFirstAndLastFocusable.ts +2 -2
  78. package/src/utils/makeListenerTowerContext.ts +32 -0
  79. package/src/utils/type.ts +3 -0
  80. package/types.d.ts +56 -6
  81. package/src/components/link-preview/__snapshots__/LinkPreview.test.tsx.snap +0 -51
@@ -1,101 +1,85 @@
1
1
  import { useEffect } from 'react';
2
2
  import { D as DOCUMENT } from './constants.js';
3
+ import { g as getFirstAndLastFocusable, m as makeListenerTowerContext } from './ClickAwayProvider.js';
3
4
 
4
- /** CSS selector listing all tabbable elements. */
5
- var TABBABLE_ELEMENTS_SELECTOR = "a[href], button, textarea, input:not([type=\"hidden\"]), [tabindex]";
6
- /** CSS selector matching element that are disabled (should not receive focus). */
7
-
8
- var DISABLED_SELECTOR = "[tabindex=\"-1\"], [disabled]:not([disabled=\"false\"]), [aria-disabled]:not([aria-disabled=\"false\"])";
9
-
10
- var isNotDisabled = function isNotDisabled(element) {
11
- return !element.matches(DISABLED_SELECTOR);
12
- };
5
+ var FOCUS_TRAPS = makeListenerTowerContext();
13
6
  /**
14
- * Get first and last elements focusable in an element.
7
+ * Trap 'Tab' focus switch inside the `focusZoneElement`.
8
+ *
9
+ * If multiple focus trap are activated, only the last one is maintained and when a focus trap closes, the previous one
10
+ * gets activated again.
15
11
  *
16
- * @param parentElement The element in which to search focusable elements.
17
- * @return first and last focusable elements
12
+ * @param focusZoneElement The element in which to trap the focus.
13
+ * @param focusElement The element to focus when the focus trap is activated (otherwise the first focusable element
14
+ * will be focused).
18
15
  */
19
16
 
17
+ function useFocusTrap(focusZoneElement, focusElement) {
18
+ useEffect(function () {
19
+ // Body element can be undefined in SSR context.
20
+ var rootElement = DOCUMENT === null || DOCUMENT === void 0 ? void 0 : DOCUMENT.body;
20
21
 
21
- function getFirstAndLastFocusable(parentElement) {
22
- var focusableElements = Array.from(parentElement.querySelectorAll(TABBABLE_ELEMENTS_SELECTOR)); // First non disabled element.
22
+ if (!rootElement || !focusZoneElement) {
23
+ return undefined;
24
+ } // Trap 'Tab' key down focus switch into the focus zone.
23
25
 
24
- var first = focusableElements.find(isNotDisabled); // Last non disabled element.
25
26
 
26
- var last = focusableElements.reverse().find(isNotDisabled);
27
+ var trapTabFocusInFocusZone = function trapTabFocusInFocusZone(evt) {
28
+ var key = evt.key;
27
29
 
28
- if (last && first) {
29
- return {
30
- first: first,
31
- last: last
32
- };
33
- }
30
+ if (key !== 'Tab') {
31
+ return;
32
+ }
34
33
 
35
- return {};
36
- }
34
+ var focusable = getFirstAndLastFocusable(focusZoneElement); // Prevent focus switch if no focusable available.
37
35
 
38
- /**
39
- * Add a key down event handler to the given root element (document.body by default) to trap the move of focus
40
- * (TAB and SHIFT-TAB keys) inside the given focusZoneElement.
41
- * Will focus the given focus element when activating the focus trap.
42
- *
43
- * @param focusZoneElement The element in which to trap the focus.
44
- * @param focusElement The element to focus when the focus trap is activated.
45
- * @param rootElement The element on which the key down event will be placed.
46
- */
36
+ if (!focusable.first) {
37
+ evt.preventDefault();
38
+ return;
39
+ }
47
40
 
48
- function useFocusTrap(focusZoneElement, focusElement) {
49
- var rootElement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DOCUMENT === null || DOCUMENT === void 0 ? void 0 : DOCUMENT.body;
50
- useEffect(function () {
51
- if (rootElement && focusZoneElement) {
52
- var _ref;
41
+ if ( // No previous focus
42
+ !document.activeElement || // Previous focus is at the end of the focus zone.
43
+ !evt.shiftKey && document.activeElement === focusable.last || // Previous focus is outside the focus zone
44
+ !focusZoneElement.contains(document.activeElement)) {
45
+ focusable.first.focus();
46
+ evt.preventDefault();
47
+ return;
48
+ }
53
49
 
54
- (_ref = document.activeElement) === null || _ref === void 0 ? void 0 : _ref.blur();
50
+ if ( // Focus order reversed
51
+ evt.shiftKey && // Previous focus is at the start of the focus zone.
52
+ document.activeElement === focusable.first) {
53
+ focusable.last.focus();
54
+ evt.preventDefault();
55
+ }
56
+ };
55
57
 
56
- if (focusElement) {
57
- focusElement.focus();
58
+ var focusTrap = {
59
+ enable: function enable() {
60
+ return rootElement.addEventListener('keydown', trapTabFocusInFocusZone);
61
+ },
62
+ disable: function disable() {
63
+ return rootElement.removeEventListener('keydown', trapTabFocusInFocusZone);
58
64
  }
65
+ }; // SETUP:
66
+
67
+ if (focusElement && focusZoneElement.contains(focusElement)) {
68
+ // Focus the given element.
69
+ focusElement.focus();
70
+ } else {
71
+ var _getFirstAndLastFocus;
59
72
 
60
- var onKeyDown = function onKeyDown(evt) {
61
- var key = evt.key;
62
-
63
- if (key !== 'Tab') {
64
- return;
65
- }
66
-
67
- var focusable = getFirstAndLastFocusable(focusZoneElement); // Prevent focus switch if no focusable available.
68
-
69
- if (!focusable.first) {
70
- evt.preventDefault();
71
- return;
72
- }
73
-
74
- if ( // No previous focus
75
- !document.activeElement || // Previous focus is at the end of the focus zone.
76
- !evt.shiftKey && document.activeElement === focusable.last || // Previous focus is outside the focus zone
77
- !focusZoneElement.contains(document.activeElement)) {
78
- focusable.first.focus();
79
- evt.preventDefault();
80
- return;
81
- }
82
-
83
- if ( // Focus order reversed
84
- evt.shiftKey && // Previous focus is at the start of the focus zone.
85
- document.activeElement === focusable.first) {
86
- focusable.last.focus();
87
- evt.preventDefault();
88
- }
89
- };
90
-
91
- rootElement.addEventListener('keydown', onKeyDown);
92
- return function () {
93
- return rootElement.removeEventListener('keydown', onKeyDown);
94
- };
73
+ // Focus the first focusable element in the zone.
74
+ (_getFirstAndLastFocus = getFirstAndLastFocusable(focusZoneElement).first) === null || _getFirstAndLastFocus === void 0 ? void 0 : _getFirstAndLastFocus.focus();
95
75
  }
96
76
 
97
- return undefined;
98
- }, [focusElement, focusZoneElement, rootElement]);
77
+ FOCUS_TRAPS.register(focusTrap); // TEARDOWN:
78
+
79
+ return function () {
80
+ return FOCUS_TRAPS.unregister(focusTrap);
81
+ };
82
+ }, [focusElement, focusZoneElement]);
99
83
  }
100
84
 
101
85
  export { useFocusTrap as u };
@@ -1 +1 @@
1
- {"version":3,"file":"useFocusTrap.js","sources":["../../../src/utils/focus/getFirstAndLastFocusable.ts","../../../src/hooks/useFocusTrap.ts"],"sourcesContent":["/** CSS selector listing all tabbable elements. */\nconst TABBABLE_ELEMENTS_SELECTOR = `a[href], button, textarea, input:not([type=\"hidden\"]), [tabindex]`;\n\n/** CSS selector matching element that are disabled (should not receive focus). */\nconst DISABLED_SELECTOR = `[tabindex=\"-1\"], [disabled]:not([disabled=\"false\"]), [aria-disabled]:not([aria-disabled=\"false\"])`;\n\nconst isNotDisabled = (element: HTMLElement) => !element.matches(DISABLED_SELECTOR);\n\n/**\n * Get first and last elements focusable in an element.\n *\n * @param parentElement The element in which to search focusable elements.\n * @return first and last focusable elements\n */\nexport function getFirstAndLastFocusable(parentElement: HTMLElement) {\n const focusableElements = Array.from(parentElement.querySelectorAll<HTMLElement>(TABBABLE_ELEMENTS_SELECTOR));\n\n // First non disabled element.\n const first = focusableElements.find(isNotDisabled);\n // Last non disabled element.\n const last = focusableElements.reverse().find(isNotDisabled);\n\n if (last && first) {\n return { first, last };\n }\n return {};\n}\n","import { useEffect } from 'react';\n\nimport { DOCUMENT } from '@lumx/react/constants';\nimport { getFirstAndLastFocusable } from '@lumx/react/utils/focus/getFirstAndLastFocusable';\n\n/**\n * Add a key down event handler to the given root element (document.body by default) to trap the move of focus\n * (TAB and SHIFT-TAB keys) inside the given focusZoneElement.\n * Will focus the given focus element when activating the focus trap.\n *\n * @param focusZoneElement The element in which to trap the focus.\n * @param focusElement The element to focus when the focus trap is activated.\n * @param rootElement The element on which the key down event will be placed.\n */\nexport function useFocusTrap(\n focusZoneElement: HTMLElement | null,\n focusElement?: HTMLElement | null,\n rootElement = DOCUMENT?.body,\n): void {\n useEffect(() => {\n if (rootElement && focusZoneElement) {\n (document.activeElement as HTMLElement)?.blur();\n if (focusElement) {\n focusElement.focus();\n }\n\n const onKeyDown = (evt: KeyboardEvent) => {\n const { key } = evt;\n if (key !== 'Tab') {\n return;\n }\n const focusable = getFirstAndLastFocusable(focusZoneElement);\n\n // Prevent focus switch if no focusable available.\n if (!focusable.first) {\n evt.preventDefault();\n return;\n }\n\n if (\n // No previous focus\n !document.activeElement ||\n // Previous focus is at the end of the focus zone.\n (!evt.shiftKey && document.activeElement === focusable.last) ||\n // Previous focus is outside the focus zone\n !focusZoneElement.contains(document.activeElement)\n ) {\n focusable.first.focus();\n evt.preventDefault();\n return;\n }\n\n if (\n // Focus order reversed\n evt.shiftKey &&\n // Previous focus is at the start of the focus zone.\n document.activeElement === focusable.first\n ) {\n focusable.last.focus();\n evt.preventDefault();\n }\n };\n rootElement.addEventListener('keydown', onKeyDown);\n return () => rootElement.removeEventListener('keydown', onKeyDown);\n }\n return undefined;\n }, [focusElement, focusZoneElement, rootElement]);\n}\n"],"names":["TABBABLE_ELEMENTS_SELECTOR","DISABLED_SELECTOR","isNotDisabled","element","matches","getFirstAndLastFocusable","parentElement","focusableElements","Array","from","querySelectorAll","first","find","last","reverse","useFocusTrap","focusZoneElement","focusElement","rootElement","DOCUMENT","body","useEffect","document","activeElement","blur","focus","onKeyDown","evt","key","focusable","preventDefault","shiftKey","contains","addEventListener","removeEventListener","undefined"],"mappings":";;;AAAA;AACA,IAAMA,0BAA0B,wEAAhC;AAEA;;AACA,IAAMC,iBAAiB,4GAAvB;;AAEA,IAAMC,aAAa,GAAG,SAAhBA,aAAgB,CAACC,OAAD;AAAA,SAA0B,CAACA,OAAO,CAACC,OAAR,CAAgBH,iBAAhB,CAA3B;AAAA,CAAtB;AAEA;;;;;;;;AAMO,SAASI,wBAAT,CAAkCC,aAAlC,EAA8D;AACjE,MAAMC,iBAAiB,GAAGC,KAAK,CAACC,IAAN,CAAWH,aAAa,CAACI,gBAAd,CAA4CV,0BAA5C,CAAX,CAA1B,CADiE;;AAIjE,MAAMW,KAAK,GAAGJ,iBAAiB,CAACK,IAAlB,CAAuBV,aAAvB,CAAd,CAJiE;;AAMjE,MAAMW,IAAI,GAAGN,iBAAiB,CAACO,OAAlB,GAA4BF,IAA5B,CAAiCV,aAAjC,CAAb;;AAEA,MAAIW,IAAI,IAAIF,KAAZ,EAAmB;AACf,WAAO;AAAEA,MAAAA,KAAK,EAALA,KAAF;AAASE,MAAAA,IAAI,EAAJA;AAAT,KAAP;AACH;;AACD,SAAO,EAAP;AACH;;ACrBD;;;;;;;;;;AASO,SAASE,YAAT,CACHC,gBADG,EAEHC,YAFG,EAIC;AAAA,MADJC,WACI,uEADUC,QACV,aADUA,QACV,uBADUA,QAAQ,CAAEC,IACpB;AACJC,EAAAA,SAAS,CAAC,YAAM;AACZ,QAAIH,WAAW,IAAIF,gBAAnB,EAAqC;AAAA;;AACjC,cAACM,QAAQ,CAACC,aAAV,8CAAyCC,IAAzC;;AACA,UAAIP,YAAJ,EAAkB;AACdA,QAAAA,YAAY,CAACQ,KAAb;AACH;;AAED,UAAMC,SAAS,GAAG,SAAZA,SAAY,CAACC,GAAD,EAAwB;AAAA,YAC9BC,GAD8B,GACtBD,GADsB,CAC9BC,GAD8B;;AAEtC,YAAIA,GAAG,KAAK,KAAZ,EAAmB;AACf;AACH;;AACD,YAAMC,SAAS,GAAGxB,wBAAwB,CAACW,gBAAD,CAA1C,CALsC;;AAQtC,YAAI,CAACa,SAAS,CAAClB,KAAf,EAAsB;AAClBgB,UAAAA,GAAG,CAACG,cAAJ;AACA;AACH;;AAED;AAEI,SAACR,QAAQ,CAACC,aAAV;AAEC,SAACI,GAAG,CAACI,QAAL,IAAiBT,QAAQ,CAACC,aAAT,KAA2BM,SAAS,CAAChB,IAFvD;AAIA,SAACG,gBAAgB,CAACgB,QAAjB,CAA0BV,QAAQ,CAACC,aAAnC,CANL,EAOE;AACEM,UAAAA,SAAS,CAAClB,KAAV,CAAgBc,KAAhB;AACAE,UAAAA,GAAG,CAACG,cAAJ;AACA;AACH;;AAED;AAEIH,QAAAA,GAAG,CAACI,QAAJ;AAEAT,QAAAA,QAAQ,CAACC,aAAT,KAA2BM,SAAS,CAAClB,KAJzC,EAKE;AACEkB,UAAAA,SAAS,CAAChB,IAAV,CAAeY,KAAf;AACAE,UAAAA,GAAG,CAACG,cAAJ;AACH;AACJ,OAnCD;;AAoCAZ,MAAAA,WAAW,CAACe,gBAAZ,CAA6B,SAA7B,EAAwCP,SAAxC;AACA,aAAO;AAAA,eAAMR,WAAW,CAACgB,mBAAZ,CAAgC,SAAhC,EAA2CR,SAA3C,CAAN;AAAA,OAAP;AACH;;AACD,WAAOS,SAAP;AACH,GA/CQ,EA+CN,CAAClB,YAAD,EAAeD,gBAAf,EAAiCE,WAAjC,CA/CM,CAAT;AAgDH;;;;"}
1
+ {"version":3,"file":"useFocusTrap.js","sources":["../../../src/hooks/useFocusTrap.ts"],"sourcesContent":["import { useEffect } from 'react';\n\nimport { DOCUMENT } from '@lumx/react/constants';\nimport { getFirstAndLastFocusable } from '@lumx/react/utils/focus/getFirstAndLastFocusable';\nimport { Falsy } from '@lumx/react/utils';\nimport { Listener, makeListenerTowerContext } from '@lumx/react/utils/makeListenerTowerContext';\n\nconst FOCUS_TRAPS = makeListenerTowerContext();\n\n/**\n * Trap 'Tab' focus switch inside the `focusZoneElement`.\n *\n * If multiple focus trap are activated, only the last one is maintained and when a focus trap closes, the previous one\n * gets activated again.\n *\n * @param focusZoneElement The element in which to trap the focus.\n * @param focusElement The element to focus when the focus trap is activated (otherwise the first focusable element\n * will be focused).\n */\nexport function useFocusTrap(focusZoneElement: HTMLElement | Falsy, focusElement?: HTMLElement | null): void {\n useEffect(() => {\n // Body element can be undefined in SSR context.\n const rootElement = DOCUMENT?.body;\n\n if (!rootElement || !focusZoneElement) {\n return undefined;\n }\n\n // Trap 'Tab' key down focus switch into the focus zone.\n const trapTabFocusInFocusZone = (evt: KeyboardEvent) => {\n const { key } = evt;\n if (key !== 'Tab') {\n return;\n }\n const focusable = getFirstAndLastFocusable(focusZoneElement);\n\n // Prevent focus switch if no focusable available.\n if (!focusable.first) {\n evt.preventDefault();\n return;\n }\n\n if (\n // No previous focus\n !document.activeElement ||\n // Previous focus is at the end of the focus zone.\n (!evt.shiftKey && document.activeElement === focusable.last) ||\n // Previous focus is outside the focus zone\n !focusZoneElement.contains(document.activeElement)\n ) {\n focusable.first.focus();\n evt.preventDefault();\n return;\n }\n\n if (\n // Focus order reversed\n evt.shiftKey &&\n // Previous focus is at the start of the focus zone.\n document.activeElement === focusable.first\n ) {\n focusable.last.focus();\n evt.preventDefault();\n }\n };\n\n const focusTrap: Listener = {\n enable: () => rootElement.addEventListener('keydown', trapTabFocusInFocusZone),\n disable: () => rootElement.removeEventListener('keydown', trapTabFocusInFocusZone),\n };\n\n // SETUP:\n if (focusElement && focusZoneElement.contains(focusElement)) {\n // Focus the given element.\n focusElement.focus();\n } else {\n // Focus the first focusable element in the zone.\n getFirstAndLastFocusable(focusZoneElement).first?.focus();\n }\n FOCUS_TRAPS.register(focusTrap);\n\n // TEARDOWN:\n return () => FOCUS_TRAPS.unregister(focusTrap);\n }, [focusElement, focusZoneElement]);\n}\n"],"names":["FOCUS_TRAPS","makeListenerTowerContext","useFocusTrap","focusZoneElement","focusElement","useEffect","rootElement","DOCUMENT","body","undefined","trapTabFocusInFocusZone","evt","key","focusable","getFirstAndLastFocusable","first","preventDefault","document","activeElement","shiftKey","last","contains","focus","focusTrap","enable","addEventListener","disable","removeEventListener","register","unregister"],"mappings":";;;;AAOA,IAAMA,WAAW,GAAGC,wBAAwB,EAA5C;AAEA;;;;;;;;;;;AAUO,SAASC,YAAT,CAAsBC,gBAAtB,EAA6DC,YAA7D,EAAsG;AACzGC,EAAAA,SAAS,CAAC,YAAM;AACZ;AACA,QAAMC,WAAW,GAAGC,QAAH,aAAGA,QAAH,uBAAGA,QAAQ,CAAEC,IAA9B;;AAEA,QAAI,CAACF,WAAD,IAAgB,CAACH,gBAArB,EAAuC;AACnC,aAAOM,SAAP;AACH,KANW;;;AASZ,QAAMC,uBAAuB,GAAG,SAA1BA,uBAA0B,CAACC,GAAD,EAAwB;AAAA,UAC5CC,GAD4C,GACpCD,GADoC,CAC5CC,GAD4C;;AAEpD,UAAIA,GAAG,KAAK,KAAZ,EAAmB;AACf;AACH;;AACD,UAAMC,SAAS,GAAGC,wBAAwB,CAACX,gBAAD,CAA1C,CALoD;;AAQpD,UAAI,CAACU,SAAS,CAACE,KAAf,EAAsB;AAClBJ,QAAAA,GAAG,CAACK,cAAJ;AACA;AACH;;AAED;AAEI,OAACC,QAAQ,CAACC,aAAV;AAEC,OAACP,GAAG,CAACQ,QAAL,IAAiBF,QAAQ,CAACC,aAAT,KAA2BL,SAAS,CAACO,IAFvD;AAIA,OAACjB,gBAAgB,CAACkB,QAAjB,CAA0BJ,QAAQ,CAACC,aAAnC,CANL,EAOE;AACEL,QAAAA,SAAS,CAACE,KAAV,CAAgBO,KAAhB;AACAX,QAAAA,GAAG,CAACK,cAAJ;AACA;AACH;;AAED;AAEIL,MAAAA,GAAG,CAACQ,QAAJ;AAEAF,MAAAA,QAAQ,CAACC,aAAT,KAA2BL,SAAS,CAACE,KAJzC,EAKE;AACEF,QAAAA,SAAS,CAACO,IAAV,CAAeE,KAAf;AACAX,QAAAA,GAAG,CAACK,cAAJ;AACH;AACJ,KAnCD;;AAqCA,QAAMO,SAAmB,GAAG;AACxBC,MAAAA,MAAM,EAAE;AAAA,eAAMlB,WAAW,CAACmB,gBAAZ,CAA6B,SAA7B,EAAwCf,uBAAxC,CAAN;AAAA,OADgB;AAExBgB,MAAAA,OAAO,EAAE;AAAA,eAAMpB,WAAW,CAACqB,mBAAZ,CAAgC,SAAhC,EAA2CjB,uBAA3C,CAAN;AAAA;AAFe,KAA5B,CA9CY;;AAoDZ,QAAIN,YAAY,IAAID,gBAAgB,CAACkB,QAAjB,CAA0BjB,YAA1B,CAApB,EAA6D;AACzD;AACAA,MAAAA,YAAY,CAACkB,KAAb;AACH,KAHD,MAGO;AAAA;;AACH;AACA,+BAAAR,wBAAwB,CAACX,gBAAD,CAAxB,CAA2CY,KAA3C,gFAAkDO,KAAlD;AACH;;AACDtB,IAAAA,WAAW,CAAC4B,QAAZ,CAAqBL,SAArB,EA3DY;;AA8DZ,WAAO;AAAA,aAAMvB,WAAW,CAAC6B,UAAZ,CAAuBN,SAAvB,CAAN;AAAA,KAAP;AACH,GA/DQ,EA+DN,CAACnB,YAAD,EAAeD,gBAAf,CA/DM,CAAT;AAgEH;;;;"}
package/esm/index.js CHANGED
@@ -37,11 +37,11 @@ import 'moment';
37
37
  export { D as DatePicker, a as DatePickerControlled, b as DatePickerField } from './_internal/DatePickerField.js';
38
38
  import 'lodash/range';
39
39
  import 'moment-range';
40
+ import './_internal/ClickAwayProvider.js';
41
+ import 'lodash/pull';
40
42
  import './_internal/useFocusTrap.js';
41
43
  import 'react-dom';
42
- import './_internal/ClickAwayProvider.js';
43
44
  export { D as Dialog } from './_internal/Dialog2.js';
44
- import 'lodash/pull';
45
45
  import './_internal/useDelayedVisibility.js';
46
46
  import './_internal/useDisableBodyScroll.js';
47
47
  export { D as DEFAULT_PROPS, a as Divider } from './_internal/Divider2.js';
@@ -52,6 +52,7 @@ export { E as ExpansionPanel } from './_internal/ExpansionPanel.js';
52
52
  export { F as Flag } from './_internal/Flag2.js';
53
53
  import 'lodash/castArray';
54
54
  export { F as FlexBox } from './_internal/FlexBox.js';
55
+ export { G as GenericBlock } from './_internal/GenericBlock.js';
55
56
  export { G as Grid, a as GridItem } from './_internal/GridItem.js';
56
57
  import 'lodash/isObject';
57
58
  export { a as ImageBlock, I as ImageBlockCaptionPosition } from './_internal/ImageBlock.js';
package/esm/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -7,8 +7,8 @@
7
7
  },
8
8
  "dependencies": {
9
9
  "@juggle/resize-observer": "^3.2.0",
10
- "@lumx/core": "^2.2.20-alpha.xss.datatable",
11
- "@lumx/icons": "^2.2.20-alpha.xss.datatable",
10
+ "@lumx/core": "^2.2.21",
11
+ "@lumx/icons": "^2.2.21",
12
12
  "@popperjs/core": "^2.5.4",
13
13
  "body-scroll-lock": "^3.1.5",
14
14
  "classnames": "^2.2.6",
@@ -111,7 +111,7 @@
111
111
  },
112
112
  "scripts": {
113
113
  "build": "rollup -c && yarn generate:types",
114
- "generate:types": "dts-bundle-generator --no-check --external-types=react --external-imports=moment -o dist/types.d.ts src/index.ts",
114
+ "generate:types": "dts-bundle-generator --no-check --external-imports=react -o dist/types.d.ts src/index.ts",
115
115
  "prepare": "install-peers || exit 0",
116
116
  "prepublishOnly": "yarn build",
117
117
  "test": "jest --config jest/index.js --coverage --notify --passWithNoTests --detectOpenHandles --runInBand",
@@ -120,6 +120,6 @@
120
120
  "build:storybook": "cd storybook && ./build"
121
121
  },
122
122
  "sideEffects": false,
123
- "version": "2.2.20-alpha.xss.datatable",
124
- "gitHead": "f3e5a90ee87b5f78417bff7015fa43852205a69d"
123
+ "version": "2.2.21",
124
+ "gitHead": "5d516f8032b5e8beedc1615c25b5dc8ee511dfc7"
125
125
  }
@@ -62,7 +62,7 @@ export const DatePickerField: Comp<DatePickerFieldProps, HTMLDivElement> = forwa
62
62
  value,
63
63
  ...forwardedProps
64
64
  } = props;
65
- const wrapperRef = useRef(null);
65
+ const [wrapperElement, setWrapperElement] = useState<HTMLDivElement | null>(null);
66
66
  const anchorRef = useRef(null);
67
67
 
68
68
  const [isOpen, setIsOpen] = useState(false);
@@ -83,8 +83,8 @@ export const DatePickerField: Comp<DatePickerFieldProps, HTMLDivElement> = forwa
83
83
  };
84
84
 
85
85
  // Handle focus trap.
86
- const todayOrSelectedDateRef = useRef<HTMLButtonElement>(null);
87
- useFocusTrap(todayOrSelectedDateRef.current && wrapperRef.current, todayOrSelectedDateRef.current);
86
+ const [todayOrSelectedDate, setTodayOrSelectedDate] = useState<HTMLButtonElement | null>(null);
87
+ useFocusTrap(isOpen && wrapperElement, todayOrSelectedDate);
88
88
 
89
89
  const onTextFieldChange = (textFieldValue: string, textFieldName?: string, event?: SyntheticEvent) => {
90
90
  if (!textFieldValue) {
@@ -121,19 +121,18 @@ export const DatePickerField: Comp<DatePickerFieldProps, HTMLDivElement> = forwa
121
121
  closeOnClickAway
122
122
  closeOnEscape
123
123
  >
124
- <div ref={wrapperRef}>
125
- <DatePicker
126
- locale={locale}
127
- maxDate={maxDate}
128
- minDate={minDate}
129
- value={value}
130
- onChange={onDatePickerChange}
131
- todayOrSelectedDateRef={todayOrSelectedDateRef}
132
- defaultMonth={defaultMonth}
133
- nextButtonProps={nextButtonProps}
134
- previousButtonProps={previousButtonProps}
135
- />
136
- </div>
124
+ <DatePicker
125
+ ref={setWrapperElement}
126
+ locale={locale}
127
+ maxDate={maxDate}
128
+ minDate={minDate}
129
+ value={value}
130
+ onChange={onDatePickerChange}
131
+ todayOrSelectedDateRef={setTodayOrSelectedDate}
132
+ defaultMonth={defaultMonth}
133
+ nextButtonProps={nextButtonProps}
134
+ previousButtonProps={previousButtonProps}
135
+ />
137
136
  </Popover>
138
137
  ) : null}
139
138
  </>
@@ -1,6 +1,6 @@
1
1
  import { IconButtonProps } from '@lumx/react';
2
2
  import { GenericProps } from '@lumx/react/utils';
3
- import { RefObject } from 'react';
3
+ import { Ref } from 'react';
4
4
 
5
5
  /**
6
6
  * Defines the props of the component.
@@ -20,7 +20,7 @@ export interface DatePickerProps extends GenericProps {
20
20
  previousButtonProps: Pick<IconButtonProps, 'label'> &
21
21
  Omit<IconButtonProps, 'label' | 'onClick' | 'icon' | 'emphasis'>;
22
22
  /** Reference to the <button> element corresponding to the current date or the selected date. */
23
- todayOrSelectedDateRef?: RefObject<HTMLButtonElement>;
23
+ todayOrSelectedDateRef?: Ref<HTMLButtonElement>;
24
24
  /** Currently selected date. */
25
25
  value: Date | undefined;
26
26
  /** On change callback. */
@@ -1,3 +1,4 @@
1
+ import noop from 'lodash/noop';
1
2
  import { mdiClose } from '@lumx/icons';
2
3
  import {
3
4
  AlertDialog,
@@ -17,7 +18,8 @@ import {
17
18
  } from '@lumx/react';
18
19
  import { DIALOG_TRANSITION_DURATION } from '@lumx/core/js/constants';
19
20
  import { select } from '@storybook/addon-knobs';
20
- import React, { RefObject, useRef, useState } from 'react';
21
+ import React, { RefObject, useCallback, useRef, useState } from 'react';
22
+ import { useBooleanState } from '@lumx/react/hooks/useBooleanState';
21
23
  import { Dialog, DialogSizes } from './Dialog';
22
24
  import { loremIpsum } from '../../stories/knobs/lorem';
23
25
  import { chromaticForceScreenSize } from '../../stories/chromaticForceScreenSize';
@@ -43,8 +45,8 @@ const footer = <footer className="lumx-spacing-padding">Dialog footer</footer>;
43
45
  function useOpenButton(theme: Theme, defaultState = true) {
44
46
  const buttonRef = useRef() as RefObject<HTMLButtonElement>;
45
47
  const [isOpen, setOpen] = useState(defaultState);
46
- const openDialog = () => setOpen(true);
47
- const closeDialog = () => setOpen(false);
48
+ const openDialog = useCallback(() => setOpen(true), []);
49
+ const closeDialog = useCallback(() => setOpen(false), []);
48
50
 
49
51
  return {
50
52
  button: (
@@ -301,6 +303,9 @@ export const DialogFocusTrap = ({ theme }: any) => {
301
303
  };
302
304
  const [date, setDate] = useState<Date | undefined>(new Date('2020-05-18'));
303
305
 
306
+ const datePickerDialogButtonRef = useRef<HTMLButtonElement>(null);
307
+ const [isDatePickerDialogOpen, closeDatePickerDialog, openDatePickerDialog] = useBooleanState(false);
308
+
304
309
  return (
305
310
  <>
306
311
  {button}
@@ -314,6 +319,10 @@ export const DialogFocusTrap = ({ theme }: any) => {
314
319
  />
315
320
  </header>
316
321
  <div className="lumx-spacing-padding-horizontal-huge lumx-spacing-padding-bottom-huge">
322
+ {/* Testing hidden input do not count in th focus trap*/}
323
+ <input hidden type="file" />
324
+ <input type="hidden" />
325
+
317
326
  <div className="lumx-spacing-margin-bottom-huge">
318
327
  The text field should capture the focus on open and a focus trap should be in place.
319
328
  </div>
@@ -335,16 +344,51 @@ export const DialogFocusTrap = ({ theme }: any) => {
335
344
  />
336
345
 
337
346
  <FlexBox orientation="horizontal" hAlign="bottom" gap="regular">
338
- <DatePickerField
339
- locale="fr"
340
- label="Start date"
341
- placeholder="Pick a date"
342
- theme={theme}
343
- onChange={setDate}
344
- value={date}
345
- nextButtonProps={{ label: 'Next month' }}
346
- previousButtonProps={{ label: 'Previous month' }}
347
- />
347
+ <Button ref={datePickerDialogButtonRef} onClick={openDatePickerDialog}>
348
+ Open date picker
349
+ </Button>
350
+ <Dialog
351
+ isOpen={isDatePickerDialogOpen}
352
+ parentElement={datePickerDialogButtonRef}
353
+ onClose={closeDatePickerDialog}
354
+ >
355
+ <header>
356
+ <Toolbar
357
+ label={<h1 className="lumx-typography-title">Date picker</h1>}
358
+ after={
359
+ <IconButton
360
+ label="Close"
361
+ icon={mdiClose}
362
+ onClick={closeDatePickerDialog}
363
+ emphasis={Emphasis.low}
364
+ />
365
+ }
366
+ />
367
+ </header>
368
+ <div className="lumx-spacing-padding">
369
+ <DatePickerField
370
+ locale="fr"
371
+ label="Start date"
372
+ placeholder="Pick a date"
373
+ theme={theme}
374
+ onChange={setDate}
375
+ value={date}
376
+ nextButtonProps={{ label: 'Next month' }}
377
+ previousButtonProps={{ label: 'Previous month' }}
378
+ />
379
+ <DatePickerField
380
+ locale="fr"
381
+ label="Start date"
382
+ placeholder="Pick a date"
383
+ theme={theme}
384
+ onChange={noop}
385
+ value={undefined}
386
+ nextButtonProps={{ label: 'Next month' }}
387
+ previousButtonProps={{ label: 'Previous month' }}
388
+ defaultMonth={new Date('2020-05-18')}
389
+ />
390
+ </div>
391
+ </Dialog>
348
392
 
349
393
  <Select
350
394
  className="lumx-spacing-margin-left-huge"
@@ -142,7 +142,7 @@ export const Dialog: Comp<DialogProps, HTMLDivElement> = forwardRef((props, ref)
142
142
  const localContentRef = useRef<HTMLDivElement>(null);
143
143
  // Handle focus trap.
144
144
  // eslint-disable-next-line react-hooks/rules-of-hooks
145
- useFocusTrap(wrapperRef.current, focusElement?.current);
145
+ useFocusTrap(isOpen && wrapperRef.current, focusElement?.current);
146
146
 
147
147
  // eslint-disable-next-line react-hooks/rules-of-hooks
148
148
  useDisableBodyScroll(isOpen && localContentRef.current);
@@ -49,6 +49,13 @@ exports[`<Dialog> Snapshots and structure should render story DialogFocusTrap 1`
49
49
  <div
50
50
  className="lumx-spacing-padding-horizontal-huge lumx-spacing-padding-bottom-huge"
51
51
  >
52
+ <input
53
+ hidden={true}
54
+ type="file"
55
+ />
56
+ <input
57
+ type="hidden"
58
+ />
52
59
  <div
53
60
  className="lumx-spacing-margin-bottom-huge"
54
61
  >
@@ -80,23 +87,84 @@ exports[`<Dialog> Snapshots and structure should render story DialogFocusTrap 1`
80
87
  hAlign="bottom"
81
88
  orientation="horizontal"
82
89
  >
83
- <DatePickerField
84
- label="Start date"
85
- locale="fr"
86
- nextButtonProps={
87
- Object {
88
- "label": "Next month",
89
- }
90
- }
91
- onChange={[Function]}
92
- placeholder="Pick a date"
93
- previousButtonProps={
90
+ <Button
91
+ emphasis="high"
92
+ onClick={[Function]}
93
+ size="m"
94
+ theme="light"
95
+ >
96
+ Open date picker
97
+ </Button>
98
+ <Dialog
99
+ isOpen={false}
100
+ onClose={[Function]}
101
+ parentElement={
94
102
  Object {
95
- "label": "Previous month",
103
+ "current": null,
96
104
  }
97
105
  }
98
- value={2020-05-18T00:00:00.000Z}
99
- />
106
+ size="big"
107
+ >
108
+ <header>
109
+ <Toolbar
110
+ after={
111
+ <IconButton
112
+ emphasis="low"
113
+ icon="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
114
+ label="Close"
115
+ onClick={[Function]}
116
+ size="m"
117
+ theme="light"
118
+ />
119
+ }
120
+ label={
121
+ <h1
122
+ className="lumx-typography-title"
123
+ >
124
+ Date picker
125
+ </h1>
126
+ }
127
+ />
128
+ </header>
129
+ <div
130
+ className="lumx-spacing-padding"
131
+ >
132
+ <DatePickerField
133
+ label="Start date"
134
+ locale="fr"
135
+ nextButtonProps={
136
+ Object {
137
+ "label": "Next month",
138
+ }
139
+ }
140
+ onChange={[Function]}
141
+ placeholder="Pick a date"
142
+ previousButtonProps={
143
+ Object {
144
+ "label": "Previous month",
145
+ }
146
+ }
147
+ value={2020-05-18T00:00:00.000Z}
148
+ />
149
+ <DatePickerField
150
+ defaultMonth={2020-05-18T00:00:00.000Z}
151
+ label="Start date"
152
+ locale="fr"
153
+ nextButtonProps={
154
+ Object {
155
+ "label": "Next month",
156
+ }
157
+ }
158
+ onChange={[Function]}
159
+ placeholder="Pick a date"
160
+ previousButtonProps={
161
+ Object {
162
+ "label": "Previous month",
163
+ }
164
+ }
165
+ />
166
+ </div>
167
+ </Dialog>
100
168
  <Select
101
169
  className="lumx-spacing-margin-left-huge"
102
170
  isOpen={false}