@redsift/popovers 8.0.0 → 8.0.1

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 (185) hide show
  1. package/coverage/clover.xml +738 -0
  2. package/coverage/coverage-final.json +53 -0
  3. package/coverage/lcov-report/Tooltip.tsx.html +235 -0
  4. package/coverage/lcov-report/TooltipContent.tsx.html +235 -0
  5. package/coverage/lcov-report/TooltipTrigger.tsx.html +241 -0
  6. package/coverage/lcov-report/base.css +224 -0
  7. package/coverage/lcov-report/block-navigation.js +87 -0
  8. package/coverage/lcov-report/context.ts.html +97 -0
  9. package/coverage/lcov-report/dialog/Dialog.tsx.html +271 -0
  10. package/coverage/lcov-report/dialog/context.ts.html +97 -0
  11. package/coverage/lcov-report/dialog/index.html +191 -0
  12. package/coverage/lcov-report/dialog/index.ts.html +100 -0
  13. package/coverage/lcov-report/dialog/types.ts.html +253 -0
  14. package/coverage/lcov-report/dialog/useDialog.tsx.html +346 -0
  15. package/coverage/lcov-report/dialog/useDialogContext.tsx.html +121 -0
  16. package/coverage/lcov-report/dialog-content/DialogContent.tsx.html +487 -0
  17. package/coverage/lcov-report/dialog-content/index.html +146 -0
  18. package/coverage/lcov-report/dialog-content/index.ts.html +91 -0
  19. package/coverage/lcov-report/dialog-content/intl/index.html +116 -0
  20. package/coverage/lcov-report/dialog-content/intl/index.ts.html +106 -0
  21. package/coverage/lcov-report/dialog-content/styles.ts.html +301 -0
  22. package/coverage/lcov-report/dialog-content-actions/DialogContentActions.tsx.html +205 -0
  23. package/coverage/lcov-report/dialog-content-actions/index.html +146 -0
  24. package/coverage/lcov-report/dialog-content-actions/index.ts.html +91 -0
  25. package/coverage/lcov-report/dialog-content-actions/styles.ts.html +139 -0
  26. package/coverage/lcov-report/dialog-content-body/DialogContentBody.tsx.html +232 -0
  27. package/coverage/lcov-report/dialog-content-body/index.html +146 -0
  28. package/coverage/lcov-report/dialog-content-body/index.ts.html +91 -0
  29. package/coverage/lcov-report/dialog-content-body/styles.ts.html +259 -0
  30. package/coverage/lcov-report/dialog-content-header/DialogContentHeader.tsx.html +328 -0
  31. package/coverage/lcov-report/dialog-content-header/index.html +146 -0
  32. package/coverage/lcov-report/dialog-content-header/index.ts.html +91 -0
  33. package/coverage/lcov-report/dialog-content-header/styles.ts.html +193 -0
  34. package/coverage/lcov-report/dialog-trigger/DialogTrigger.tsx.html +214 -0
  35. package/coverage/lcov-report/dialog-trigger/index.html +131 -0
  36. package/coverage/lcov-report/dialog-trigger/index.ts.html +91 -0
  37. package/coverage/lcov-report/favicon.png +0 -0
  38. package/coverage/lcov-report/index.html +341 -0
  39. package/coverage/lcov-report/index.ts.html +97 -0
  40. package/coverage/lcov-report/popover/Popover.tsx.html +244 -0
  41. package/coverage/lcov-report/popover/context.ts.html +97 -0
  42. package/coverage/lcov-report/popover/index.html +191 -0
  43. package/coverage/lcov-report/popover/index.ts.html +100 -0
  44. package/coverage/lcov-report/popover/types.ts.html +244 -0
  45. package/coverage/lcov-report/popover/usePopover.tsx.html +295 -0
  46. package/coverage/lcov-report/popover/usePopoverContext.tsx.html +121 -0
  47. package/coverage/lcov-report/popover-content/PopoverContent.tsx.html +268 -0
  48. package/coverage/lcov-report/popover-content/index.html +146 -0
  49. package/coverage/lcov-report/popover-content/index.ts.html +91 -0
  50. package/coverage/lcov-report/popover-content/styles.ts.html +166 -0
  51. package/coverage/lcov-report/popover-trigger/PopoverTrigger.tsx.html +214 -0
  52. package/coverage/lcov-report/popover-trigger/index.html +131 -0
  53. package/coverage/lcov-report/popover-trigger/index.ts.html +91 -0
  54. package/coverage/lcov-report/prettify.css +1 -0
  55. package/coverage/lcov-report/prettify.js +2 -0
  56. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  57. package/coverage/lcov-report/sorter.js +196 -0
  58. package/coverage/lcov-report/styles.ts.html +391 -0
  59. package/coverage/lcov-report/toast/Toast.tsx.html +451 -0
  60. package/coverage/lcov-report/toast/index.html +161 -0
  61. package/coverage/lcov-report/toast/index.ts.html +91 -0
  62. package/coverage/lcov-report/toast/intl/index.html +116 -0
  63. package/coverage/lcov-report/toast/intl/index.ts.html +106 -0
  64. package/coverage/lcov-report/toast/styles.ts.html +214 -0
  65. package/coverage/lcov-report/toast/types.ts.html +217 -0
  66. package/coverage/lcov-report/toast-container/ToastContainer.tsx.html +217 -0
  67. package/coverage/lcov-report/toast-container/index.html +161 -0
  68. package/coverage/lcov-report/toast-container/index.ts.html +94 -0
  69. package/coverage/lcov-report/toast-container/styles.ts.html +2323 -0
  70. package/coverage/lcov-report/toast-container/useToast.tsx.html +469 -0
  71. package/coverage/lcov-report/toast-provider/ToastProvider.tsx.html +256 -0
  72. package/coverage/lcov-report/toast-provider/context.ts.html +106 -0
  73. package/coverage/lcov-report/toast-provider/index.html +161 -0
  74. package/coverage/lcov-report/toast-provider/index.ts.html +97 -0
  75. package/coverage/lcov-report/toast-provider/useToast.tsx.html +109 -0
  76. package/coverage/lcov-report/tooltip/Tooltip.tsx.html +250 -0
  77. package/coverage/lcov-report/tooltip/context.ts.html +97 -0
  78. package/coverage/lcov-report/tooltip/index.html +191 -0
  79. package/coverage/lcov-report/tooltip/index.ts.html +100 -0
  80. package/coverage/lcov-report/tooltip/types.ts.html +250 -0
  81. package/coverage/lcov-report/tooltip/useTooltip.tsx.html +352 -0
  82. package/coverage/lcov-report/tooltip/useTooltipContext.tsx.html +121 -0
  83. package/coverage/lcov-report/tooltip-content/TooltipContent.tsx.html +313 -0
  84. package/coverage/lcov-report/tooltip-content/index.html +146 -0
  85. package/coverage/lcov-report/tooltip-content/index.ts.html +91 -0
  86. package/coverage/lcov-report/tooltip-content/styles.ts.html +337 -0
  87. package/coverage/lcov-report/tooltip-trigger/TooltipTrigger.tsx.html +211 -0
  88. package/coverage/lcov-report/tooltip-trigger/index.html +131 -0
  89. package/coverage/lcov-report/tooltip-trigger/index.ts.html +91 -0
  90. package/coverage/lcov-report/types.ts.html +226 -0
  91. package/coverage/lcov-report/useTooltip.tsx.html +277 -0
  92. package/coverage/lcov-report/useTooltipContext.tsx.html +121 -0
  93. package/coverage/lcov-report/useTooltipOpen.tsx.html +559 -0
  94. package/coverage/lcov.info +1330 -0
  95. package/dist/package.json +96 -0
  96. package/index.ts +1 -0
  97. package/jest.config.js +3 -0
  98. package/package.json +2 -3
  99. package/rollup.config.js +13 -0
  100. package/src/components/dialog/Dialog.stories.tsx +201 -0
  101. package/src/components/dialog/Dialog.test.tsx +116 -0
  102. package/src/components/dialog/Dialog.tsx +62 -0
  103. package/src/components/dialog/context.ts +4 -0
  104. package/src/components/dialog/index.ts +5 -0
  105. package/src/components/dialog/types.ts +56 -0
  106. package/src/components/dialog/useDialog.tsx +87 -0
  107. package/src/components/dialog/useDialogContext.tsx +12 -0
  108. package/src/components/dialog-content/DialogContent.stories.tsx +337 -0
  109. package/src/components/dialog-content/DialogContent.tsx +134 -0
  110. package/src/components/dialog-content/index.ts +2 -0
  111. package/src/components/dialog-content/intl/en-US.json +3 -0
  112. package/src/components/dialog-content/intl/fr-FR.json +3 -0
  113. package/src/components/dialog-content/intl/index.ts +7 -0
  114. package/src/components/dialog-content/styles.ts +72 -0
  115. package/src/components/dialog-content/types.ts +11 -0
  116. package/src/components/dialog-content-actions/DialogContentActions.test.tsx +68 -0
  117. package/src/components/dialog-content-actions/DialogContentActions.tsx +40 -0
  118. package/src/components/dialog-content-actions/index.ts +2 -0
  119. package/src/components/dialog-content-actions/styles.ts +18 -0
  120. package/src/components/dialog-content-actions/types.ts +11 -0
  121. package/src/components/dialog-content-body/DialogContentBody.test.tsx +63 -0
  122. package/src/components/dialog-content-body/DialogContentBody.tsx +49 -0
  123. package/src/components/dialog-content-body/index.ts +2 -0
  124. package/src/components/dialog-content-body/styles.ts +58 -0
  125. package/src/components/dialog-content-body/types.ts +14 -0
  126. package/src/components/dialog-content-header/DialogContentHeader.test.tsx +63 -0
  127. package/src/components/dialog-content-header/DialogContentHeader.tsx +81 -0
  128. package/src/components/dialog-content-header/index.ts +2 -0
  129. package/src/components/dialog-content-header/styles.ts +36 -0
  130. package/src/components/dialog-content-header/types.ts +23 -0
  131. package/src/components/dialog-trigger/DialogTrigger.tsx +43 -0
  132. package/src/components/dialog-trigger/index.ts +2 -0
  133. package/src/components/dialog-trigger/types.ts +9 -0
  134. package/src/components/popover/Popover.stories.tsx +158 -0
  135. package/src/components/popover/Popover.test.tsx +102 -0
  136. package/src/components/popover/Popover.tsx +53 -0
  137. package/src/components/popover/context.ts +4 -0
  138. package/src/components/popover/index.ts +5 -0
  139. package/src/components/popover/types.ts +53 -0
  140. package/src/components/popover/usePopover.tsx +70 -0
  141. package/src/components/popover/usePopoverContext.tsx +12 -0
  142. package/src/components/popover-content/PopoverContent.tsx +61 -0
  143. package/src/components/popover-content/index.ts +2 -0
  144. package/src/components/popover-content/styles.ts +27 -0
  145. package/src/components/popover-content/types.ts +11 -0
  146. package/src/components/popover-trigger/PopoverTrigger.tsx +43 -0
  147. package/src/components/popover-trigger/index.ts +2 -0
  148. package/src/components/popover-trigger/types.ts +9 -0
  149. package/src/components/toast/Toast.stories.tsx +75 -0
  150. package/src/components/toast/Toast.test.tsx +63 -0
  151. package/src/components/toast/Toast.tsx +122 -0
  152. package/src/components/toast/index.ts +2 -0
  153. package/src/components/toast/intl/en-US.json +3 -0
  154. package/src/components/toast/intl/fr-FR.json +3 -0
  155. package/src/components/toast/intl/index.ts +7 -0
  156. package/src/components/toast/styles.ts +43 -0
  157. package/src/components/toast/types.ts +44 -0
  158. package/src/components/toast-container/ToastContainer.stories.tsx +349 -0
  159. package/src/components/toast-container/ToastContainer.tsx +44 -0
  160. package/src/components/toast-container/index.ts +3 -0
  161. package/src/components/toast-container/styles.ts +746 -0
  162. package/src/components/toast-container/types.ts +110 -0
  163. package/src/components/toast-container/useToast.test.tsx +111 -0
  164. package/src/components/toast-container/useToast.tsx +128 -0
  165. package/src/components/tooltip/Tooltip.stories.tsx +200 -0
  166. package/src/components/tooltip/Tooltip.test.tsx +119 -0
  167. package/src/components/tooltip/Tooltip.tsx +55 -0
  168. package/src/components/tooltip/context.ts +4 -0
  169. package/src/components/tooltip/index.ts +5 -0
  170. package/src/components/tooltip/types.ts +55 -0
  171. package/src/components/tooltip/useTooltip.tsx +89 -0
  172. package/src/components/tooltip/useTooltipContext.tsx +12 -0
  173. package/src/components/tooltip-content/TooltipContent.tsx +76 -0
  174. package/src/components/tooltip-content/index.ts +2 -0
  175. package/src/components/tooltip-content/styles.ts +84 -0
  176. package/src/components/tooltip-content/types.ts +14 -0
  177. package/src/components/tooltip-trigger/TooltipTrigger.tsx +42 -0
  178. package/src/components/tooltip-trigger/index.ts +2 -0
  179. package/src/components/tooltip-trigger/types.ts +9 -0
  180. package/src/index.ts +14 -0
  181. package/tsconfig.json +3 -0
  182. /package/{CONTRIBUTING.md → dist/CONTRIBUTING.md} +0 -0
  183. /package/{index.d.ts → dist/index.d.ts} +0 -0
  184. /package/{index.js → dist/index.js} +0 -0
  185. /package/{index.js.map → dist/index.js.map} +0 -0
