@navikt/ds-react 4.12.1 → 5.0.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 (79) hide show
  1. package/_docs.json +101 -127
  2. package/cjs/date/datepicker/DatePicker.js +1 -1
  3. package/cjs/date/hooks/useEscape.js +6 -1
  4. package/cjs/modal/Modal.js +85 -46
  5. package/cjs/modal/{ModalContent.js → ModalBody.js} +3 -3
  6. package/cjs/modal/ModalContext.js +8 -0
  7. package/cjs/modal/ModalFooter.js +46 -0
  8. package/cjs/modal/ModalHeader.js +56 -0
  9. package/cjs/modal/ModalUtils.js +40 -0
  10. package/cjs/modal/dialog-polyfill.js +833 -0
  11. package/cjs/popover/Popover.js +5 -2
  12. package/cjs/table/DataCell.js +1 -3
  13. package/cjs/table/ExpandableRow.js +2 -1
  14. package/cjs/table/HeaderCell.js +1 -4
  15. package/cjs/table/Table.js +1 -1
  16. package/esm/date/datepicker/DatePicker.d.ts +2 -2
  17. package/esm/date/datepicker/DatePicker.js +1 -1
  18. package/esm/date/datepicker/DatePicker.js.map +1 -1
  19. package/esm/date/hooks/useEscape.d.ts +2 -1
  20. package/esm/date/hooks/useEscape.js +6 -1
  21. package/esm/date/hooks/useEscape.js.map +1 -1
  22. package/esm/modal/Modal.d.ts +76 -51
  23. package/esm/modal/Modal.js +87 -48
  24. package/esm/modal/Modal.js.map +1 -1
  25. package/esm/modal/ModalBody.d.ts +6 -0
  26. package/esm/modal/{ModalContent.js → ModalBody.js} +4 -4
  27. package/esm/modal/ModalBody.js.map +1 -0
  28. package/esm/modal/ModalContext.d.ts +6 -0
  29. package/esm/modal/ModalContext.js +3 -0
  30. package/esm/modal/ModalContext.js.map +1 -0
  31. package/esm/modal/ModalFooter.d.ts +6 -0
  32. package/esm/modal/ModalFooter.js +19 -0
  33. package/esm/modal/ModalFooter.js.map +1 -0
  34. package/esm/modal/ModalHeader.d.ts +11 -0
  35. package/esm/modal/ModalHeader.js +29 -0
  36. package/esm/modal/ModalHeader.js.map +1 -0
  37. package/esm/modal/ModalUtils.d.ts +4 -0
  38. package/esm/modal/ModalUtils.js +33 -0
  39. package/esm/modal/ModalUtils.js.map +1 -0
  40. package/esm/modal/dialog-polyfill.d.ts +5 -0
  41. package/esm/modal/dialog-polyfill.js +832 -0
  42. package/esm/modal/dialog-polyfill.js.map +1 -0
  43. package/esm/modal/index.d.ts +3 -1
  44. package/esm/popover/Popover.js +6 -3
  45. package/esm/popover/Popover.js.map +1 -1
  46. package/esm/provider/Provider.d.ts +1 -6
  47. package/esm/provider/Provider.js.map +1 -1
  48. package/esm/table/DataCell.js +2 -4
  49. package/esm/table/DataCell.js.map +1 -1
  50. package/esm/table/ExpandableRow.js +2 -1
  51. package/esm/table/ExpandableRow.js.map +1 -1
  52. package/esm/table/HeaderCell.js +1 -4
  53. package/esm/table/HeaderCell.js.map +1 -1
  54. package/esm/table/Table.d.ts +2 -3
  55. package/esm/table/Table.js +1 -1
  56. package/esm/table/Table.js.map +1 -1
  57. package/package.json +3 -5
  58. package/src/date/datepicker/DatePicker.tsx +3 -3
  59. package/src/date/hooks/useEscape.tsx +8 -3
  60. package/src/modal/Modal.tsx +171 -121
  61. package/src/modal/ModalBody.tsx +14 -0
  62. package/src/modal/ModalContext.ts +6 -0
  63. package/src/modal/ModalFooter.tsx +14 -0
  64. package/src/modal/ModalHeader.tsx +42 -0
  65. package/src/modal/ModalUtils.ts +37 -0
  66. package/src/modal/dialog-polyfill.ts +980 -0
  67. package/src/modal/index.ts +3 -1
  68. package/src/modal/modal.stories.tsx +142 -59
  69. package/src/popover/Popover.tsx +6 -2
  70. package/src/provider/Provider.tsx +1 -6
  71. package/src/table/DataCell.tsx +1 -5
  72. package/src/table/ExpandableRow.tsx +2 -1
  73. package/src/table/HeaderCell.tsx +1 -5
  74. package/src/table/Table.tsx +3 -4
  75. package/src/table/stories/table-expandable.stories.tsx +37 -1
  76. package/src/table/stories/table.stories.tsx +4 -1
  77. package/esm/modal/ModalContent.d.ts +0 -10
  78. package/esm/modal/ModalContent.js.map +0 -1
  79. package/src/modal/ModalContent.tsx +0 -26
