@squiz/resource-browser 1.32.1-alpha.12

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 (160) hide show
  1. package/.storybook/main.ts +23 -0
  2. package/.storybook/preview-head.html +15 -0
  3. package/.storybook/preview.ts +16 -0
  4. package/build.js +21 -0
  5. package/jest.config.ts +18 -0
  6. package/lib/Icons/Generics/ArrowDown.d.ts +15 -0
  7. package/lib/Icons/Generics/ArrowDown.js +23 -0
  8. package/lib/Icons/Generics/ArrowRight.d.ts +15 -0
  9. package/lib/Icons/Generics/ArrowRight.js +23 -0
  10. package/lib/Icons/Generics/Close.d.ts +15 -0
  11. package/lib/Icons/Generics/Close.js +23 -0
  12. package/lib/Icons/Generics/GenericIconMap.d.ts +10 -0
  13. package/lib/Icons/Generics/GenericIconMap.js +14 -0
  14. package/lib/Icons/Generics/ResourceSelect.d.ts +15 -0
  15. package/lib/Icons/Generics/ResourceSelect.js +28 -0
  16. package/lib/Icons/Generics/Root.d.ts +15 -0
  17. package/lib/Icons/Generics/Root.js +23 -0
  18. package/lib/Icons/Generics/Selected.d.ts +15 -0
  19. package/lib/Icons/Generics/Selected.js +23 -0
  20. package/lib/Icons/Generics/index.d.ts +6 -0
  21. package/lib/Icons/Generics/index.js +19 -0
  22. package/lib/Icons/Icon.d.ts +47 -0
  23. package/lib/Icons/Icon.js +44 -0
  24. package/lib/Icons/MatrixResources/Audio.d.ts +15 -0
  25. package/lib/Icons/MatrixResources/Audio.js +28 -0
  26. package/lib/Icons/MatrixResources/Excel.d.ts +15 -0
  27. package/lib/Icons/MatrixResources/Excel.js +27 -0
  28. package/lib/Icons/MatrixResources/Folder.d.ts +15 -0
  29. package/lib/Icons/MatrixResources/Folder.js +24 -0
  30. package/lib/Icons/MatrixResources/GenericFile.d.ts +15 -0
  31. package/lib/Icons/MatrixResources/GenericFile.js +28 -0
  32. package/lib/Icons/MatrixResources/Image.d.ts +15 -0
  33. package/lib/Icons/MatrixResources/Image.js +26 -0
  34. package/lib/Icons/MatrixResources/MatrixResourceMap.d.ts +15 -0
  35. package/lib/Icons/MatrixResources/MatrixResourceMap.js +19 -0
  36. package/lib/Icons/MatrixResources/Page.d.ts +15 -0
  37. package/lib/Icons/MatrixResources/Page.js +30 -0
  38. package/lib/Icons/MatrixResources/Pdf.d.ts +15 -0
  39. package/lib/Icons/MatrixResources/Pdf.js +31 -0
  40. package/lib/Icons/MatrixResources/Powerpoint.d.ts +15 -0
  41. package/lib/Icons/MatrixResources/Powerpoint.js +28 -0
  42. package/lib/Icons/MatrixResources/Site.d.ts +15 -0
  43. package/lib/Icons/MatrixResources/Site.js +30 -0
  44. package/lib/Icons/MatrixResources/Video.d.ts +15 -0
  45. package/lib/Icons/MatrixResources/Video.js +24 -0
  46. package/lib/Icons/MatrixResources/Word.d.ts +17 -0
  47. package/lib/Icons/MatrixResources/Word.js +28 -0
  48. package/lib/Icons/MatrixResources/index.d.ts +11 -0
  49. package/lib/Icons/MatrixResources/index.js +29 -0
  50. package/lib/Modal/Modal.d.ts +11 -0
  51. package/lib/Modal/Modal.js +46 -0
  52. package/lib/Modal/ModalOpeningButton.d.ts +10 -0
  53. package/lib/Modal/ModalOpeningButton.js +13 -0
  54. package/lib/Modal/ModalTrigger.d.ts +9 -0
  55. package/lib/Modal/ModalTrigger.js +24 -0
  56. package/lib/PreviewPanel/PreviewModal.d.ts +11 -0
  57. package/lib/PreviewPanel/PreviewModal.js +81 -0
  58. package/lib/PreviewPanel/PreviewPanel.d.ts +16 -0
  59. package/lib/PreviewPanel/PreviewPanel.js +87 -0
  60. package/lib/PreviewPanel/details/MatrixResource.d.ts +12 -0
  61. package/lib/PreviewPanel/details/MatrixResource.js +41 -0
  62. package/lib/ResourceBreadcrumb/ResourceBreadcrumb.d.ts +9 -0
  63. package/lib/ResourceBreadcrumb/ResourceBreadcrumb.js +20 -0
  64. package/lib/ResourceItem/ResourceItem.d.ts +19 -0
  65. package/lib/ResourceItem/ResourceItem.js +26 -0
  66. package/lib/ResourceList/ResourceList.d.ts +14 -0
  67. package/lib/ResourceList/ResourceList.js +51 -0
  68. package/lib/ResourcePickerContainer/ResourcePickerContainer.d.ts +15 -0
  69. package/lib/ResourcePickerContainer/ResourcePickerContainer.js +145 -0
  70. package/lib/Skeleton/List/SkeletonList.d.ts +6 -0
  71. package/lib/Skeleton/List/SkeletonList.js +13 -0
  72. package/lib/Skeleton/ListItem/SkeletonListItem.d.ts +2 -0
  73. package/lib/Skeleton/ListItem/SkeletonListItem.js +15 -0
  74. package/lib/SourceDropdown/SourceDropdown.d.ts +9 -0
  75. package/lib/SourceDropdown/SourceDropdown.js +106 -0
  76. package/lib/SourceList/SourceList.d.ts +14 -0
  77. package/lib/SourceList/SourceList.js +58 -0
  78. package/lib/Spinner/Spinner.d.ts +8 -0
  79. package/lib/Spinner/Spinner.js +12 -0
  80. package/lib/index.css +968 -0
  81. package/lib/index.d.ts +37 -0
  82. package/lib/index.js +15 -0
  83. package/lib/uuid.d.ts +1 -0
  84. package/lib/uuid.js +8 -0
  85. package/package.json +74 -0
  86. package/postcss.config.js +11 -0
  87. package/src/Icons/Generics/ArrowDown.tsx +27 -0
  88. package/src/Icons/Generics/ArrowRight.tsx +27 -0
  89. package/src/Icons/Generics/Close.tsx +26 -0
  90. package/src/Icons/Generics/GenericIconMap.ts +14 -0
  91. package/src/Icons/Generics/ResourceSelect.tsx +40 -0
  92. package/src/Icons/Generics/Root.tsx +24 -0
  93. package/src/Icons/Generics/Selected.tsx +27 -0
  94. package/src/Icons/Generics/index.tsx +7 -0
  95. package/src/Icons/Icon.spec.tsx +62 -0
  96. package/src/Icons/Icon.stories.tsx +105 -0
  97. package/src/Icons/Icon.tsx +61 -0
  98. package/src/Icons/MatrixResources/Audio.tsx +30 -0
  99. package/src/Icons/MatrixResources/Excel.tsx +29 -0
  100. package/src/Icons/MatrixResources/Folder.tsx +29 -0
  101. package/src/Icons/MatrixResources/GenericFile.tsx +34 -0
  102. package/src/Icons/MatrixResources/Image.tsx +36 -0
  103. package/src/Icons/MatrixResources/MatrixResourceMap.ts +19 -0
  104. package/src/Icons/MatrixResources/Page.tsx +33 -0
  105. package/src/Icons/MatrixResources/Pdf.tsx +34 -0
  106. package/src/Icons/MatrixResources/Powerpoint.tsx +34 -0
  107. package/src/Icons/MatrixResources/Site.tsx +37 -0
  108. package/src/Icons/MatrixResources/Video.tsx +27 -0
  109. package/src/Icons/MatrixResources/Word.tsx +30 -0
  110. package/src/Icons/MatrixResources/index.tsx +12 -0
  111. package/src/Modal/Modal.spec.tsx +244 -0
  112. package/src/Modal/Modal.tsx +58 -0
  113. package/src/Modal/ModalContainer.stories.tsx +33 -0
  114. package/src/Modal/ModalOpeningButton.tsx +20 -0
  115. package/src/Modal/ModalTrigger.tsx +45 -0
  116. package/src/PreviewPanel/PreviewModal.spec.tsx +164 -0
  117. package/src/PreviewPanel/PreviewModal.tsx +92 -0
  118. package/src/PreviewPanel/PreviewPanel.spec.tsx +197 -0
  119. package/src/PreviewPanel/PreviewPanel.stories.tsx +61 -0
  120. package/src/PreviewPanel/PreviewPanel.tsx +123 -0
  121. package/src/PreviewPanel/details/MatrixResource.tsx +59 -0
  122. package/src/ResourceBreadcrumb/ResourceBreadcrumb.spec.tsx +76 -0
  123. package/src/ResourceBreadcrumb/ResourceBreadcrumb.stories.tsx +24 -0
  124. package/src/ResourceBreadcrumb/ResourceBreadcrumb.tsx +39 -0
  125. package/src/ResourceBreadcrumb/sample-hierarchy.json +23 -0
  126. package/src/ResourceItem/ResourceItem.spec.tsx +69 -0
  127. package/src/ResourceItem/ResourceItem.tsx +82 -0
  128. package/src/ResourceList/ResourceList.spec.tsx +196 -0
  129. package/src/ResourceList/ResourceList.stories.tsx +40 -0
  130. package/src/ResourceList/ResourceList.tsx +74 -0
  131. package/src/ResourceList/sample-resources.json +75 -0
  132. package/src/ResourcePickerContainer/ResourcePickerContainer.spec.tsx +706 -0
  133. package/src/ResourcePickerContainer/ResourcePickerContainer.stories.tsx +56 -0
  134. package/src/ResourcePickerContainer/ResourcePickerContainer.tsx +224 -0
  135. package/src/Skeleton/List/SkeletonList.spec.tsx +18 -0
  136. package/src/Skeleton/List/SkeletonList.stories.tsx +15 -0
  137. package/src/Skeleton/List/SkeletonList.tsx +16 -0
  138. package/src/Skeleton/ListItem/SkeletonListItem.stories.tsx +15 -0
  139. package/src/Skeleton/ListItem/SkeletonListItem.tsx +14 -0
  140. package/src/SourceDropdown/SourceDropdown.spec.tsx +263 -0
  141. package/src/SourceDropdown/SourceDropdown.stories.tsx +36 -0
  142. package/src/SourceDropdown/SourceDropdown.tsx +175 -0
  143. package/src/SourceDropdown/sample-sources.json +110 -0
  144. package/src/SourceList/SourceList.spec.tsx +224 -0
  145. package/src/SourceList/SourceList.stories.tsx +40 -0
  146. package/src/SourceList/SourceList.tsx +93 -0
  147. package/src/SourceList/sample-sources.json +110 -0
  148. package/src/Spinner/Spinner.spec.tsx +18 -0
  149. package/src/Spinner/Spinner.stories.tsx +26 -0
  150. package/src/Spinner/Spinner.tsx +18 -0
  151. package/src/Spinner/_spinner.scss +11 -0
  152. package/src/__mocks__/JestHelpers.ts +65 -0
  153. package/src/__mocks__/jestHelpers.spec.ts +38 -0
  154. package/src/__mocks__/styleMock.ts +1 -0
  155. package/src/index.scss +7 -0
  156. package/src/index.stories.tsx +70 -0
  157. package/src/index.tsx +71 -0
  158. package/src/uuid.ts +7 -0
  159. package/tailwind.config.cjs +84 -0
  160. package/tsconfig.json +22 -0