@@ -0,0 +1,96 @@
1
+ {
2
+ "author": {
3
+ "name": "Red Sift"
4
+ },
5
+ "bugs": {
6
+ "url": "https://github.com/redsift/design-system/issues"
7
+ },
8
+ "description": "Popover component library as part of Red Sift's Design System. This package is based on @floating-ui/react and react-toastify.",
9
+ "homepage": "https://github.com/redsift/design-system",
10
+ "license": "MIT",
11
+ "main": "index.js",
12
+ "module": "index.js",
13
+ "name": "@redsift/popovers",
14
+ "publishConfig": {
15
+ "directory": "dist"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/redsift/design-system"
20
+ },
21
+ "sideEffects": false,
22
+ "scripts": {
23
+ "build": "rollup -c",
24
+ "check-types": "tsc && tsc-strict",
25
+ "lint": "eslint --ext .js,.jsx,.ts,.tsx src/",
26
+ "prepare": "install-peers || exit 0",
27
+ "prepublishOnly": "yarn build",
28
+ "test:unit": "NODE_ENV=test jest --verbose",
29
+ "test:storybook": "test-storybook -c ../../.storybook --url http://localhost:9000/ --coverage",
30
+ "test": "yarn test:unit && yarn test:storybook"
31
+ },
32
+ "types": "index.d.ts",
33
+ "version": "8.0.1",
34
+ "dependencies": {
35
+ "@floating-ui/react": "^0.19.2",
36
+ "classnames": "^2.3.1",
37
+ "react-aria": "^3.23.1",
38
+ "react-toastify": "^9.1.1"
39
+ },
40
+ "devDependencies": {
41
+ "@babel/core": "^7.8.3",
42
+ "@babel/plugin-proposal-class-properties": "^7.16.7",
43
+ "@babel/plugin-proposal-export-default-from": "^7.16.7",
44
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
45
+ "@babel/plugin-proposal-object-rest-spread": "^7.17.3",
46
+ "@babel/plugin-proposal-optional-chaining": "^7.16.7",
47
+ "@babel/plugin-proposal-private-methods": "^7.16.11",
48
+ "@babel/plugin-proposal-private-property-in-object": "^7.16.7",
49
+ "@babel/preset-env": "^7.17.10",
50
+ "@babel/preset-react": "^7.17.12",
51
+ "@babel/preset-typescript": "^7.16.7",
52
+ "@rollup/plugin-babel": "^6.0.2",
53
+ "@rollup/plugin-commonjs": "^24.0.0",
54
+ "@rollup/plugin-json": "^6.0.0",
55
+ "@rollup/plugin-node-resolve": "^13.3.0",
56
+ "@svgr/rollup": "^6.2.1",
57
+ "@testing-library/dom": "^8.5.0",
58
+ "@testing-library/jest-dom": "^5.16.4",
59
+ "@testing-library/react": "^13.4.0",
60
+ "@testing-library/user-event": "14.2.1",
61
+ "@types/jest": "^27.5.1",
62
+ "@types/react": "18.0.12",
63
+ "@types/react-dom": "18.0.5",
64
+ "@types/react-transition-group": "^4.4.5",
65
+ "@types/styled-components": "^5.1.25",
66
+ "@typescript-eslint/eslint-plugin": "^5.48.0",
67
+ "@typescript-eslint/parser": "^5.26.0",
68
+ "autoprefixer": "^9.7.4",
69
+ "babel-plugin-require-context-hook": "^1.0.0",
70
+ "changelog-verify": "^1.1.2",
71
+ "identity-obj-proxy": "^3.0.0",
72
+ "install-peers-cli": "^2.2.0",
73
+ "jest": "^28.1.0",
74
+ "jest-environment-jsdom": "^29.3.0",
75
+ "react": "18.2.0",
76
+ "react-dom": "18.2.0",
77
+ "rollup": "^2.72.1",
78
+ "rollup-plugin-analyzer": "^4.0.0",
79
+ "rollup-plugin-auto-external": "^2.0.0",
80
+ "rollup-plugin-cleaner": "^1.0.0",
81
+ "rollup-plugin-copy": "^3.4.0",
82
+ "rollup-plugin-dts": "^5.0.0",
83
+ "rollup-plugin-execute": "^1.1.0",
84
+ "rollup-plugin-svg": "^2.0.0",
85
+ "rollup-plugin-ts-paths-resolve": "^1.7.1",
86
+ "rollup-plugin-typescript-paths": "^1.3.1",
87
+ "ts-jest": "^28.0.3"
88
+ },
89
+ "peerDependencies": {
90
+ "@redsift/design-system": "^7.8.1",
91
+ "@redsift/icons": "^7.8.1",
92
+ "react": "18",
93
+ "react-dom": "18",
94
+ "styled-components": "^5.3.5"
95
+ }
96
+ }
package/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './src';
package/jest.config.js ADDED
@@ -0,0 +1,3 @@
1
+ var JEST_CONFIG = require('../../configs/jest.config');
2
+
3
+ module.exports = { ...JEST_CONFIG };
package/package.json CHANGED
@@ -30,7 +30,7 @@
30
30
  "test": "yarn test:unit && yarn test:storybook"