package/_docs.json CHANGED
@@ -5769,10 +5769,10 @@
5769
5769
  "filePath": "src/modal/Modal.tsx",
5770
5770
  "displayName": "Modal",
5771
5771
  "props": {
5772
- "children": {
5772
+ "header": {
5773
5773
  "defaultValue": null,
5774
- "description": "Modal content",
5775
- "name": "children",
5774
+ "description": "Content for the header. Alteratively you can use <Modal.Header> instead for more control,\nbut then you have to set `aria-label` or `aria-labelledby` on the modal manually.",
5775
+ "name": "header",
5776
5776
  "parent": {
5777
5777
  "fileName": "src/modal/Modal.tsx",
5778
5778
  "name": "ModalProps"
@@ -5783,15 +5783,15 @@
5783
5783
  "name": "ModalProps"
5784
5784
  }
5785
5785
  ],
5786
- "required": true,
5786
+ "required": false,
5787
5787
  "type": {
5788
- "name": "ReactNode"
5788
+ "name": "{ label?: string; icon?: ReactNode; heading: string; size?: \"medium\" | \"small\"; closeButton?: boolean | undefined; } | undefined"
5789
5789
  }
5790
5790
  },
5791
- "open": {
5791
+ "children": {
5792
5792
  "defaultValue": null,
5793
- "description": "Open state for modal",
5794
- "name": "open",
5793
+ "description": "Modal content",
5794
+ "name": "children",
5795
5795
  "parent": {
5796
5796
  "fileName": "src/modal/Modal.tsx",
5797
5797
  "name": "ModalProps"
@@ -5804,34 +5804,13 @@
5804
5804
  ],
5805
5805
  "required": true,
5806
5806
  "type": {
5807
- "name": "boolean"
5807
+ "name": "ReactNode"
5808
5808
  }
5809
5809
  },
5810
- "onClose": {
5810
+ "open": {
5811
5811
  "defaultValue": null,
5812
- "description": "Callback for modal wanting to close",
5813
- "name": "onClose",
5814
- "parent": {
5815
- "fileName": "src/modal/Modal.tsx",
5816
- "name": "ModalProps"
5817
- },
5818
- "declarations": [
5819
- {
5820
- "fileName": "src/modal/Modal.tsx",
5821
- "name": "ModalProps"
5822
- }
5823
- ],
5824
- "required": true,
5825
- "type": {
5826
- "name": "() => void"
5827
- }
5828
- },
5829
- "shouldCloseOnOverlayClick": {
5830
- "defaultValue": {
5831
- "value": "true"
5832
- },
5833
- "description": "If modal should close on overlay click (click outside Modal)",
5834
- "name": "shouldCloseOnOverlayClick",
5812
+ "description": "Whether the modal should be visible or not.\nRemember to use the `onClose` callback to keep your local state in sync.\nYou can also use `ref.current.openModal()` and `ref.current.close()`.",
5813
+ "name": "open",
5835
5814
  "parent": {
5836
5815
  "fileName": "src/modal/Modal.tsx",
5837
5816
  "name": "ModalProps"
@@ -5847,10 +5826,10 @@
5847
5826
  "name": "boolean"
5848
5827
  }
5849
5828
  },
5850
- "className": {
5829
+ "onClose": {
5851
5830
  "defaultValue": null,
5852
- "description": "User defined classname for modal",
5853
- "name": "className",
5831
+ "description": "Called when the modal has been closed",
5832
+ "name": "onClose",
5854
5833
  "parent": {
5855
5834
  "fileName": "src/modal/Modal.tsx",
5856
5835
  "name": "ModalProps"
@@ -5863,13 +5842,13 @@
5863
5842
  ],
5864
5843
  "required": false,
5865
5844
  "type": {
5866
- "name": "string"
5845
+ "name": "ReactEventHandler<HTMLDialogElement>"
5867
5846
  }
5868
5847
  },
5869
- "overlayClassName": {
5848
+ "onBeforeClose": {
5870
5849
  "defaultValue": null,
5871
- "description": "User defined classname for modal",
5872
- "name": "overlayClassName",
5850
+ "description": "Called when the user wants to close the modal (clicked the close button or pressed Esc).\n@returns Whether to close the modal",
5851
+ "name": "onBeforeClose",
5873
5852
  "parent": {
5874
5853
  "fileName": "src/modal/Modal.tsx",
5875
5854
  "name": "ModalProps"
@@ -5882,15 +5861,13 @@
5882
5861
  ],
5883
5862
  "required": false,
5884
5863
  "type": {
5885
- "name": "string"
5864
+ "name": "(() => boolean | void)"
5886
5865
  }
5887
5866
  },
5888
- "closeButton": {
5889
- "defaultValue": {
5890
- "value": "true"
5891
- },
5892
- "description": "Removes close-button(X) when false",
5893
- "name": "closeButton",
5867
+ "onCancel": {
5868
+ "defaultValue": null,
5869
+ "description": "Called when the user presses the Esc key, unless `onBeforeClose()` returns `false`.",
5870
+ "name": "onCancel",
5894
5871
  "parent": {
5895
5872
  "fileName": "src/modal/Modal.tsx",
5896
5873
  "name": "ModalProps"
@@ -5903,13 +5880,15 @@
5903
5880
  ],
5904
5881
  "required": false,
5905
5882
  "type": {
5906
- "name": "boolean"
5883
+ "name": "ReactEventHandler<HTMLDialogElement>"
5907
5884
  }
5908
5885
  },
5909
- "shouldCloseOnEsc": {
5910
- "defaultValue": null,
5886
+ "width": {
5887
+ "defaultValue": {
5888
+ "value": "fit-content (up to 700px)"
5889
+ },
5911
5890
  "description": "",
5912
- "name": "shouldCloseOnEsc",
5891
+ "name": "width",
5913
5892
  "parent": {
5914
5893
  "fileName": "src/modal/Modal.tsx",
5915
5894
  "name": "ModalProps"
@@ -5922,13 +5901,13 @@
5922
5901
  ],
5923
5902
  "required": false,
5924
5903
  "type": {
5925
- "name": "boolean"
5904
+ "name": "string | number"
5926
5905
  }
5927
5906
  },
5928
- "style": {
5907
+ "className": {
5929
5908
  "defaultValue": null,
5930
- "description": "Allows custom styling of ReactModal, in accordance with their typing",
5931
- "name": "style",
5909
+ "description": "User defined classname for modal",
5910
+ "name": "className",
5932
5911
  "parent": {
5933
5912
  "fileName": "src/modal/Modal.tsx",
5934
5913
  "name": "ModalProps"
@@ -5941,13 +5920,13 @@
5941
5920
  ],
5942
5921
  "required": false,
5943
5922
  "type": {
5944
- "name": "Styles"
5923
+ "name": "string"
5945
5924
  }
5946
5925
  },
5947
- "parentSelector": {
5926
+ "aria-labelledby": {
5948
5927
  "defaultValue": null,
5949
- "description": "Callback for setting parent element modal will attach to",
5950
- "name": "parentSelector",
5928
+ "description": "Sets aria-labelledby on modal.\nNo need to set this manually if the `header` prop is used. A reference to `header.heading` will be created automatically.\n@warning If not using `header`, you should set either `aria-labelledby` or `aria-label`.",
5929
+ "name": "aria-labelledby",
5951
5930
  "parent": {
5952
5931
  "fileName": "src/modal/Modal.tsx",
5953
5932
  "name": "ModalProps"
@@ -5960,40 +5939,46 @@
5960
5939
  ],
5961
5940
  "required": false,
5962
5941
  "type": {
5963
- "name": "(() => HTMLElement)"
5942
+ "name": "string"
5964
5943
  }
5965
5944
  },
5966
- "aria-labelledby": {
5945
+ "ref": {
5967
5946
  "defaultValue": null,
5968
- "description": "",
5969
- "name": "aria-labelledby",
5947
+ "description": "Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref).\n@see https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom",
5948
+ "name": "ref",
5970
5949
  "parent": {
5971
- "fileName": "src/modal/Modal.tsx",
5972
- "name": "ModalProps"
5950
+ "fileName": "aksel/node_modules/@types/react/ts5.0/index.d.ts",
5951
+ "name": "RefAttributes"
5973
5952
  },
5974
5953
  "declarations": [
5975
5954
  {
5976
- "fileName": "src/modal/Modal.tsx",
5977
- "name": "ModalProps"
5955
+ "fileName": "aksel/node_modules/@types/react/ts5.0/index.d.ts",
5956
+ "name": "RefAttributes"
5978
5957
  }
5979
5958
  ],
5980
5959
  "required": false,
5981
5960
  "type": {
5982
- "name": "string"
5961
+ "name": "Ref<HTMLDialogElement>"
5983
5962
  }
5984
- },
5985
- "aria-describedby": {
5963
+ }
5964
+ }
5965
+ },
5966
+ {
5967
+ "filePath": "src/modal/ModalBody.tsx",
5968
+ "displayName": "ModalBody",
5969
+ "props": {
5970
+ "className": {
5986
5971
  "defaultValue": null,
5987
5972
  "description": "",
5988
- "name": "aria-describedby",
5973
+ "name": "className",
5989
5974
  "parent": {
5990
- "fileName": "src/modal/Modal.tsx",
5991
- "name": "ModalProps"
5975
+ "fileName": "aksel/node_modules/@types/react/ts5.0/index.d.ts",
5976
+ "name": "HTMLAttributes"
5992
5977
  },
5993
5978
  "declarations": [
5994
5979
  {
5995
- "fileName": "src/modal/Modal.tsx",
5996
- "name": "ModalProps"
5980
+ "fileName": "aksel/node_modules/@types/react/ts5.0/index.d.ts",
5981
+ "name": "HTMLAttributes"
5997
5982
  }
5998
5983
  ],
5999
5984
  "required": false,
@@ -6001,37 +5986,43 @@
6001
5986
  "name": "string"
6002
5987
  }
6003
5988
  },
6004
- "aria-modal": {
5989
+ "ref": {
6005
5990
  "defaultValue": null,
6006
- "description": "",
6007
- "name": "aria-modal",
5991
+ "description": "Allows getting a ref to the component instance.\nOnce the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref).\n@see https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom",
5992
+ "name": "ref",
6008
5993
  "parent": {
6009
- "fileName": "src/modal/Modal.tsx",
6010
- "name": "ModalProps"
5994
+ "fileName": "aksel/node_modules/@types/react/ts5.0/index.d.ts",
5995
+ "name": "RefAttributes"
6011
5996
  },
6012
5997
  "declarations": [
6013
5998
  {
6014
- "fileName": "src/modal/Modal.tsx",
6015
- "name": "ModalProps"
5999
+ "fileName": "aksel/node_modules/@types/react/ts5.0/index.d.ts",
6000
+ "name": "RefAttributes"
6016
6001
  }
6017
6002
  ],
6018
6003
  "required": false,
6019
6004
  "type": {
6020
- "name": "boolean"
6005
+ "name": "Ref<HTMLDivElement>"
6021
6006
  }
6022
- },
6023
- "aria-label": {
6007
+ }
6008
+ }
6009
+ },
6010
+ {
6011
+ "filePath": "src/modal/ModalFooter.tsx",
6012
+ "displayName": "ModalFooter",
6013
+ "props": {
6014
+ "className": {
6024
6015
  "defaultValue": null,
6025
- "description": "Sets aria-label on modal\n@warning This should be set if not using 'aria-labelledby' or 'aria-describedby'",
6026
- "name": "aria-label",
6016
+ "description": "",
6017
+ "name": "className",
6027
6018
  "parent": {
6028
- "fileName": "src/modal/Modal.tsx",
6029
- "name": "ModalProps"
6019
+ "fileName": "aksel/node_modules/@types/react/ts5.0/index.d.ts",
6020
+ "name": "HTMLAttributes"
6030
6021
  },
6031
6022
  "declarations": [
6032
6023
  {
6033
- "fileName": "src/modal/Modal.tsx",
6034
- "name": "ModalProps"
6024
+ "fileName": "aksel/node_modules/@types/react/ts5.0/index.d.ts",
6025
+ "name": "HTMLAttributes"
6035
6026
  }
6036
6027
  ],
6037
6028
  "required": false,
@@ -6055,32 +6046,34 @@
6055
6046
  ],
6056
6047
  "required": false,
6057
6048
  "type": {
6058
- "name": "Ref<ReactModal>"
6049
+ "name": "Ref<HTMLDivElement>"
6059
6050
  }
6060
6051
  }
6061
6052
  }
6062
6053
  },
6063
6054
  {
6064
- "filePath": "src/modal/ModalContent.tsx",
6065
- "displayName": "ModalContent",
6055
+ "filePath": "src/modal/ModalHeader.tsx",
6056
+ "displayName": "ModalHeader",
6066
6057
  "props": {
6067
- "children": {
6068
- "defaultValue": null,
6069
- "description": "Modal.Content content",
6070
- "name": "children",
6058
+ "closeButton": {
6059
+ "defaultValue": {
6060
+ "value": "true"
6061
+ },
6062
+ "description": "Removes close-button (X) when false",
6063
+ "name": "closeButton",
6071
6064
  "parent": {
6072
- "fileName": "src/modal/ModalContent.tsx",
6073
- "name": "ModalContentProps"
6065
+ "fileName": "src/modal/ModalHeader.tsx",
6066
+ "name": "ModalHeaderProps"
6074
6067
  },
6075
6068
  "declarations": [
6076
6069
  {
6077
- "fileName": "src/modal/ModalContent.tsx",
6078
- "name": "ModalContentProps"
6070
+ "fileName": "src/modal/ModalHeader.tsx",
6071
+ "name": "ModalHeaderProps"
6079
6072
  }
6080
6073
  ],
6081
- "required": true,
6074
+ "required": false,
6082
6075
  "type": {
6083
- "name": "ReactNode"
6076
+ "name": "boolean"
6084
6077
  }
6085
6078
  },
6086
6079
  "className": {
@@ -6975,25 +6968,6 @@
6975
6968
  "type": {
6976
6969
  "name": "HTMLElement"
6977
6970
  }
6978
- },
6979
- "appElement": {
6980
- "defaultValue": null,
6981
- "description": "",
6982
- "name": "appElement",
6983
- "parent": {
6984
- "fileName": "src/provider/Provider.tsx",
6985
- "name": "ProviderProps"
6986
- },
6987
- "declarations": [
6988
- {
6989
- "fileName": "src/provider/Provider.tsx",
6990
- "name": "ProviderProps"
6991
- }
6992
- ],
6993
- "required": false,
6994
- "type": {
6995
- "name": "HTMLElement"
6996
- }
6997
6971
  }
6998
6972
  }
6999
6973
  },
@@ -8215,7 +8189,7 @@
8215
8189
  "defaultValue": {
8216
8190
  "value": "\"medium\""
8217
8191
  },
8218
- "description": "Changes padding",
8192
+ "description": "Changes padding around Cells",
8219
8193
  "name": "size",
8220
8194
  "parent": {
8221
8195
  "fileName": "src/table/Table.tsx",
@@ -8229,7 +8203,7 @@
8229
8203
  ],
8230
8204
  "required": false,
8231
8205
  "type": {
8232
- "name": "\"medium\" | \"small\""
8206
+ "name": "\"large\" | \"medium\" | \"small\""
8233
8207
  }
8234
8208
  },
8235
8209
  "zebraStripes": {
@@ -10919,9 +10893,9 @@
10919
10893
  },
10920
10894
  "strategy": {
10921
10895
  "defaultValue": {
10922
- "value": "\"absolute\""
10896
+ "value": "See Popover"
10923
10897
  },
10924
- "description": "Avoid using if possible!\nChanges what CSS position property to use\nYou want to use \"fixed\" if parent wrapper has position relative, but you want popover to escape",
10898
+ "description": "Avoid using if possible!\nChanges what CSS position property to use.\nYou want to use \"fixed\" if parent wrapper has position relative, but you want popover to escape",
10925
10899
  "name": "strategy",
10926
10900
  "parent": {
10927
10901
  "fileName": "src/date/datepicker/DatePicker.tsx",
@@ -74,7 +74,7 @@ const TableHead_1 = require("./TableHead");
74
74
  */
75
75
  exports.DatePicker = (0, react_1.forwardRef)((_a, ref) => {
76
76
  var _b;
77
- var { children, locale = "nb", dropdownCaption, disabled = [], disableWeekends = false, showWeekNumber = false, selected, id, defaultSelected, className, wrapperClassName, open: _open, onClose, onOpenToggle, strategy = "absolute", bubbleEscape = false } = _a, rest = __rest(_a, ["children", "locale", "dropdownCaption", "disabled", "disableWeekends", "showWeekNumber", "selected", "id", "defaultSelected", "className", "wrapperClassName", "open", "onClose", "onOpenToggle", "strategy", "bubbleEscape"]);
77
+ var { children, locale = "nb", dropdownCaption, disabled = [], disableWeekends = false, showWeekNumber = false, selected, id, defaultSelected, className, wrapperClassName, open: _open, onClose, onOpenToggle, strategy, bubbleEscape = false } = _a, rest = __rest(_a, ["children", "locale", "dropdownCaption", "disabled", "disableWeekends", "showWeekNumber", "selected", "id", "defaultSelected", "className", "wrapperClassName", "open", "onClose", "onOpenToggle", "strategy", "bubbleEscape"]);
78
78
  const ariaId = (0, __1.useId)(id);
79
79
  const [open, setOpen] = (0, react_1.useState)(_open !== null && _open !== void 0 ? _open : false);
80
80
  const wrapperRef = (0, react_1.useRef)(null);
@@ -7,7 +7,12 @@ const useEscape = (open, setOpen, focusRef) => {
7
7
  setOpen(false);
8
8
  (focusRef === null || focusRef === void 0 ? void 0 : focusRef.current) && focusRef.current.focus();
9
9
  }, [focusRef, setOpen]);
10
- const escape = (0, react_1.useCallback)((e) => open && e.key === "Escape" && handleClose(), [handleClose, open]);
10
+ const escape = (0, react_1.useCallback)((event) => {
11
+ if (open && event.key === "Escape") {
12
+ event.preventDefault(); // This prevents modal from closing when using datepicker inside modal
13
+ handleClose();
14
+ }
15
+ }, [handleClose, open]);
11
16
  (0, react_1.useEffect)(() => {
12
17
  window.addEventListener("keydown", escape, false);
13
18
  return () => {
@@ -40,10 +40,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.Modal = void 0;
41
41
  const react_1 = __importStar(require("react"));
42
42
  const clsx_1 = __importDefault(require("clsx"));
43
- const react_modal_1 = __importDefault(require("react-modal"));
43
+ const dialog_polyfill_1 = __importDefault(require("./dialog-polyfill"));
44
44
  const __1 = require("..");
45
- const ModalContent_1 = __importDefault(require("./ModalContent"));
46
- const aksel_icons_1 = require("@navikt/aksel-icons");
45
+ const ModalBody_1 = __importDefault(require("./ModalBody"));
46
+ const ModalHeader_1 = __importDefault(require("./ModalHeader"));
47
+ const ModalFooter_1 = __importDefault(require("./ModalFooter"));
48
+ const ModalUtils_1 = require("./ModalUtils");
49
+ const ModalContext_1 = require("./ModalContext");
50
+ const needPolyfill = typeof window !== "undefined" && window.HTMLDialogElement === undefined;
47
51
  /**
48
52
  * A component that displays a modal dialog.
49
53
  *
@@ -51,63 +55,98 @@ const aksel_icons_1 = require("@navikt/aksel-icons");
51
55
  * @see 🏷️ {@link ModalProps}
52
56
  *
53
57
  * @example
58
+ * State change with `useRef`
59
+ * ```jsx
60
+ * const ref = useRef<HTMLDialogElement>(null);
61
+ * <Button onClick={() => ref.current?.showModal()}>Open modal</Button>
62
+ * <Modal
63
+ * ref={ref}
64
+ * header={{
65
+ * label: "Optional label",
66
+ * icon: <FileIcon aria-hidden />,
67
+ * heading: "My heading",
68
+ * }}
69
+ * >
70
+ * <Modal.Body>
71
+ * <BodyLong>Hello world</BodyLong>
72
+ * </Modal.Body>
73
+ * <Modal.Footer>
74
+ * <Button>Save</Button>
75
+ * <Button type="button" variant="tertiary" onClick={() => ref.current?.close()}>Close</Button>
76
+ * </Modal.Footer>
77
+ * </Modal>
78
+ * ```
79
+ * @example
80
+ * State change with `useState`
54
81
  * ```jsx
55
82
  * const [open, setOpen] = useState(false);
56
- *
57
83
  * <Modal
58
84
  * open={open}
59
- * aria-label="Modal demo"
60
- * onClose={() => setOpen((x) => !x)}
85
+ * onClose={() => setOpen(false)}
61
86
  * aria-labelledby="modal-heading"
62
87
  * >
63
- * <Modal.Content>
64
- * <Heading spacing level="1" size="large" id="modal-heading">
65
- * Viktig info
66
- * </Heading>
67
- * <BodyLong spacing>
68
- * Hallo!
69
- * </BodyLong>
70
- * </Modal.Content>
88
+ * <Modal.Header>
89
+ * <Heading level="1" size="large" id="modal-heading">My heading</Heading>
90
+ * </Modal.Header>
91
+ * <Modal.Body>
92
+ * <BodyLong>Hello world</BodyLong>
93
+ * </Modal.Body>
71
94
  * </Modal>
72
95
  * ```
73
96
  */
74
97
  exports.Modal = (0, react_1.forwardRef)((_a, ref) => {
75
- var _b, _c;
76
- var { children, open, onClose, className, overlayClassName, shouldCloseOnOverlayClick = true, shouldCloseOnEsc = true, closeButton = true, "aria-describedby": ariaDescribedBy, "aria-labelledby": ariaLabelledBy, "aria-modal": ariaModal, "aria-label": contentLabel, style, parentSelector } = _a, rest = __rest(_a, ["children", "open", "onClose", "className", "overlayClassName", "shouldCloseOnOverlayClick", "shouldCloseOnEsc", "closeButton", "aria-describedby", "aria-labelledby", "aria-modal", "aria-label", "style", "parentSelector"]);
98
+ var _b;
99
+ var { header, children, open, onBeforeClose, onCancel, width, className, "aria-labelledby": ariaLabelledby, style } = _a, rest = __rest(_a, ["header", "children", "open", "onBeforeClose", "onCancel", "width", "className", "aria-labelledby", "style"]);
77
100
  const modalRef = (0, react_1.useRef)(null);
78
101
  const mergedRef = (0, react_1.useMemo)(() => (0, __1.mergeRefs)([modalRef, ref]), [ref]);
79
- const buttonRef = (0, react_1.useRef)(null);
80
- const rootElement = (_b = (0, __1.useProvider)()) === null || _b === void 0 ? void 0 : _b.rootElement;
81
- const appElement = (_c = (0, __1.useProvider)()) === null || _c === void 0 ? void 0 : _c.appElement;
102
+ const ariaLabelId = (0, __1.useId)();
103
+ if ((0, react_1.useContext)(ModalContext_1.ModalContext)) {
104
+ console.error("Modals should not be nested");
105
+ }
82
106
  (0, react_1.useEffect)(() => {
83
- appElement && exports.Modal.setAppElement(appElement);
84
- }, [appElement]);
85
- const onModalCloseRequest = (e) => {
86
- if (shouldCloseOnOverlayClick || e.type === "keydown") {
87
- onClose();
107
+ if (needPolyfill && modalRef.current) {
108
+ dialog_polyfill_1.default.registerDialog(modalRef.current);
88
109
  }
89
- else if (buttonRef.current) {
90
- buttonRef.current.focus();
91
- }
92
- };
93
- const getParentSelector = () => {
94
- if (parentSelector) {
95
- return parentSelector;
110
+ }, [modalRef]);
111
+ (0, react_1.useEffect)(() => {
112
+ // We need to have this in a useEffect so that the content renders before the modal is displayed,
113
+ // and in case `open` is true initially.
114
+ if (modalRef.current && open !== undefined) {
115
+ if (open && !modalRef.current.open) {
116
+ modalRef.current.showModal();
117
+ }
118
+ else if (!open && modalRef.current.open) {
119
+ modalRef.current.close();
120
+ }
96
121
  }
97
- return rootElement !== undefined
98
- ? () => rootElement
99
- : undefined;
100
- };
101
- return (react_1.default.createElement(react_modal_1.default, Object.assign({}, rest, { parentSelector: getParentSelector(), style: style, isOpen: open, ref: mergedRef, className: (0, clsx_1.default)("navds-modal", className), overlayClassName: (0, clsx_1.default)("navds-modal__overlay", overlayClassName), shouldCloseOnOverlayClick: shouldCloseOnOverlayClick, shouldCloseOnEsc: shouldCloseOnEsc, onRequestClose: (e) => onModalCloseRequest(e), aria: {
102
- describedby: ariaDescribedBy,
103
- labelledby: ariaLabelledBy,
104
- modal: ariaModal,
105
- }, contentLabel: contentLabel }),
106
- children,
107
- closeButton && (react_1.default.createElement(__1.Button, { className: (0, clsx_1.default)("navds-modal__button", {
108
- "navds-modal__button--shake": shouldCloseOnOverlayClick,
109
- }), size: "small", variant: "tertiary-neutral", ref: buttonRef, onClick: onClose, icon: react_1.default.createElement(aksel_icons_1.XMarkIcon, { title: "Lukk modalvindu" }) }))));
122
+ }, [modalRef, open]);
123
+ (0, ModalUtils_1.useBodyScrollLock)(modalRef, "navds-modal__document-body");
124
+ const isWidthPreset = typeof width === "string" && ["small", "medium"].includes(width);
125
+ return (react_1.default.createElement("dialog", Object.assign({ ref: mergedRef, className: (0, clsx_1.default)("navds-modal", className, {
126
+ "navds-modal--polyfilled": needPolyfill,
127
+ "navds-modal--autowidth": !width,
128
+ [`navds-modal--${width}`]: isWidthPreset,
129
+ }), style: Object.assign(Object.assign({}, style), (!isWidthPreset ? { width } : {})), onCancel: (event) => {
130
+ // FYI: onCancel fires when you press Esc
131
+ if (onBeforeClose && onBeforeClose() === false) {
132
+ event.preventDefault();
133
+ }
134
+ else if (onCancel)
135
+ onCancel(event);
136
+ }, "aria-labelledby": !ariaLabelledby && !rest["aria-label"] && header
137
+ ? ariaLabelId
138
+ : ariaLabelledby }, rest),
139
+ react_1.default.createElement(ModalContext_1.ModalContext.Provider, { value: {
140
+ closeHandler: (0, ModalUtils_1.getCloseHandler)(modalRef, header, onBeforeClose),
141
+ } },
142
+ header && (react_1.default.createElement(ModalHeader_1.default, null,
143
+ header.label && (react_1.default.createElement(__1.Detail, { className: "navds-modal__label" }, header.label)),
144
+ react_1.default.createElement(__1.Heading, { size: (_b = header.size) !== null && _b !== void 0 ? _b : "medium", level: "1", id: ariaLabelId },
145
+ react_1.default.createElement("span", { className: "navds-modal__header-icon" }, header.icon),
146
+ header.heading))),
147
+ children)));
110
148
  });
111
- exports.Modal.setAppElement = (element) => react_modal_1.default.setAppElement(element);
112
- exports.Modal.Content = ModalContent_1.default;
149
+ exports.Modal.Header = ModalHeader_1.default;
150
+ exports.Modal.Body = ModalBody_1.default;
151
+ exports.Modal.Footer = ModalFooter_1.default;
113
152
  exports.default = exports.Modal;
@@ -39,8 +39,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  const react_1 = __importStar(require("react"));
41
41
  const clsx_1 = __importDefault(require("clsx"));
42
- const ModalContent = (0, react_1.forwardRef)((_a, ref) => {
42
+ const ModalBody = (0, react_1.forwardRef)((_a, ref) => {
43
43
  var { className } = _a, rest = __rest(_a, ["className"]);
44
- return (react_1.default.createElement("div", Object.assign({}, rest, { ref: ref, className: (0, clsx_1.default)("navds-modal__content", className) })));
44
+ return (react_1.default.createElement("div", Object.assign({}, rest, { ref: ref, className: (0, clsx_1.default)("navds-modal__body", className) })));
45
45
  });
46
- exports.default = ModalContent;
46
+ exports.default = ModalBody;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ModalContext = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ exports.ModalContext = react_1.default.createContext(null);