@@ -0,0 +1,29 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Renders the Folder icon
5
+ * @param {React.SVGProps<SVGSVGElement>} props - The props for the Folder component
6
+ * @returns {JSX.Element} - The Folder component
7
+ * @example
8
+ * <Folder />
9
+ * @example
10
+ * <Folder height={24} width={24} />
11
+ * @example
12
+ * <Folder className="custom-class" />
13
+ */
14
+ export default function Folder({ isDecorative, ...props }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
15
+ return (
16
+ <svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" {...props}>
17
+ <path
18
+ d="m1 5c0-1.10457.89543-2 2-2h8.1177c.6752 0 1.3049.34077 1.6743.9061l2.208 3.37961-14 3.71429z"
19
+ fill="#efb324"
20
+ />
21
+ <path
22
+ d="m3 6.5c-.82843 0-1.5.67157-1.5 1.5v11.5c0 .5523.44771 1 1 1h18.5c.8284 0 1.5-.6716 1.5-1.5v-11c0-.82843-.6716-1.5-1.5-1.5h-7.5106-2.8369z"
23
+ fill="#fbefd2"
24
+ stroke="#f5d584"
25
+ />
26
+ {!isDecorative && <title>folder icon</title>}
27
+ </svg>
28
+ );
29
+ }
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Renders the GenericFile icon
5
+ * @param {React.SVGProps<SVGSVGElement>} props - The props for the GenericFile component
6
+ * @returns {JSX.Element} - The GenericFile component
7
+ * @example
8
+ * <GenericFile />
9
+ * @example
10
+ * <GenericFile height={24} width={24} />
11
+ * @example
12
+ * <GenericFile className="custom-class" />
13
+ */
14
+ export default function GenericFile({
15
+ isDecorative,
16
+ ...props
17
+ }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
18
+ return (
19
+ <svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" {...props}>
20
+ <path
21
+ d="m15.9393 3.23223-.3515.35154.3515-.35154c-.4688-.46884-1.1047-.73223-1.7677-.73223h-8.1716c-.82843 0-1.5.67157-1.5 1.5v16c0 .8284.67157 1.5 1.5 1.5h12c.8284 0 1.5-.6716 1.5-1.5v-12.17157c0-.66304-.2634-1.29893-.7322-1.76777z"
22
+ fill="#fff"
23
+ stroke="#74b1e6"
24
+ />
25
+ <path d="m14.5 3v3.5c0 .55228.4477 1 1 1h3.5" stroke="#74b1e6" />
26
+ <path d="m14.5 7.5v-5l5 5z" fill="#cce2f6" />
27
+ <g stroke="#74b1e6">
28
+ <path d="m15.9393 3.23223-.3515.35154.3515-.35154c-.4688-.46884-1.1047-.73223-1.7677-.73223h-8.1716c-.82843 0-1.5.67157-1.5 1.5v16c0 .8284.67157 1.5 1.5 1.5h12c.8284 0 1.5-.6716 1.5-1.5v-12.17157c0-.66304-.2634-1.29893-.7322-1.76777z" />
29
+ <path d="m14.5 3v3.5c0 .55228.4477 1 1 1h3.5" />
30
+ </g>
31
+ {!isDecorative && <title>generic file icon</title>}
32
+ </svg>
33
+ );
34
+ }
@@ -0,0 +1,36 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Renders the Image icon
5
+ * @param {React.SVGProps<SVGSVGElement>} props - The props for the Image component
6
+ * @returns {JSX.Element} - The Image component
7
+ * @example
8
+ * <Image />
9
+ * @example
10
+ * <Image height={24} width={24} />
11
+ * @example
12
+ * <Image className="custom-class" />
13
+ */
14
+ export default function Image({ isDecorative, ...props }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
15
+ return (
16
+ <svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" {...props}>
17
+ <path
18
+ d="m21 17.5h.5v-.5-13c0-.82843-.6716-1.5-1.5-1.5h-16c-.82843 0-1.5.67157-1.5 1.5v13 .5h.5z"
19
+ fill="#cce2f6"
20
+ stroke="#74b1e6"
21
+ />
22
+ <circle cx="15.5" cy="8.5" fill="#fbefd2" r="3" stroke="#f0c14f" />
23
+ <path
24
+ d="m21.3749 16.0025.1251.1418v.189 3.6667c0 .8284-.6716 1.5-1.5 1.5h-8.5-1.1506l.7851-.8412 6.2481-6.6944c.6044-.6476 1.6353-.6331 2.2214.031z"
25
+ fill="#cee9d8"
26
+ stroke="#79c292"
27
+ />
28
+ <path
29
+ d="m2.62777 13.6262-.12777.1424v.1914 6.04c0 .8284.67157 1.5 1.5 1.5h13 1.0974l-.7201-.8281-9.25697-10.6455c-.59189-.68068-1.64634-.68877-2.2486-.0173z"
30
+ fill="#79c292"
31
+ stroke="#098934"
32
+ />
33
+ {!isDecorative && <title>image icon</title>}
34
+ </svg>
35
+ );
36
+ }
@@ -0,0 +1,19 @@
1
+ import { Audio, Excel, Folder, GenericFile, Image, Page, Pdf, Powerpoint, Site, Video, Word } from '.';
2
+
3
+ // Define our map of matrix types to icons
4
+ const MatrixResourceMap = {
5
+ audio: Audio,
6
+ excel: Excel,
7
+ folder: Folder,
8
+ generic_file: GenericFile,
9
+ image: Image,
10
+ page_standard: Page,
11
+ pdf: Pdf,
12
+ powerpoint: Powerpoint,
13
+ site: Site,
14
+ video: Video,
15
+ word: Word,
16
+ };
17
+
18
+ // Export our map
19
+ export default MatrixResourceMap;
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Renders the Page icon
5
+ * @param {React.SVGProps<SVGSVGElement>} props - The props for the Page component
6
+ * @returns {JSX.Element} - The Page component
7
+ * @example
8
+ * <Page />
9
+ * @example
10
+ * <Page height={24} width={24} />
11
+ * @example
12
+ * <Page className="custom-class" />
13
+ */
14
+ export default function Page({ isDecorative, ...props }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
15
+ return (
16
+ <svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" {...props}>
17
+ <path
18
+ d="m4 1.5c-.82843 0-1.5.67157-1.5 1.5v18c0 .8284.67157 1.5 1.5 1.5h16c.8284 0 1.5-.6716 1.5-1.5v-18c0-.82843-.6716-1.5-1.5-1.5z"
19
+ fill="#cee9d8"
20
+ stroke="#79c292"
21
+ />
22
+ <g fill="#098934">
23
+ <rect height="1" rx=".5" width="6" x="6" y="8" />
24
+ <rect height="1" rx=".5" width="6" x="6" y="10" />
25
+ <rect height="1" rx=".5" width="12" x="6" y="12" />
26
+ <rect height="1" rx=".5" width="12" x="6" y="14" />
27
+ <rect height="1" rx=".5" width="12" x="6" y="16" />
28
+ <rect height="1" rx=".5" width="12" x="6" y="18" />
29
+ </g>
30
+ {!isDecorative && <title>page standard icon</title>}
31
+ </svg>
32
+ );
33
+ }
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Renders the Pdf icon
5
+ * @param {React.SVGProps<SVGSVGElement>} props - The props for the Pdf component
6
+ * @returns {JSX.Element} - The Pdf component
7
+ * @example
8
+ * <Pdf />
9
+ * @example
10
+ * <Pdf height={24} width={24} />
11
+ * @example
12
+ * <Pdf className="custom-class" />
13
+ */
14
+ export default function Pdf({ isDecorative, ...props }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
15
+ return (
16
+ <svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" {...props}>
17
+ <path
18
+ d="m15.9393 3.23223-.3515.35154.3515-.35154c-.4688-.46884-1.1047-.73223-1.7677-.73223h-8.1716c-.82843 0-1.5.67157-1.5 1.5v16c0 .8284.67157 1.5 1.5 1.5h12c.8284 0 1.5-.6716 1.5-1.5v-12.17157c0-.66304-.2634-1.29893-.7322-1.76777z"
19
+ fill="#f6d2d1"
20
+ stroke="#e88483"
21
+ />
22
+ <path d="m14.5 3v3.5c0 .55228.4477 1 1 1h3.5" stroke="#e88483" />
23
+ <rect fill="#d72321" height="9" rx="2" width="17" y="11" />
24
+ <rect fill="#d72321" height="7" rx="1" width="13" x="2" y="12" />
25
+ <g stroke="#fff" strokeLinecap="round">
26
+ <path d="m3.5 17.5v-2m0 0v-1.8c0-.1105.08954-.2.2-.2h1c.55228 0 1 .4477 1 1 0 .5523-.44772 1-1 1z" />
27
+ <path d="m7.5 17.3v-3.6c0-.1105.08954-.2.2-.2h.3c.82843 0 1.5.6716 1.5 1.5v1c0 .8284-.67157 1.5-1.5 1.5h-.3c-.11046 0-.2-.0895-.2-.2z" />
28
+ <path d="m11.5 17.5v-3.8c0-.1105.0895-.2.2-.2h1.8" />
29
+ <path d="m11.5 15.5h1" />
30
+ </g>
31
+ {!isDecorative && <title>pdf icon</title>}
32
+ </svg>
33
+ );
34
+ }
@@ -0,0 +1,34 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Renders the Powerpoint icon
5
+ * @param {React.SVGProps<SVGSVGElement>} props - The props for the Powerpoint component
6
+ * @returns {JSX.Element} - The Powerpoint component
7
+ * @example
8
+ * <Powerpoint />
9
+ * @example
10
+ * <Powerpoint height={24} width={24} />
11
+ * @example
12
+ * <Powerpoint className="custom-class" />
13
+ */
14
+ export default function Powerpoint({
15
+ isDecorative,
16
+ ...props
17
+ }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
18
+ return (
19
+ <svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" {...props}>
20
+ <path
21
+ d="m15.9393 3.23223-.3515.35154.3515-.35154c-.4688-.46884-1.1047-.73223-1.7677-.73223h-8.1716c-.82843 0-1.5.67157-1.5 1.5v16c0 .8284.67157 1.5 1.5 1.5h12c.8284 0 1.5-.6716 1.5-1.5v-12.17157c0-.66304-.2634-1.29893-.7322-1.76777z"
22
+ fill="#e7e7e7"
23
+ stroke="#bcbcbc"
24
+ />
25
+ <path d="m14.5 3v3.5c0 .55228.4477 1 1 1h3.5" stroke="#bcbcbc" />
26
+ <rect fill="#e96900" height="12" rx="2" width="12" y="8" />
27
+ <g stroke="#fff" strokeLinecap="round">
28
+ <path d="m3.5 11.5v5" />
29
+ <path d="m3.5 11.5h3.5c.33333 0 1.5 0 1.5 1.5s-1.16667 1.5-1.5 1.5h-3" />
30
+ </g>
31
+ {!isDecorative && <title>powerpoint icon</title>}
32
+ </svg>
33
+ );
34
+ }
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Renders the Site icon
5
+ * @param {React.SVGProps<SVGSVGElement>} props - The props for the Site component
6
+ * @returns {JSX.Element} - The Site component
7
+ * @example
8
+ * <Site />
9
+ * @example
10
+ * <Site height={24} width={24} />
11
+ * @example
12
+ * <Site className="custom-class" />
13
+ */
14
+ export default function Site({ isDecorative, ...props }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
15
+ return (
16
+ <svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" {...props}>
17
+ <circle cx="12" cy="12" fill="#cce2f5" r="10.5" stroke="#7db4e3" />
18
+ <path
19
+ d="m19.5 12c0 2.9434-.8684 5.5878-2.2475 7.4841-1.3795 1.8968-3.2427 3.0159-5.2525 3.0159-2.00976 0-3.87299-1.1191-5.25249-3.0159-1.37913-1.8963-2.24751-4.5407-2.24751-7.4841 0-2.94337.86838-5.58777 2.24751-7.48409 1.3795-1.8968 3.24273-3.01591 5.25249-3.01591 2.0098 0 3.873 1.11911 5.2525 3.01591 1.3791 1.89632 2.2475 4.54072 2.2475 7.48409z"
20
+ fill="#cce2f5"
21
+ stroke="#7db4e3"
22
+ />
23
+ <path
24
+ d="m14.5 12c0 3.0063-.3329 5.7099-.8611 7.6466-.265.9719-.5718 1.7223-.8914 2.218-.334.5181-.5981.6354-.7475.6354s-.4135-.1173-.7475-.6354c-.3196-.4957-.6264-1.2461-.8914-2.218-.52822-1.9367-.8611-4.6403-.8611-7.6466 0-3.00626.33288-5.70993.8611-7.64662.265-.97192.5718-1.72231.8914-2.21799.334-.51806.5981-.63539.7475-.63539s.4135.11733.7475.63539c.3196.49568.6264 1.24607.8914 2.21799.5282 1.93669.8611 4.64036.8611 7.64662z"
25
+ fill="#cce2f5"
26
+ stroke="#7db4e3"
27
+ />
28
+ <g fill="#7db4e3">
29
+ <path d="m2 14h20v1h-20z" />
30
+ <path d="m2 9h20v1h-20z" />
31
+ <path d="m5 4 2.5-1-.5 1h10l-.5-1 2.5 1 .5 1h-15z" />
32
+ <path d="m4 19h16l-.5 1-3 1 .5-1h-10l.5 1-3-1z" />
33
+ </g>
34
+ {!isDecorative && <title>site icon</title>}
35
+ </svg>
36
+ );
37
+ }
@@ -0,0 +1,27 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Renders the Video icon
5
+ * @param {React.SVGProps<SVGSVGElement>} props - The props for the Video component
6
+ * @returns {JSX.Element} - The Video component
7
+ * @example
8
+ * <Video />
9
+ * @example
10
+ * <Video height={24} width={24} />
11
+ * @example
12
+ * <Video className="custom-class" />
13
+ */
14
+ export default function Video({ isDecorative, ...props }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
15
+ return (
16
+ <svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" {...props}>
17
+ <rect fill="#e0d9ef" height="15" rx="1.5" stroke="#ac98d5" width="21" x="1.5" y="4.5" />
18
+ <path
19
+ d="m9.5 15.2516v-6.50318c0-.38671.42019-.62704.7535-.43097l5.5277 3.25155c.3287.1934.3287.6686 0 .862l-5.5277 3.2516c-.33331.196-.7535-.0443-.7535-.431z"
20
+ fill="#ac98d5"
21
+ stroke="#6b47b4"
22
+ strokeLinecap="round"
23
+ />
24
+ {!isDecorative && <title>video icon</title>}
25
+ </svg>
26
+ );
27
+ }
@@ -0,0 +1,30 @@
1
+ import React from 'react';
2
+
3
+ /**
4
+ * Renders the Word icon
5
+ * @param {React.SVGProps<SVGSVGElement>} props - The props for the Word component
6
+ * @returns {JSX.Element} - The Word component
7
+ * @example
8
+ * <Word />
9
+ * @example
10
+ * <Word isDecorative={false} />
11
+ * @example
12
+ * <Word height={24} width={24} />
13
+ * @example
14
+ * <Word className="custom-class" />
15
+ */
16
+ export default function Word({ isDecorative, ...props }: { isDecorative: boolean } & React.SVGProps<SVGSVGElement>) {
17
+ return (
18
+ <svg fill="none" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg" {...props}>
19
+ <path
20
+ d="m15.9393 3.23223-.3515.35154.3515-.35154c-.4688-.46884-1.1047-.73223-1.7677-.73223h-8.1716c-.82843 0-1.5.67157-1.5 1.5v16c0 .8284.67157 1.5 1.5 1.5h12c.8284 0 1.5-.6716 1.5-1.5v-12.17157c0-.66304-.2634-1.29893-.7322-1.76777z"
21
+ fill="#e7e7e7"
22
+ stroke="#bcbcbc"
23
+ />
24
+ <path d="m14.5 3v3.5c0 .55228.4477 1 1 1h3.5" stroke="#bcbcbc" />
25
+ <rect fill="#0774d2" height="12" rx="2" width="12" y="8" />
26
+ <path d="m2.5 11.5 1.5 5 2-5 2 5 1.5-5" stroke="#fff" strokeLinecap="round" strokeLinejoin="round" />
27
+ {!isDecorative && <title>word icon</title>}
28
+ </svg>
29
+ );
30
+ }
@@ -0,0 +1,12 @@
1
+ // Export all the icons from the MatrixResources folder
2
+ export { default as Audio } from './Audio';
3
+ export { default as Excel } from './Excel';
4
+ export { default as Folder } from './Folder';
5
+ export { default as GenericFile } from './GenericFile';
6
+ export { default as Image } from './Image';
7
+ export { default as Page } from './Page';
8
+ export { default as Pdf } from './Pdf';
9
+ export { default as Powerpoint } from './Powerpoint';
10
+ export { default as Site } from './Site';
11
+ export { default as Video } from './Video';
12
+ export { default as Word } from './Word';
@@ -0,0 +1,244 @@
1
+ import React from 'react';
2
+ import { screen, render, waitFor } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+
5
+ import ModalTrigger from './ModalTrigger';
6
+
7
+ describe('Modal', () => {
8
+ it('Modal is closed by default', async () => {
9
+ render(
10
+ <div>
11
+ <ModalTrigger label={'Open testing modal'} showLabel>
12
+ {(onClose, titleProps) => (
13
+ <div data-testingid="modal">
14
+ <div {...titleProps}>Testing</div>
15
+ <button type="button" onClick={onClose}>
16
+ Close
17
+ </button>
18
+ </div>
19
+ )}
20
+ </ModalTrigger>
21
+ <div style={{ height: '150vh' }} />
22
+ </div>,
23
+ );
24
+ expect(screen.queryByTestId('modal')).toBeFalsy();
25
+ });
26
+
27
+ it('Modal opens when triggered', async () => {
28
+ render(
29
+ <div>
30
+ <ModalTrigger label={'Open testing modal'} showLabel>
31
+ {(onClose, titleProps) => (
32
+ <div data-testid="modal">
33
+ <div {...titleProps}>Testing</div>
34
+ <button type="button" onClick={onClose}>
35
+ Close
36
+ </button>
37
+ </div>
38
+ )}
39
+ </ModalTrigger>
40
+ <div style={{ height: '150vh' }} />
41
+ </div>,
42
+ );
43
+
44
+ const user = userEvent.setup();
45
+ user.click(screen.getByText('Open testing modal'));
46
+
47
+ await waitFor(() => {
48
+ expect(screen.queryByTestId('modal')).toBeTruthy();
49
+ });
50
+ });
51
+
52
+ it('Clicking outside the modal closes it', async () => {
53
+ render(
54
+ <div data-testid="outside">
55
+ <ModalTrigger label={'Open testing modal'} showLabel>
56
+ {(onClose, titleProps) => (
57
+ <div data-testid="modal">
58
+ <div {...titleProps}>Testing</div>
59
+ <button type="button" onClick={onClose}>
60
+ Close
61
+ </button>
62
+ </div>
63
+ )}
64
+ </ModalTrigger>
65
+ <div style={{ height: '150vh' }} />
66
+ </div>,
67
+ );
68
+
69
+ const user = userEvent.setup();
70
+ user.click(screen.getByText('Open testing modal'));
71
+
72
+ await waitFor(() => {
73
+ expect(screen.queryByTestId('modal')).toBeTruthy();
74
+ });
75
+
76
+ user.click(screen.getByTestId('outside'));
77
+
78
+ await waitFor(() => {
79
+ expect(screen.queryByTestId('modal')).toBeFalsy();
80
+ });
81
+ });
82
+
83
+ it('ESC closes modal', async () => {
84
+ render(
85
+ <div data-testid="outside">
86
+ <ModalTrigger label={'Open testing modal'} showLabel>
87
+ {(onClose, titleProps) => (
88
+ <div data-testid="modal">
89
+ <div {...titleProps}>Testing</div>
90
+ <button type="button" onClick={onClose}>
91
+ Close
92
+ </button>
93
+ </div>
94
+ )}
95
+ </ModalTrigger>
96
+ <div style={{ height: '150vh' }} />
97
+ </div>,
98
+ );
99
+
100
+ const user = userEvent.setup();
101
+ user.click(screen.getByText('Open testing modal'));
102
+
103
+ await waitFor(() => {
104
+ expect(screen.queryByTestId('modal')).toBeTruthy();
105
+ });
106
+
107
+ user.type(screen.getByTestId('modal'), '{escape}');
108
+
109
+ await waitFor(() => {
110
+ expect(screen.queryByTestId('modal')).toBeFalsy();
111
+ });
112
+ });
113
+
114
+ it('Invoking onClose function closes modal', async () => {
115
+ render(
116
+ <div>
117
+ <ModalTrigger label={'Open testing modal'} showLabel>
118
+ {(onClose, titleProps) => (
119
+ <div data-testid="modal">
120
+ <div {...titleProps}>Testing</div>
121
+ <button data-testid="closeButton" type="button" onClick={onClose}>
122
+ Close
123
+ </button>
124
+ </div>
125
+ )}
126
+ </ModalTrigger>
127
+ <div style={{ height: '150vh' }} />
128
+ </div>,
129
+ );
130
+
131
+ const user = userEvent.setup();
132
+ user.click(screen.getByText('Open testing modal'));
133
+
134
+ await waitFor(() => {
135
+ expect(screen.queryByTestId('modal')).toBeTruthy();
136
+ });
137
+
138
+ user.click(screen.getByTestId('closeButton'));
139
+
140
+ await waitFor(() => {
141
+ expect(screen.queryByTestId('modal')).toBeFalsy();
142
+ });
143
+ });
144
+
145
+ it('Focus is trapped within modal', async () => {
146
+ render(
147
+ <div>
148
+ <button />
149
+ <ModalTrigger label={'Open testing modal'} showLabel>
150
+ {(onClose, titleProps) => (
151
+ <div data-testid="modal">
152
+ <div {...titleProps}>Testing</div>
153
+ <button data-testid="closeButton" type="button" onClick={onClose}>
154
+ Close
155
+ </button>
156
+ </div>
157
+ )}
158
+ </ModalTrigger>
159
+ <button />
160
+ <div style={{ height: '150vh' }} />
161
+ </div>,
162
+ );
163
+
164
+ const user = userEvent.setup();
165
+ user.click(screen.getByText('Open testing modal'));
166
+
167
+ await waitFor(() => {
168
+ expect(screen.queryByTestId('modal')).toBeTruthy();
169
+ });
170
+
171
+ user.type(screen.getByTestId('modal'), '{tab}');
172
+ user.type(screen.getByTestId('modal'), '{tab}');
173
+
174
+ await waitFor(() => {
175
+ expect(screen.queryByTestId('closeButton')).toHaveFocus();
176
+ });
177
+ });
178
+
179
+ it('Focus should start on the dialog', async () => {
180
+ render(
181
+ <div>
182
+ <button />
183
+ <ModalTrigger label={'Open testing modal'} showLabel>
184
+ {(onClose, titleProps) => (
185
+ <div data-testid="modal">
186
+ <div data-testid="title" {...titleProps}>
187
+ Testing
188
+ </div>
189
+ <button data-testid="closeButton" type="button" onClick={onClose}>
190
+ Close
191
+ </button>
192
+ </div>
193
+ )}
194
+ </ModalTrigger>
195
+ <button />
196
+ <div style={{ height: '150vh' }} />
197
+ </div>,
198
+ );
199
+
200
+ const user = userEvent.setup();
201
+ user.click(screen.getByText('Open testing modal'));
202
+
203
+ await waitFor(() => {
204
+ expect(screen.queryByTestId('modal')).toBeTruthy();
205
+ expect(screen.queryByRole('dialog')).toHaveFocus();
206
+ });
207
+ });
208
+
209
+ it('Title props provides id for dialog link', async () => {
210
+ let titleId: string | undefined = '';
211
+ render(
212
+ <div>
213
+ <button />
214
+ <ModalTrigger label={'Open testing modal'} showLabel>
215
+ {(onClose, titleProps) => {
216
+ titleId = titleProps.id;
217
+
218
+ return (
219
+ <div data-testid="modal">
220
+ <div data-testid="title" {...titleProps}>
221
+ Testing
222
+ </div>
223
+ <button data-testid="closeButton" type="button" onClick={onClose}>
224
+ Close
225
+ </button>
226
+ </div>
227
+ );
228
+ }}
229
+ </ModalTrigger>
230
+ <button />
231
+ <div style={{ height: '150vh' }} />
232
+ </div>,
233
+ );
234
+
235
+ const user = userEvent.setup();
236
+ user.click(screen.getByText('Open testing modal'));
237
+
238
+ await waitFor(() => {
239
+ expect(screen.queryByTestId('modal')).toBeTruthy();
240
+ expect(screen.queryByTestId('title')).toHaveAttribute('id'); // Title was provided an id by 'titleProps'
241
+ expect(screen.queryByRole('dialog')).toHaveAttribute('aria-labelledby', titleId); // 'titleProps' id is added to the dialog label
242
+ });
243
+ });
244
+ });
@@ -0,0 +1,58 @@
1
+ import React, { useRef } from 'react';
2
+
3
+ import { Overlay, useModalOverlay, useDialog } from 'react-aria';
4
+ import { OverlayTriggerState } from 'react-stately';
5
+ import { DOMAttributes, FocusableElement } from '@react-types/shared';
6
+
7
+ /*
8
+ This has to be a separate element otherwise the focus trap fails. Assuming this is because it needs
9
+ to fit inside the 'Overlay' as a form of context.
10
+ */
11
+ function ModalContent({
12
+ children,
13
+ ...props
14
+ }: {
15
+ children: (titleProps: DOMAttributes<FocusableElement>) => React.ReactElement;
16
+ }) {
17
+ const ref = useRef<HTMLDivElement>(null);
18
+ const { dialogProps, titleProps } = useDialog(props, ref);
19
+
20
+ return (
21
+ <div
22
+ {...dialogProps}
23
+ ref={ref}
24
+ className="z-50 relative bg-white rounded-lg h-screen lg:h-[calc(100vh-3.5rem)] w-screen max-w-screen-lg"
25
+ >
26
+ {children(titleProps)}
27
+ </div>
28
+ );
29
+ }
30
+
31
+ export type ModalProps = {
32
+ isDismissable?: boolean;
33
+ state: OverlayTriggerState;
34
+ overlayProps: DOMAttributes<FocusableElement>;
35
+ children: (titleProps: DOMAttributes<FocusableElement>) => React.ReactElement; // Returns the title props from the 'ModalContent'
36
+ };
37
+
38
+ function Modal({ isDismissable, state, overlayProps, children, ...props }: ModalProps) {
39
+ const ref = useRef<HTMLDivElement>(null);
40
+ const { modalProps, underlayProps } = useModalOverlay({ isDismissable, ...props }, state, ref);
41
+
42
+ return (
43
+ <Overlay>
44
+ <div className="squiz-rb-scope">
45
+ <div
46
+ {...underlayProps}
47
+ className="h-full z-[9999] fixed inset-0 before:z-40 before:fixed before:inset-0 before:bg-black before:bg-opacity-25"
48
+ >
49
+ <div {...modalProps} ref={ref} className="h-full flex items-center justify-center">
50
+ <ModalContent {...overlayProps}>{(titleProps) => children(titleProps)}</ModalContent>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ </Overlay>
55
+ );
56
+ }
57
+
58
+ export default Modal;
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import { StoryFn, Meta } from '@storybook/react';
3
+
4
+ import ModalTrigger from './ModalTrigger';
5
+
6
+ export default {
7
+ title: 'Modal',
8
+ component: ModalTrigger,
9
+ } as Meta<typeof ModalTrigger>;
10
+
11
+ const Template: StoryFn<typeof ModalTrigger> = ({ label }) => {
12
+ return (
13
+ <>
14
+ <ModalTrigger label={label} showLabel>
15
+ {(onClose, titleProps) => (
16
+ <div>
17
+ <div {...titleProps}>Testing</div>
18
+ <button type="button" onClick={onClose}>
19
+ Close
20
+ </button>
21
+ </div>
22
+ )}
23
+ </ModalTrigger>
24
+ <div style={{ height: '150vh' }} />
25
+ </>
26
+ );
27
+ };
28
+
29
+ export const Primary = Template.bind({});
30
+
31
+ Primary.args = {
32
+ label: 'Resource Browser Modal',
33
+ };