31
31
  },
32
32
  "types": "index.d.ts",
33
- "version": "8.0.0",
33
+ "version": "8.0.1",
34
34
  "dependencies": {
35
35
  "@floating-ui/react": "^0.19.2",
36
36
  "classnames": "^2.3.1",
@@ -92,6 +92,5 @@
92
92
  "react": "18",
93
93
  "react-dom": "18",
94
94
  "styled-components": "^5.3.5"
95
- },
96
- "gitHead": "d5b44ad25f944f5869829750628000b346718034"
95
+ }
97
96
  }
@@ -0,0 +1,13 @@
1
+ import { rollupConfig } from '../../configs/rollup.base.config';
2
+
3
+ import pkg from './package.json';
4
+
5
+ const baseRollupConfig = rollupConfig(__dirname, pkg);
6
+
7
+ // Bundle JS code
8
+ const bundleJS = { ...baseRollupConfig.bundleJS };
9
+
10
+ // Bundle TS types in D.TS files
11
+ const bundleType = { ...baseRollupConfig.bundleType };
12
+
13
+ export default [bundleJS, bundleType];
@@ -0,0 +1,201 @@
1
+ import React, { RefObject, useRef, useState } from 'react';
2
+ import { Flexbox, Button, Number, Text } from '@redsift/design-system';
3
+ import { Dialog, DialogProps } from '.';
4
+
5
+ export default {
6
+ title: 'Popovers/Dialog',
7
+ component: Dialog,
8
+ };
9
+
10
+ export const Uncontrolled = () => {
11
+ const nextButtonRef = useRef<HTMLButtonElement>(null);
12
+
13
+ return (
14
+ <Dialog initialFocus={nextButtonRef} defaultOpen={false}>
15
+ <Dialog.Trigger>
16
+ <Button variant="secondary">Button</Button>
17
+ </Dialog.Trigger>
18
+ <Dialog.Content>
19
+ <Dialog.Content.Header header="Dialog Header">
20
+ <Text>
21
+ <Number value={102} as="b" /> mails have been detected from this
22
+ domain.
23
+ </Text>
24
+ </Dialog.Content.Header>
25
+ <Dialog.Content.Body>
26
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
27
+ eiusmod tempor incididunt ut labore et dolore magna aliqua.
28
+ </Dialog.Content.Body>
29
+ <Dialog.Content.Actions justifyContent="space-between">
30
+ <Button variant="unstyled">Previous</Button>
31
+ <Flexbox>
32
+ <Button variant="secondary">Cancel</Button>
33
+ <Button ref={nextButtonRef as RefObject<HTMLButtonElement>}>
34
+ Next
35
+ </Button>
36
+ </Flexbox>
37
+ </Dialog.Content.Actions>
38
+ </Dialog.Content>
39
+ </Dialog>
40
+ );
41
+ };
42
+
43
+ export const Controlled = () => {
44
+ const [isOpen, setIsOpen] = useState(false);
45
+ const nextButtonRef = useRef<HTMLButtonElement>(null);
46
+
47
+ return (
48
+ <>
49
+ <Button variant="secondary" onClick={() => setIsOpen(!isOpen)}>
50
+ External trigger
51
+ </Button>
52
+ <Dialog isOpen={isOpen} onOpen={setIsOpen} initialFocus={nextButtonRef}>
53
+ <Dialog.Trigger>
54
+ <Button variant="secondary">Trigger</Button>
55
+ </Dialog.Trigger>
56
+ <Dialog.Content>
57
+ <Dialog.Content.Header header="Dialog Header">
58
+ <Text>
59
+ <Number value={102} as="b" /> mails have been detected from this
60
+ domain.
61
+ </Text>
62
+ </Dialog.Content.Header>
63
+ <Dialog.Content.Body>
64
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
65
+ eiusmod tempor incididunt ut labore et dolore magna aliqua.
66
+ </Dialog.Content.Body>
67
+ <Dialog.Content.Actions justifyContent="space-between">
68
+ <Button variant="unstyled">Previous</Button>
69
+ <Flexbox>
70
+ <Button variant="secondary">Cancel</Button>
71
+ <Button ref={nextButtonRef as RefObject<HTMLButtonElement>}>
72
+ Next
73
+ </Button>
74
+ </Flexbox>
75
+ </Dialog.Content.Actions>
76
+ </Dialog.Content>
77
+ </Dialog>
78
+ </>
79
+ );
80
+ };
81
+
82
+ export const TriggerIsNotAButton = () => (
83
+ <Dialog>
84
+ <Dialog.Trigger>
85
+ This text has a dialog but this will never be displayed because the
86
+ trigger is not a button.
87
+ </Dialog.Trigger>
88
+ <Dialog.Content>
89
+ <Text margin="16px">
90
+ Cupcake ipsum dolor sit amet blueie jujubes topping sesame snaps.
91
+ Liquorice marzipan jelly-o carrot cake icing croissant carrot cake. Tart
92
+ soufflé sweet roll halvah croissant wafer cotton candy. Candy halvah
93
+ marzipan bear claw donut.
94
+ </Text>
95
+ </Dialog.Content>
96
+ </Dialog>
97
+ );
98
+
99
+ export const EmptyDialog = () => (
100
+ <Flexbox
101
+ flexDirection="row"
102
+ flexWrap="wrap"
103
+ justifyContent="center"
104
+ alignItems="center"
105
+ padding="32px"
106
+ style={{ backgroundColor: '#EFEFEF' }}
107
+ >
108
+ <Dialog>
109
+ <Dialog.Trigger>
110
+ <Button variant="secondary">Button</Button>
111
+ </Dialog.Trigger>
112
+ </Dialog>
113
+ </Flexbox>
114
+ );
115
+
116
+ export const DialogOnDisabledButton = () => (
117
+ <Flexbox
118
+ flexDirection="row"
119
+ flexWrap="wrap"
120
+ justifyContent="center"
121
+ alignItems="center"
122
+ padding="32px"
123
+ style={{ backgroundColor: '#EFEFEF' }}
124
+ >
125
+ <Dialog>
126
+ <Dialog.Trigger>
127
+ <Button variant="secondary" isDisabled>
128
+ Disabled
129
+ </Button>
130
+ </Dialog.Trigger>
131
+ <Dialog.Content>
132
+ <Text margin="16px">
133
+ Cupcake ipsum dolor sit amet blueie jujubes topping sesame snaps.
134
+ Liquorice marzipan jelly-o carrot cake icing croissant carrot cake.
135
+ Tart soufflé sweet roll halvah croissant wafer cotton candy. Candy
136
+ halvah marzipan bear claw donut.
137
+ </Text>
138
+ </Dialog.Content>
139
+ </Dialog>
140
+ </Flexbox>
141
+ );
142
+
143
+ export const InitialFocus = () => {
144
+ const nextButtonRef = useRef<HTMLButtonElement>(null);
145
+
146
+ const renderDialog = ({
147
+ title,
148
+ initialFocus,
149
+ }: {
150
+ title: string;
151
+ initialFocus?: DialogProps['initialFocus'];
152
+ }) => (
153
+ <Dialog initialFocus={initialFocus}>
154
+ <Dialog.Trigger>
155
+ <Button variant="secondary">{title}</Button>
156
+ </Dialog.Trigger>
157
+ <Dialog.Content>
158
+ <Dialog.Content.Header header="Dialog Header">
159
+ <Text>
160
+ <Number value={102} as="b" /> mails have been detected from this
161
+ domain.
162
+ </Text>
163
+ </Dialog.Content.Header>
164
+ <Dialog.Content.Body>
165
+ {Array.from({ length: 100 }).map((e, i) => (
166
+ <p key={i}>
167
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
168
+ eiusmod tempor incididunt ut labore et dolore magna aliqua.
169
+ </p>
170
+ ))}
171
+ </Dialog.Content.Body>
172
+ <Dialog.Content.Actions justifyContent="space-between">
173
+ <Button variant="unstyled">Previous</Button>
174
+ <Flexbox>
175
+ <Button variant="secondary">Cancel</Button>
176
+ <Button ref={nextButtonRef as RefObject<HTMLButtonElement>}>
177
+ Next
178
+ </Button>
179
+ </Flexbox>
180
+ </Dialog.Content.Actions>
181
+ </Dialog.Content>
182
+ </Dialog>
183
+ );
184
+
185
+ return (
186
+ <Flexbox
187
+ flexDirection="row"
188
+ flexWrap="wrap"
189
+ justifyContent="center"
190
+ alignItems="center"
191
+ padding="32px"
192
+ style={{ backgroundColor: '#EFEFEF' }}
193
+ >
194
+ {renderDialog({ title: 'Default' })}
195
+ {renderDialog({ title: 'Next', initialFocus: nextButtonRef })}
196
+ {renderDialog({ title: 'Header', initialFocus: 'header' })}
197
+ {renderDialog({ title: 'Body', initialFocus: 'body' })}
198
+ {renderDialog({ title: 'Actions', initialFocus: 'actions' })}
199
+ </Flexbox>
200
+ );
201
+ };
@@ -0,0 +1,116 @@
1
+ import React from 'react';
2
+ import {
3
+ act,
4
+ fireEvent,
5
+ render,
6
+ screen,
7
+ waitFor,
8
+ } from '@testing-library/react';
9
+
10
+ import { Button } from '@redsift/design-system';
11
+ import { DialogContent } from '../dialog-content';
12
+ import { DialogTrigger } from '../dialog-trigger';
13
+ import { DialogContentHeader } from '../dialog-content-header';
14
+ import { DialogContentBody } from '../dialog-content-body';
15
+ import { Dialog } from '.';
16
+
17
+ describe('Dialog', () => {
18
+ const onOpenSpy = jest.fn();
19
+ const realError = console.error;
20
+
21
+ beforeEach(() => {
22
+ console.error = jest.fn();
23
+ });
24
+
25
+ afterEach(() => {
26
+ onOpenSpy.mockClear();
27
+ console.error = realError;
28
+ });
29
+
30
+ it.each`
31
+ Name | Component | props
32
+ ${'DialogTrigger'} | ${DialogTrigger} | ${{}}
33
+ ${'DialogContent'} | ${DialogContent} | ${{}}
34
+ ${'DialogContentHeader'} | ${DialogContentHeader} | ${{}}
35
+ `(
36
+ '$Name should throw error when not wrapped inside `Dialog`',
37
+ function ({ Component, props }) {
38
+ expect(() => render(<Component {...props} />)).toThrow(
39
+ 'Dialog components must be wrapped in <Dialog />'
40
+ );
41
+ }
42
+ );
43
+
44
+ it.each`
45
+ Name | Component | props
46
+ ${'Controlled Dialog'} | ${Dialog} | ${{ onOpen: onOpenSpy, isOpen: false }}
47
+ ${'Uncontrolled Dialog'} | ${Dialog} | ${{ onOpen: onOpenSpy, defaultOpen: false }}
48
+ `(
49
+ '$Name can be closed by default and then open',
50
+ async function ({ Name, Component, props }) {
51
+ const { getByText } = render(
52
+ <Component {...props}>
53
+ <DialogTrigger>
54
+ <Button variant="secondary">Trigger</Button>
55
+ </DialogTrigger>
56
+ <DialogContent>
57
+ <DialogContentBody>Content</DialogContentBody>
58
+ </DialogContent>
59
+ </Component>
60
+ );
61
+
62
+ expect(screen.queryByText('Content')).not.toBeInTheDocument();
63
+ expect(onOpenSpy).not.toHaveBeenCalled();
64
+
65
+ const triggerButton = getByText('Trigger');
66
+ fireEvent.click(triggerButton);
67
+
68
+ expect(onOpenSpy).toHaveBeenCalled();
69
+ if (Name.includes('Uncontrolled')) {
70
+ await act(async () => {});
71
+ await waitFor(() => {
72
+ expect(screen.queryByText('Content')).toBeInTheDocument();
73
+ });
74
+ expect(getByText('Content')).toBeVisible();
75
+ }
76
+ }
77
+ );
78
+
79
+ it.each`
80
+ Name | Component | props
81
+ ${'Controlled Dialog'} | ${Dialog} | ${{ onOpen: onOpenSpy, isOpen: true }}
82
+ ${'Uncontrolled Dialog'} | ${Dialog} | ${{ onOpen: onOpenSpy, defaultOpen: true }}
83
+ `(
84
+ '$Name can be open by default and then closed',
85
+ async function ({ Name, Component, props }) {
86
+ render(
87
+ <Component {...props}>
88
+ <DialogTrigger>
89
+ <Button variant="secondary">Trigger</Button>
90
+ </DialogTrigger>
91
+ <DialogContent>
92
+ <DialogContentBody>Content</DialogContentBody>
93
+ </DialogContent>
94
+ </Component>
95
+ );
96
+ await act(async () => {});
97
+ await waitFor(() => {
98
+ expect(screen.queryByText('Content')).toBeInTheDocument();
99
+ });
100
+
101
+ expect(screen.queryByText('Content')).toBeVisible();
102
+ expect(onOpenSpy).not.toHaveBeenCalled();
103
+
104
+ const closeButton = screen.getByLabelText('Close');
105
+ fireEvent.click(closeButton);
106
+
107
+ expect(onOpenSpy).toHaveBeenCalled();
108
+ if (Name.includes('Uncontrolled')) {
109
+ await act(async () => {});
110
+ await waitFor(() => {
111
+ expect(screen.queryByText('Content')).not.toBeInTheDocument();
112
+ });
113
+ }
114
+ }
115
+ );
116
+ });
@@ -0,0 +1,62 @@
1
+ import React from 'react';
2
+ import { partitionComponents, isComponent } from '@redsift/design-system';
3
+ import { DialogContent } from '../dialog-content';
4
+ import { DialogTrigger } from '../dialog-trigger';
5
+
6
+ import { DialogContext } from './context';
7
+ import { DialogProps, DialogSize } from './types';
8
+ import { useDialog } from './useDialog';
9
+
10
+ const COMPONENT_NAME = 'Dialog';
11
+ const CLASSNAME = 'redsift-dialog';
12
+ const DEFAULT_PROPS: Partial<DialogProps> = {
13
+ hasCloseButton: true,
14
+ size: DialogSize.medium,
15
+ };
16
+
17
+ /**
18
+ * The Dialog component.
19
+ */
20
+ export const BaseDialog: React.FC<DialogProps> & {
21
+ displayName?: string;
22
+ className?: string;
23
+ } = (props) => {
24
+ const {
25
+ children,
26
+ defaultOpen,
27
+ hasCloseButton = DEFAULT_PROPS.hasCloseButton,
28
+ initialFocus,
29
+ isOpen,
30
+ onOpen,
31
+ size,
32
+ } = props;
33
+
34
+ const dialog = useDialog({
35
+ defaultOpen,
36
+ hasCloseButton,
37
+ initialFocus,
38
+ isOpen,
39
+ onOpen,
40
+ size,
41
+ });
42
+
43
+ const [[trigger], [content]] = partitionComponents(
44
+ React.Children.toArray(children),
45
+ [isComponent('DialogTrigger'), isComponent('DialogContent')]
46
+ );
47
+
48
+ return (
49
+ <DialogContext.Provider value={dialog}>
50
+ {trigger}
51
+ {content}
52
+ </DialogContext.Provider>
53
+ );
54
+ };
55
+ BaseDialog.className = CLASSNAME;
56
+ BaseDialog.defaultProps = DEFAULT_PROPS;
57
+ BaseDialog.displayName = COMPONENT_NAME;
58
+
59
+ export const Dialog = Object.assign(BaseDialog, {
60
+ Trigger: DialogTrigger,
61
+ Content: DialogContent,
62
+ });
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import { DialogState } from './types';
3
+
4
+ export const DialogContext = React.createContext<DialogState | null>(null);
@@ -0,0 +1,5 @@
1
+ export * from './context';
2
+ export * from './types';
3
+ export * from './Dialog';
4
+ export * from './useDialog';
5
+ export * from './useDialogContext';
@@ -0,0 +1,56 @@
1
+ import { Dispatch, ReactNode, SetStateAction } from 'react';
2
+ import { ValueOf } from '@redsift/design-system';
3
+ import { useDialog } from './useDialog';
4
+
5
+ /**
6
+ * Context props.
7
+ */
8
+ export type DialogState =
9
+ | (ReturnType<typeof useDialog> & {
10
+ setLabelId: Dispatch<SetStateAction<string | undefined>>;
11
+ setDescriptionId: Dispatch<SetStateAction<string | undefined>>;
12
+ })
13
+ | null;
14
+
15
+ /**
16
+ * Component variant.
17
+ */
18
+ export const DialogSize = {
19
+ small: 'small',
20
+ medium: 'medium',
21
+ large: 'large',
22
+ } as const;
23
+ export type DialogSize = ValueOf<typeof DialogSize>;
24
+
25
+ /**
26
+ * Component props.
27
+ */
28
+ export interface DialogProps {
29
+ /** Children. Can only be DialogTrigger and DialogContent. */
30
+ children: ReactNode;
31
+ /**
32
+ * Default open status.
33
+ * Used for uncontrolled version.
34
+ */
35
+ defaultOpen?: boolean;
36
+ /** Whether the Close icon button is displayed or not. */
37
+ hasCloseButton?: boolean;
38
+ /** Which element to initially focus. Can be either a number (tabbable index), a ref to en element, or a shortcut pointing towards a section of the dialog. See the accessibility section in the documentation to know which one to use. */
39
+ initialFocus?:
40
+ | number
41
+ | React.MutableRefObject<HTMLElement | null>
42
+ | 'header'
43
+ | 'body'
44
+ | 'actions';
45
+ /**
46
+ * Whether the component is opened or not.
47
+ * Used for controlled version.
48
+ */
49
+ isOpen?: boolean;
50
+ /** Method to handle component change. */
51
+ onOpen?: (open: boolean) => void;
52
+ /** Dialog size. */
53
+ size?: DialogSize;
54
+ }
55
+
56
+ export type StyledDialogProps = DialogProps;
@@ -0,0 +1,87 @@
1
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
2
+ import {
3
+ useFloating,
4
+ useDismiss,
5
+ useRole,
6
+ useInteractions,
7
+ } from '@floating-ui/react';
8
+ import { DialogProps } from './types';
9
+
10
+ export function useDialog({
11
+ defaultOpen,
12
+ hasCloseButton,
13
+ initialFocus,
14
+ isOpen: propsIsOpen,
15
+ onOpen,
16
+ size,
17
+ }: Omit<DialogProps, 'children'>) {
18
+ const headerRef = useRef(null);
19
+ const bodyRef = useRef(null);
20
+ const actionsRef = useRef(null);
21
+ const [labelId, setLabelId] = React.useState<string | undefined>();
22
+ const [descriptionId, setDescriptionId] =
23
+ React.useState<string | undefined>();
24
+
25
+ const [isOpen, setIsOpen] = useState(propsIsOpen ?? defaultOpen);
26
+
27
+ useEffect(() => {
28
+ setIsOpen(propsIsOpen ?? defaultOpen);
29
+ }, [propsIsOpen, defaultOpen]);
30
+
31
+ const handleOpen = useCallback(
32
+ (collapsed: boolean) => {
33
+ if (onOpen) {
34
+ onOpen(collapsed);
35
+ }
36
+ if (propsIsOpen === undefined || propsIsOpen === null) {
37
+ setIsOpen(collapsed);
38
+ }
39
+ },
40
+ [onOpen]
41
+ );
42
+
43
+ const data = useFloating({
44
+ open: isOpen,
45
+ onOpenChange: handleOpen,
46
+ });
47
+
48
+ const context = data.context;
49
+
50
+ const dismiss = useDismiss(context, { outsidePressEvent: 'mousedown' });
51
+ const role = useRole(context);
52
+
53
+ const interactions = useInteractions([dismiss, role]);
54
+
55
+ return React.useMemo(
56
+ () => ({
57
+ isOpen,
58
+ handleOpen,
59
+ ...interactions,
60
+ ...data,
61
+ labelId,
62
+ descriptionId,
63
+ setLabelId,
64
+ setDescriptionId,
65
+ hasCloseButton,
66
+ initialFocus,
67
+ size,
68
+ headerRef,
69
+ bodyRef,
70
+ actionsRef,
71
+ }),
72
+ [
73
+ isOpen,
74
+ handleOpen,
75
+ interactions,
76
+ data,
77
+ labelId,
78
+ descriptionId,
79
+ hasCloseButton,
80
+ initialFocus,
81
+ size,
82
+ headerRef,
83
+ bodyRef,
84
+ actionsRef,
85
+ ]
86
+ );
87
+ }
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { DialogContext } from './context';
3
+
4
+ export const useDialogContext = () => {
5
+ const context = React.useContext(DialogContext);
6
+
7
+ if (context == null) {
8
+ throw new Error('Dialog components must be wrapped in <Dialog />');
9
+ }
10
+
11
+ return context;
12
+ };