@stack-spot/portal-components 2.27.0 → 2.27.2

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 (250) hide show
  1. package/CHANGELOG.md +635 -621
  2. package/dist/components/AnimatedHeight.d.ts +1 -1
  3. package/dist/components/AnimatedHeight.js +26 -26
  4. package/dist/components/AsyncContent.d.ts +1 -1
  5. package/dist/components/AsyncContent.js +1 -1
  6. package/dist/components/BannerWarning.d.ts +1 -1
  7. package/dist/components/BannerWarning.js +1 -1
  8. package/dist/components/Breadcrumb/index.d.ts +2 -2
  9. package/dist/components/Breadcrumb/index.js +1 -1
  10. package/dist/components/Breadcrumb/styled.js +31 -31
  11. package/dist/components/ButtonLoading.d.ts +1 -1
  12. package/dist/components/ButtonLoading.js +1 -1
  13. package/dist/components/ChatBot.d.ts +1 -1
  14. package/dist/components/ChatBot.js +1 -1
  15. package/dist/components/ContentValidateFilter.d.ts +1 -1
  16. package/dist/components/ContentValidateFilter.js +1 -1
  17. package/dist/components/FadingOverflow.d.ts +1 -1
  18. package/dist/components/FadingOverflow.js +69 -69
  19. package/dist/components/FileTreeView/More.d.ts +1 -1
  20. package/dist/components/FileTreeView/More.js +1 -1
  21. package/dist/components/FileTreeView/index.d.ts +1 -1
  22. package/dist/components/FileTreeView/index.js +1 -1
  23. package/dist/components/InfiniteScroll.d.ts +1 -1
  24. package/dist/components/InfiniteScroll.js +1 -1
  25. package/dist/components/InfoMaintenanceBanner.d.ts +1 -1
  26. package/dist/components/InfoMaintenanceBanner.js +2 -2
  27. package/dist/components/LazyMarkdown/BlockquoteMd.d.ts +1 -1
  28. package/dist/components/LazyMarkdown/BlockquoteMd.js +1 -1
  29. package/dist/components/LazyMarkdown/CodeViewer.d.ts +1 -1
  30. package/dist/components/LazyMarkdown/CodeViewer.js +76 -76
  31. package/dist/components/LazyMarkdown/Markdown.d.ts +1 -1
  32. package/dist/components/LazyMarkdown/Markdown.js +1 -1
  33. package/dist/components/LazyMarkdown/MarkdownButton.d.ts +1 -1
  34. package/dist/components/LazyMarkdown/MarkdownButton.js +1 -1
  35. package/dist/components/LazyMarkdown/Video.d.ts +1 -1
  36. package/dist/components/LazyMarkdown/Video.js +1 -1
  37. package/dist/components/LazyMarkdown/index.d.ts +1 -1
  38. package/dist/components/LazyMarkdown/index.js +1 -1
  39. package/dist/components/Placeholder.d.ts +7 -3
  40. package/dist/components/Placeholder.d.ts.map +1 -1
  41. package/dist/components/Placeholder.js +3 -3
  42. package/dist/components/Placeholder.js.map +1 -1
  43. package/dist/components/ScrollView.js +16 -16
  44. package/dist/components/Select/BadgeItem.d.ts +1 -1
  45. package/dist/components/Select/BadgeItem.js +1 -1
  46. package/dist/components/Select/ClearInput.d.ts +1 -1
  47. package/dist/components/Select/ClearInput.js +1 -1
  48. package/dist/components/Select/CloseItem.d.ts +1 -1
  49. package/dist/components/Select/CloseItem.js +1 -1
  50. package/dist/components/Select/CreatableSelect.js +1 -1
  51. package/dist/components/Select/CustomMenu.d.ts +1 -1
  52. package/dist/components/Select/CustomMenu.js +1 -1
  53. package/dist/components/Select/LabelItem.d.ts +1 -1
  54. package/dist/components/Select/LabelItem.js +1 -1
  55. package/dist/components/Select/MultiValue.d.ts +1 -1
  56. package/dist/components/Select/MultiValue.js +1 -1
  57. package/dist/components/Select/SelectInfiniteScroll.d.ts +1 -1
  58. package/dist/components/Select/SelectInfiniteScroll.js +1 -1
  59. package/dist/components/Select/SelectSearch.d.ts +1 -1
  60. package/dist/components/Select/SelectSearch.js +1 -1
  61. package/dist/components/SelectionList.d.ts +1 -1
  62. package/dist/components/SelectionList.js +61 -61
  63. package/dist/components/StatusCircle.d.ts +1 -1
  64. package/dist/components/StatusCircle.js +6 -6
  65. package/dist/components/Stepper/Navigation.js +4 -4
  66. package/dist/components/Stepper/Step.js +3 -3
  67. package/dist/components/Stepper/Stepper.js +6 -6
  68. package/dist/components/Stepper/headers.js +22 -22
  69. package/dist/components/Table/HeaderItem.js +1 -1
  70. package/dist/components/Table/SettingsVerticalMenu.d.ts +1 -1
  71. package/dist/components/Table/SettingsVerticalMenu.js +1 -1
  72. package/dist/components/Table/StyledLinkTable.d.ts +1 -1
  73. package/dist/components/Table/StyledLinkTable.js +5 -5
  74. package/dist/components/Table/TableData.d.ts +1 -1
  75. package/dist/components/Table/TableData.js +25 -25
  76. package/dist/components/TimelineSection.d.ts +1 -1
  77. package/dist/components/TimelineSection.js +14 -14
  78. package/dist/components/error/ErrorFeedback.d.ts +1 -1
  79. package/dist/components/error/ErrorFeedback.js +35 -35
  80. package/dist/components/error/NotFound.d.ts +1 -1
  81. package/dist/components/error/NotFound.js +1 -1
  82. package/dist/components/error/UnderMaintenance.d.ts +1 -1
  83. package/dist/components/error/UnderMaintenance.js +1 -1
  84. package/dist/components/form/Form/Form.d.ts +1 -1
  85. package/dist/components/form/Form/Form.js +1 -1
  86. package/dist/components/form/Form/FormGroup.d.ts +2 -2
  87. package/dist/components/form/Form/FormGroup.js +1 -1
  88. package/dist/components/form/SearchInput.d.ts +1 -1
  89. package/dist/components/form/SearchInput.js +1 -1
  90. package/dist/components/form/Select/CustomSelect.d.ts +1 -1
  91. package/dist/components/form/Select/CustomSelect.js +1 -1
  92. package/dist/components/form/Select/DetailedSelect.d.ts +1 -1
  93. package/dist/components/form/Select/DetailedSelect.js +1 -1
  94. package/dist/components/form/Select/Select.d.ts +1 -1
  95. package/dist/components/form/Select/Select.js +1 -1
  96. package/dist/components/form/Select/styled.js +161 -161
  97. package/dist/components/form/Select/utils.js +1 -1
  98. package/dist/components/notification/NotificationComponent.d.ts +1 -1
  99. package/dist/components/notification/NotificationComponent.js +54 -54
  100. package/dist/components/notification/NotificationItem.d.ts +1 -1
  101. package/dist/components/notification/NotificationItem.d.ts.map +1 -1
  102. package/dist/components/notification/NotificationItem.js +11 -5
  103. package/dist/components/notification/NotificationItem.js.map +1 -1
  104. package/dist/components/notification/NotificationList.d.ts +1 -1
  105. package/dist/components/notification/NotificationList.d.ts.map +1 -1
  106. package/dist/components/notification/NotificationList.js +44 -44
  107. package/dist/components/notification/NotificationList.js.map +1 -1
  108. package/dist/components/notification/NotificationPlaceholder.d.ts +1 -1
  109. package/dist/components/notification/NotificationPlaceholder.d.ts.map +1 -1
  110. package/dist/components/notification/NotificationPlaceholder.js +2 -2
  111. package/dist/components/notification/NotificationPlaceholder.js.map +1 -1
  112. package/dist/containers/NotificationsPage.d.ts +1 -1
  113. package/dist/containers/NotificationsPage.d.ts.map +1 -1
  114. package/dist/containers/NotificationsPage.js +24 -11
  115. package/dist/containers/NotificationsPage.js.map +1 -1
  116. package/dist/context/anchor.d.ts +1 -1
  117. package/dist/context/anchor.js +1 -1
  118. package/dist/context/loading.d.ts +1 -1
  119. package/dist/context/loading.js +1 -1
  120. package/dist/context/notification/context.d.ts +1 -1
  121. package/dist/context/notification/context.js +1 -1
  122. package/dist/context/notification/types.d.ts +1 -0
  123. package/dist/context/notification/types.d.ts.map +1 -1
  124. package/dist/hooks/date.js +1 -1
  125. package/dist/hooks/service-now.js +28 -28
  126. package/dist/svg/AI.d.ts +1 -1
  127. package/dist/svg/AI.js +1 -1
  128. package/dist/svg/CS.d.ts +1 -1
  129. package/dist/svg/CS.js +1 -1
  130. package/dist/svg/EDP.d.ts +1 -1
  131. package/dist/svg/EDP.js +1 -1
  132. package/dist/svg/Forbidden.d.ts +1 -1
  133. package/dist/svg/Forbidden.js +1 -1
  134. package/dist/svg/GenericPlaceholder.d.ts +4 -2
  135. package/dist/svg/GenericPlaceholder.d.ts.map +1 -1
  136. package/dist/svg/GenericPlaceholder.js +2 -2
  137. package/dist/svg/GenericPlaceholder.js.map +1 -1
  138. package/dist/svg/HUB.d.ts +1 -1
  139. package/dist/svg/HUB.js +1 -1
  140. package/dist/svg/Logo.d.ts +1 -1
  141. package/dist/svg/Logo.js +1 -1
  142. package/dist/svg/MiniLogo.d.ts +1 -1
  143. package/dist/svg/MiniLogo.js +1 -1
  144. package/dist/svg/NotFound.d.ts +1 -1
  145. package/dist/svg/NotFound.js +1 -1
  146. package/dist/svg/ServerError.d.ts +1 -1
  147. package/dist/svg/ServerError.js +1 -1
  148. package/dist/svg/Unauthenticated.d.ts +1 -1
  149. package/dist/svg/Unauthenticated.js +1 -1
  150. package/package.json +6 -6
  151. package/readme.md +66 -66
  152. package/src/components/AnimatedHeight.tsx +174 -174
  153. package/src/components/AsyncContent.tsx +78 -78
  154. package/src/components/BannerWarning.tsx +91 -91
  155. package/src/components/Breadcrumb/index.tsx +76 -76
  156. package/src/components/Breadcrumb/styled.ts +37 -37
  157. package/src/components/ButtonLoading.tsx +29 -29
  158. package/src/components/ChatBot.tsx +82 -82
  159. package/src/components/ContentValidateFilter.tsx +15 -15
  160. package/src/components/FadingOverflow.tsx +265 -265
  161. package/src/components/FileTreeView/More.tsx +114 -114
  162. package/src/components/FileTreeView/index.tsx +186 -186
  163. package/src/components/InfiniteScroll.tsx +24 -24
  164. package/src/components/InfoMaintenanceBanner.tsx +29 -29
  165. package/src/components/LazyMarkdown/BlockquoteMd.tsx +107 -107
  166. package/src/components/LazyMarkdown/CodeViewer.tsx +161 -161
  167. package/src/components/LazyMarkdown/Markdown.tsx +122 -122
  168. package/src/components/LazyMarkdown/MarkdownButton.tsx +24 -24
  169. package/src/components/LazyMarkdown/Video.tsx +13 -13
  170. package/src/components/LazyMarkdown/index.tsx +21 -21
  171. package/src/components/Placeholder.tsx +123 -118
  172. package/src/components/ScrollView.tsx +57 -57
  173. package/src/components/Select/BadgeItem.tsx +58 -58
  174. package/src/components/Select/ClearInput.tsx +24 -24
  175. package/src/components/Select/CloseItem.tsx +38 -38
  176. package/src/components/Select/CreatableSelect.tsx +155 -155
  177. package/src/components/Select/CustomMenu.tsx +16 -16
  178. package/src/components/Select/LabelItem.tsx +8 -8
  179. package/src/components/Select/MultiValue.tsx +49 -49
  180. package/src/components/Select/SelectInfiniteScroll.tsx +82 -82
  181. package/src/components/Select/SelectSearch.tsx +195 -195
  182. package/src/components/Select/index.tsx +7 -7
  183. package/src/components/Select/types.ts +8 -8
  184. package/src/components/SelectionList.tsx +427 -427
  185. package/src/components/StatusCircle.tsx +67 -67
  186. package/src/components/Stepper/Navigation.tsx +97 -97
  187. package/src/components/Stepper/Step.tsx +30 -30
  188. package/src/components/Stepper/Stepper.tsx +113 -113
  189. package/src/components/Stepper/headers.tsx +64 -64
  190. package/src/components/Stepper/index.ts +3 -3
  191. package/src/components/Table/HeaderItem.tsx +52 -52
  192. package/src/components/Table/SettingsVerticalMenu.tsx +50 -50
  193. package/src/components/Table/StyledLinkTable.tsx +22 -22
  194. package/src/components/Table/TableData.tsx +251 -251
  195. package/src/components/Table/index.tsx +2 -2
  196. package/src/components/TimelineSection.tsx +66 -66
  197. package/src/components/error/ErrorFeedback.tsx +217 -217
  198. package/src/components/error/NotFound.tsx +24 -24
  199. package/src/components/error/UnderMaintenance.tsx +30 -30
  200. package/src/components/error/index.ts +4 -4
  201. package/src/components/form/Form/Form.tsx +101 -101
  202. package/src/components/form/Form/FormGroup.tsx +221 -221
  203. package/src/components/form/Form/index.ts +2 -2
  204. package/src/components/form/SearchInput.tsx +69 -69
  205. package/src/components/form/Select/CustomSelect.tsx +232 -232
  206. package/src/components/form/Select/DetailedSelect.tsx +85 -85
  207. package/src/components/form/Select/Select.tsx +67 -67
  208. package/src/components/form/Select/index.ts +4 -4
  209. package/src/components/form/Select/styled.ts +165 -165
  210. package/src/components/form/Select/types.ts +112 -112
  211. package/src/components/form/Select/utils.tsx +28 -28
  212. package/src/components/notification/NotificationComponent.tsx +340 -340
  213. package/src/components/notification/NotificationItem.tsx +345 -336
  214. package/src/components/notification/NotificationList.tsx +179 -178
  215. package/src/components/notification/NotificationPlaceholder.tsx +44 -43
  216. package/src/components/notification/types.ts +72 -72
  217. package/src/containers/NotificationsPage.tsx +119 -98
  218. package/src/context/anchor.tsx +37 -37
  219. package/src/context/loading.tsx +36 -36
  220. package/src/context/notification/LazyNotificationList.ts +103 -103
  221. package/src/context/notification/NotificationController.ts +104 -104
  222. package/src/context/notification/context.tsx +23 -23
  223. package/src/context/notification/hooks.ts +98 -98
  224. package/src/context/notification/types.ts +66 -65
  225. package/src/hooks/date.ts +31 -31
  226. package/src/hooks/keyboard.tsx +128 -128
  227. package/src/hooks/manual-render.tsx +10 -10
  228. package/src/hooks/service-now.tsx +233 -233
  229. package/src/hooks/text.tsx +30 -30
  230. package/src/hooks/title.tsx +28 -28
  231. package/src/hooks/use-effect-once.tsx +43 -43
  232. package/src/index.ts +19 -19
  233. package/src/notifications.ts +11 -11
  234. package/src/svg/AI.tsx +41 -41
  235. package/src/svg/CS.tsx +48 -48
  236. package/src/svg/EDP.tsx +31 -31
  237. package/src/svg/Forbidden.tsx +22 -22
  238. package/src/svg/GenericPlaceholder.tsx +20 -20
  239. package/src/svg/HUB.tsx +48 -48
  240. package/src/svg/Logo.tsx +16 -16
  241. package/src/svg/MiniLogo.tsx +12 -12
  242. package/src/svg/NotFound.tsx +16 -16
  243. package/src/svg/ServerError.tsx +33 -33
  244. package/src/svg/Unauthenticated.tsx +16 -16
  245. package/src/svg/index.ts +11 -11
  246. package/src/utils/accessibility.ts +135 -135
  247. package/src/utils/cookie.ts +73 -73
  248. package/src/utils/promise.ts +5 -5
  249. package/src/utils/read-file.ts +16 -16
  250. package/tsconfig.json +10 -10
@@ -1,85 +1,85 @@
1
- import { useCallback } from 'react'
2
- import { CustomSelect } from './CustomSelect'
3
- import { DetailedLabel, DetailedSelectProps, GenericAccessibleLabel, KeyOfType } from './types'
4
- import { parseLabel } from './utils'
5
-
6
- function getOptionAsLabel<
7
- Option,
8
- T extends KeyOfType<Option, string> | ((o: NonNullable<Option>) => DetailedLabel)
9
- >(
10
- option: Option,
11
- renderer: T | undefined,
12
- ): GenericAccessibleLabel {
13
- let result: DetailedLabel | undefined
14
- if (typeof renderer === 'function') result = option ? renderer(option) : undefined
15
- else if (typeof renderer === 'string') result = option[renderer as keyof Option] as DetailedLabel
16
- return parseLabel(result ?? `${option ?? ''}`)
17
- }
18
-
19
- /**
20
- * Renders a Select component using the Citric Design System.
21
- *
22
- * The styled version of the select component is rendered on top of the default select from the browser. Visual users will use the Citric
23
- * version of a Select, but blind users, who interacts with the keyboard, will use the default browser select instead, which is already
24
- * highly optimized for accessibility.
25
- *
26
- * The DetailedSelect lets you set an image, a title and a description for each option. To do so, use the prop `renderLabel` and
27
- * `emptyOption`. The accessible string for each option will always be `option.title: option.description`.
28
- *
29
- * To use more customizable labels, check the `CustomSelect`. To use more simple labels (just strings) use `Select`.
30
- *
31
- * The DetailedSelect expects a {@link DetailedLabel} to create labels.
32
- *
33
- * Tips:
34
- * - This is a controlled field. You can't use it any other way. If you're using it with react-hook-form, you need to wrap it under the
35
- * component `<Controller>` from the same library.
36
- * - `value` is required and must be of the same type of an item of the array of options. `value` is only optional if `emptyOption` is
37
- * provided, in this case, an empty option is rendered and the value is undefined when it's selected.
38
- * - A consequence of the previous rule is that you can't have an empty selection if you don't set a value for `emptyOption`. This
39
- * component must work exactly like the browser's `select`, so this behavior is intended.
40
- * - If `renderLabel` or `renderValue` are not provided, this will use the `toString` method of the object to set the option's title.
41
- *
42
- * @example
43
- * options as a string array
44
- * ```
45
- * const options = ['option 1', 'option 2', 'option 3']
46
- *
47
- * function renderDetailedLabel(option: string) {
48
- * return {
49
- * title: option,
50
- * description: 'my description',
51
- * image: <img src="/my-image.png" />,
52
- * }
53
- * }
54
- *
55
- * const MyComponent = {
56
- * const [value, setValue] = useState(options[0])
57
- * return <DetailedSelect options={options} value={value} onChange={setValue} renderLabel={renderDetailedLabel} />
58
- * }
59
- * ```
60
- * @example
61
- * options as an object array
62
- * ```
63
- * const options = [{ id: 1, name: 'John', age: 34 }, { id: 2, name: 'Marcia', age: 28 }, { id: 3, name: 'Angeline', age: 58 }]
64
- *
65
- * function renderDetailedLabel(option: (typeof options)[number]) {
66
- * return {
67
- * title: option.name,
68
- * description: `${option.age} years old`,
69
- * image: <img src="/my-image.png" />,
70
- * }
71
- * }
72
- *
73
- * const MyComponent = {
74
- * const [value, setValue] = useState(options[0])
75
- * // below, renderValue could be `o => o.id`
76
- * return <DetailedSelect options={options} value={value} onChange={setValue} renderValue="id" renderLabel={renderDetailedLabel} />
77
- * }
78
- * ```
79
- * @param props the component props: {@link DetailedSelectProps}.
80
- */
81
- export function DetailedSelect<T>({ renderLabel, emptyOption, ...props }: DetailedSelectProps<T>) {
82
- const renderLabelRef = useCallback((option: T) => getOptionAsLabel(option, renderLabel), [])
83
- // @ts-ignore the following usage is correct, Typescript is getting confused because it doesn't know if `emptyOption` is undefined or not.
84
- return <CustomSelect renderLabel={renderLabelRef} emptyOption={emptyOption ? parseLabel(emptyOption) : undefined} {...props} />
85
- }
1
+ import { useCallback } from 'react'
2
+ import { CustomSelect } from './CustomSelect'
3
+ import { DetailedLabel, DetailedSelectProps, GenericAccessibleLabel, KeyOfType } from './types'
4
+ import { parseLabel } from './utils'
5
+
6
+ function getOptionAsLabel<
7
+ Option,
8
+ T extends KeyOfType<Option, string> | ((o: NonNullable<Option>) => DetailedLabel)
9
+ >(
10
+ option: Option,
11
+ renderer: T | undefined,
12
+ ): GenericAccessibleLabel {
13
+ let result: DetailedLabel | undefined
14
+ if (typeof renderer === 'function') result = option ? renderer(option) : undefined
15
+ else if (typeof renderer === 'string') result = option[renderer as keyof Option] as DetailedLabel
16
+ return parseLabel(result ?? `${option ?? ''}`)
17
+ }
18
+
19
+ /**
20
+ * Renders a Select component using the Citric Design System.
21
+ *
22
+ * The styled version of the select component is rendered on top of the default select from the browser. Visual users will use the Citric
23
+ * version of a Select, but blind users, who interacts with the keyboard, will use the default browser select instead, which is already
24
+ * highly optimized for accessibility.
25
+ *
26
+ * The DetailedSelect lets you set an image, a title and a description for each option. To do so, use the prop `renderLabel` and
27
+ * `emptyOption`. The accessible string for each option will always be `option.title: option.description`.
28
+ *
29
+ * To use more customizable labels, check the `CustomSelect`. To use more simple labels (just strings) use `Select`.
30
+ *
31
+ * The DetailedSelect expects a {@link DetailedLabel} to create labels.
32
+ *
33
+ * Tips:
34
+ * - This is a controlled field. You can't use it any other way. If you're using it with react-hook-form, you need to wrap it under the
35
+ * component `<Controller>` from the same library.
36
+ * - `value` is required and must be of the same type of an item of the array of options. `value` is only optional if `emptyOption` is
37
+ * provided, in this case, an empty option is rendered and the value is undefined when it's selected.
38
+ * - A consequence of the previous rule is that you can't have an empty selection if you don't set a value for `emptyOption`. This
39
+ * component must work exactly like the browser's `select`, so this behavior is intended.
40
+ * - If `renderLabel` or `renderValue` are not provided, this will use the `toString` method of the object to set the option's title.
41
+ *
42
+ * @example
43
+ * options as a string array
44
+ * ```
45
+ * const options = ['option 1', 'option 2', 'option 3']
46
+ *
47
+ * function renderDetailedLabel(option: string) {
48
+ * return {
49
+ * title: option,
50
+ * description: 'my description',
51
+ * image: <img src="/my-image.png" />,
52
+ * }
53
+ * }
54
+ *
55
+ * const MyComponent = {
56
+ * const [value, setValue] = useState(options[0])
57
+ * return <DetailedSelect options={options} value={value} onChange={setValue} renderLabel={renderDetailedLabel} />
58
+ * }
59
+ * ```
60
+ * @example
61
+ * options as an object array
62
+ * ```
63
+ * const options = [{ id: 1, name: 'John', age: 34 }, { id: 2, name: 'Marcia', age: 28 }, { id: 3, name: 'Angeline', age: 58 }]
64
+ *
65
+ * function renderDetailedLabel(option: (typeof options)[number]) {
66
+ * return {
67
+ * title: option.name,
68
+ * description: `${option.age} years old`,
69
+ * image: <img src="/my-image.png" />,
70
+ * }
71
+ * }
72
+ *
73
+ * const MyComponent = {
74
+ * const [value, setValue] = useState(options[0])
75
+ * // below, renderValue could be `o => o.id`
76
+ * return <DetailedSelect options={options} value={value} onChange={setValue} renderValue="id" renderLabel={renderDetailedLabel} />
77
+ * }
78
+ * ```
79
+ * @param props the component props: {@link DetailedSelectProps}.
80
+ */
81
+ export function DetailedSelect<T>({ renderLabel, emptyOption, ...props }: DetailedSelectProps<T>) {
82
+ const renderLabelRef = useCallback((option: T) => getOptionAsLabel(option, renderLabel), [])
83
+ // @ts-ignore the following usage is correct, Typescript is getting confused because it doesn't know if `emptyOption` is undefined or not.
84
+ return <CustomSelect renderLabel={renderLabelRef} emptyOption={emptyOption ? parseLabel(emptyOption) : undefined} {...props} />
85
+ }
@@ -1,67 +1,67 @@
1
- import { useCallback } from 'react'
2
- import { CustomSelect } from './CustomSelect'
3
- import { GenericAccessibleLabel, KeyOfType, SelectProps } from './types'
4
- import { parseLabel } from './utils'
5
-
6
- function getOptionAsLabel<
7
- Option,
8
- T extends KeyOfType<Option, string> | ((o: NonNullable<Option>) => string)
9
- >(
10
- option: Option,
11
- renderer: T | undefined,
12
- ): GenericAccessibleLabel {
13
- let result: string | undefined
14
- if (typeof renderer === 'function') result = option ? renderer(option) : undefined
15
- else if (typeof renderer === 'string') result = option[renderer as keyof Option] as string
16
- return parseLabel(result ?? `${option ?? ''}`)
17
- }
18
-
19
- /**
20
- * Renders a Select component using the Citric Design System.
21
- *
22
- * The styled version of the select component is rendered on top of the default select from the browser. Visual users will use the Citric
23
- * version of a Select, but blind users, who interacts with the keyboard, will use the default browser select instead, which is already
24
- * highly optimized for accessibility.
25
- *
26
- * The Select component renders each option as a string. To use more customizable labels, check the `DetailedSelect` and `CustomSelect`
27
- * components.
28
- *
29
- * The Select component expects plain strings to create labels.
30
- *
31
- * Tips:
32
- * - This is a controlled field. You can't use it any other way. If you're using it with react-hook-form, you need to wrap it under the
33
- * component `<Controller>` from the same library.
34
- * - `value` is required and must be of the same type of an item of the array of options. `value` is only optional if `emptyOption` is
35
- * provided, in this case, an empty option is rendered and the value is undefined when it's selected.
36
- * - A consequence of the previous rule is that you can't have an empty selection if you don't set a value for `emptyOption`. This
37
- * component must work exactly like the browser's `select`, so this behavior is intended.
38
- * - If `renderLabel` or `renderValue` are not provided, this will use the `toString` method of the object.
39
- *
40
- * @example
41
- * options as a string array
42
- * ```
43
- * const options = ['option 1', 'option 2', 'option 3']
44
- *
45
- * const MyComponent = {
46
- * const [value, setValue] = useState(options[0])
47
- * return <Select options={options} value={value} onChange={setValue} />
48
- * }
49
- * ```
50
- * @example
51
- * options as an object array
52
- * ```
53
- * const options = [{ id: 1, name: 'John', age: 34 }, { id: 2, name: 'Marcia', age: 28 }, { id: 3, name: 'Angeline', age: 58 }]
54
- *
55
- * const MyComponent = {
56
- * const [value, setValue] = useState(options[0])
57
- * // below, renderValue could be `o => o.id` and renderLabel `o => o.name`.
58
- * return <Select options={options} value={value} onChange={setValue} renderValue="id" renderLabel="name" />
59
- * }
60
- * ```
61
- * @param props the component props: {@link CustomSelectProps}.
62
- */
63
- export function Select<T>({ renderLabel, emptyOption, ...props }: SelectProps<T>) {
64
- const renderLabelRef = useCallback((option: T) => getOptionAsLabel(option, renderLabel), [])
65
- // @ts-ignore the following usage is correct, Typescript is getting confused because it doesn't know if `emptyOption` is undefined or not.
66
- return <CustomSelect renderLabel={renderLabelRef} emptyOption={emptyOption ? parseLabel(emptyOption) : undefined} {...props} />
67
- }
1
+ import { useCallback } from 'react'
2
+ import { CustomSelect } from './CustomSelect'
3
+ import { GenericAccessibleLabel, KeyOfType, SelectProps } from './types'
4
+ import { parseLabel } from './utils'
5
+
6
+ function getOptionAsLabel<
7
+ Option,
8
+ T extends KeyOfType<Option, string> | ((o: NonNullable<Option>) => string)
9
+ >(
10
+ option: Option,
11
+ renderer: T | undefined,
12
+ ): GenericAccessibleLabel {
13
+ let result: string | undefined
14
+ if (typeof renderer === 'function') result = option ? renderer(option) : undefined
15
+ else if (typeof renderer === 'string') result = option[renderer as keyof Option] as string
16
+ return parseLabel(result ?? `${option ?? ''}`)
17
+ }
18
+
19
+ /**
20
+ * Renders a Select component using the Citric Design System.
21
+ *
22
+ * The styled version of the select component is rendered on top of the default select from the browser. Visual users will use the Citric
23
+ * version of a Select, but blind users, who interacts with the keyboard, will use the default browser select instead, which is already
24
+ * highly optimized for accessibility.
25
+ *
26
+ * The Select component renders each option as a string. To use more customizable labels, check the `DetailedSelect` and `CustomSelect`
27
+ * components.
28
+ *
29
+ * The Select component expects plain strings to create labels.
30
+ *
31
+ * Tips:
32
+ * - This is a controlled field. You can't use it any other way. If you're using it with react-hook-form, you need to wrap it under the
33
+ * component `<Controller>` from the same library.
34
+ * - `value` is required and must be of the same type of an item of the array of options. `value` is only optional if `emptyOption` is
35
+ * provided, in this case, an empty option is rendered and the value is undefined when it's selected.
36
+ * - A consequence of the previous rule is that you can't have an empty selection if you don't set a value for `emptyOption`. This
37
+ * component must work exactly like the browser's `select`, so this behavior is intended.
38
+ * - If `renderLabel` or `renderValue` are not provided, this will use the `toString` method of the object.
39
+ *
40
+ * @example
41
+ * options as a string array
42
+ * ```
43
+ * const options = ['option 1', 'option 2', 'option 3']
44
+ *
45
+ * const MyComponent = {
46
+ * const [value, setValue] = useState(options[0])
47
+ * return <Select options={options} value={value} onChange={setValue} />
48
+ * }
49
+ * ```
50
+ * @example
51
+ * options as an object array
52
+ * ```
53
+ * const options = [{ id: 1, name: 'John', age: 34 }, { id: 2, name: 'Marcia', age: 28 }, { id: 3, name: 'Angeline', age: 58 }]
54
+ *
55
+ * const MyComponent = {
56
+ * const [value, setValue] = useState(options[0])
57
+ * // below, renderValue could be `o => o.id` and renderLabel `o => o.name`.
58
+ * return <Select options={options} value={value} onChange={setValue} renderValue="id" renderLabel="name" />
59
+ * }
60
+ * ```
61
+ * @param props the component props: {@link CustomSelectProps}.
62
+ */
63
+ export function Select<T>({ renderLabel, emptyOption, ...props }: SelectProps<T>) {
64
+ const renderLabelRef = useCallback((option: T) => getOptionAsLabel(option, renderLabel), [])
65
+ // @ts-ignore the following usage is correct, Typescript is getting confused because it doesn't know if `emptyOption` is undefined or not.
66
+ return <CustomSelect renderLabel={renderLabelRef} emptyOption={emptyOption ? parseLabel(emptyOption) : undefined} {...props} />
67
+ }
@@ -1,4 +1,4 @@
1
- export { CustomSelect } from './CustomSelect'
2
- export { DetailedSelect } from './DetailedSelect'
3
- export { Select } from './Select'
4
- export * from './types'
1
+ export { CustomSelect } from './CustomSelect'
2
+ export { DetailedSelect } from './DetailedSelect'
3
+ export { Select } from './Select'
4
+ export * from './types'
@@ -1,165 +1,165 @@
1
- import { theme } from '@stack-spot/portal-theme'
2
- import { styled } from 'styled-components'
3
-
4
- export const SelectBox = styled.div<{ $maxItems: number, $inputHeight: string }>`
5
- position: relative;
6
- // controls the height of component when it's closed
7
- height: ${({ $inputHeight }) => $inputHeight};
8
-
9
- select {
10
- border: none;
11
- opacity: 0;
12
- pointer-events: none;
13
- // prevents visual overflow
14
- max-width: 10px;
15
- }
16
-
17
- .fake-select {
18
- position: absolute;
19
- top: 0;
20
- left: 0;
21
- right: 0;
22
- border-radius: 0.25rem;
23
- display: flex;
24
- flex-direction: column;
25
- border: 1px solid ${theme.color.light[600]};
26
- transition: border-color 0.3s, box-shadow 0.3s;
27
- background-color: ${theme.color.light[300]};
28
-
29
- /* lets the z-index unset until the animation on the height ends. */
30
- &:not(.open) {
31
- z-index: unset;
32
- animation: 0.3s z-index-animation;
33
- @keyframes z-index-animation {
34
- 0% {
35
- z-index: 1;
36
- }
37
- 99% {
38
- z-index: 1;
39
- }
40
- 100% {
41
- z-index: unset;
42
- }
43
- }
44
- }
45
-
46
- &.disabled {
47
- background-color: ${theme.color.light[500]};
48
- color: ${theme.color.light[700]};
49
- .current-value {
50
- cursor: not-allowed;
51
- }
52
- }
53
-
54
- .arrow {
55
- transition: transform ease-in-out 0.3s;
56
- }
57
-
58
- &.focused, &.open {
59
- border: 1px solid ${theme.color.primary[500]};
60
- box-shadow: 0 0 0 1px ${theme.color.primary[500]};
61
- }
62
-
63
- &.open {
64
- z-index: 1;
65
- .arrow {
66
- transform: rotate(180deg);
67
- }
68
- .options {
69
- /* lets the overflow be hidden until the animation on the height ends. */
70
- overflow-y: auto;
71
- animation: 0.3s overflow-animation;
72
- @keyframes overflow-animation {
73
- 0% {
74
- overflow-y: hidden;
75
- }
76
- 99% {
77
- overflow-y: hidden;
78
- }
79
- 100% {
80
- overflow-y: auto;
81
- }
82
- }
83
- }
84
- }
85
-
86
- .current-value {
87
- height: calc(100% - 2px);
88
- display: flex;
89
- flex-direction: row;
90
- padding: 8px;
91
- justify-content: space-between;
92
- align-items: center;
93
- cursor: pointer;
94
- }
95
-
96
- .clipped-text {
97
- text-overflow: ellipsis;
98
- width: 100%;
99
- overflow: hidden;
100
- white-space: nowrap;
101
- }
102
-
103
- .options {
104
- list-style: none;
105
- padding: 0;
106
- margin: 0;
107
- overflow-y: hidden;
108
- transition: height ease-in-out 0.3s;
109
- scrollbar-gutter: stable;
110
-
111
- li {
112
- display: flex;
113
- flex-direction: row;
114
- align-items: center;
115
- padding: 9px;
116
- border-top: 1px solid ${theme.color.light[600]};
117
- cursor: pointer;
118
- transition: background-color 0.2s;
119
- &:hover, &:focus {
120
- background-color: ${theme.color.light[500]};
121
- }
122
- outline: none;
123
- }
124
-
125
- .detailed {
126
- display: flex;
127
- flex-direction: row;
128
- gap: 10px;
129
- align-items: center;
130
- .image {
131
- width: 40px;
132
- height: 40px;
133
- display: flex;
134
- align-items: center;
135
- justify-content: center;
136
- flex-shrink: 0;
137
- overflow: hidden;
138
- & > * {
139
- max-width: 100%;
140
- max-height: 100%;
141
- }
142
- }
143
- .text-content {
144
- display: flex;
145
- flex-direction: column;
146
- gap: 5px;
147
- .description {
148
- color: ${theme.color.light[700]};
149
- }
150
- }
151
- }
152
-
153
- /* the list is inert when, and only when, its having its height measured */
154
- &[inert] {
155
- opacity: 0;
156
- pointer-events: none;
157
- position: absolute;
158
- /* we don't want to have the height measured over the height of $maxItems */
159
- li:nth-child(n+${({ $maxItems }) => $maxItems + 1}) {
160
- display: none;
161
- }
162
- }
163
- }
164
- }
165
- `
1
+ import { theme } from '@stack-spot/portal-theme'
2
+ import { styled } from 'styled-components'
3
+
4
+ export const SelectBox = styled.div<{ $maxItems: number, $inputHeight: string }>`
5
+ position: relative;
6
+ // controls the height of component when it's closed
7
+ height: ${({ $inputHeight }) => $inputHeight};
8
+
9
+ select {
10
+ border: none;
11
+ opacity: 0;
12
+ pointer-events: none;
13
+ // prevents visual overflow
14
+ max-width: 10px;
15
+ }
16
+
17
+ .fake-select {
18
+ position: absolute;
19
+ top: 0;
20
+ left: 0;
21
+ right: 0;
22
+ border-radius: 0.25rem;
23
+ display: flex;
24
+ flex-direction: column;
25
+ border: 1px solid ${theme.color.light[600]};
26
+ transition: border-color 0.3s, box-shadow 0.3s;
27
+ background-color: ${theme.color.light[300]};
28
+
29
+ /* lets the z-index unset until the animation on the height ends. */
30
+ &:not(.open) {
31
+ z-index: unset;
32
+ animation: 0.3s z-index-animation;
33
+ @keyframes z-index-animation {
34
+ 0% {
35
+ z-index: 1;
36
+ }
37
+ 99% {
38
+ z-index: 1;
39
+ }
40
+ 100% {
41
+ z-index: unset;
42
+ }
43
+ }
44
+ }
45
+
46
+ &.disabled {
47
+ background-color: ${theme.color.light[500]};
48
+ color: ${theme.color.light[700]};
49
+ .current-value {
50
+ cursor: not-allowed;
51
+ }
52
+ }
53
+
54
+ .arrow {
55
+ transition: transform ease-in-out 0.3s;
56
+ }
57
+
58
+ &.focused, &.open {
59
+ border: 1px solid ${theme.color.primary[500]};
60
+ box-shadow: 0 0 0 1px ${theme.color.primary[500]};
61
+ }
62
+
63
+ &.open {
64
+ z-index: 1;
65
+ .arrow {
66
+ transform: rotate(180deg);
67
+ }
68
+ .options {
69
+ /* lets the overflow be hidden until the animation on the height ends. */
70
+ overflow-y: auto;
71
+ animation: 0.3s overflow-animation;
72
+ @keyframes overflow-animation {
73
+ 0% {
74
+ overflow-y: hidden;
75
+ }
76
+ 99% {
77
+ overflow-y: hidden;
78
+ }
79
+ 100% {
80
+ overflow-y: auto;
81
+ }
82
+ }
83
+ }
84
+ }
85
+
86
+ .current-value {
87
+ height: calc(100% - 2px);
88
+ display: flex;
89
+ flex-direction: row;
90
+ padding: 8px;
91
+ justify-content: space-between;
92
+ align-items: center;
93
+ cursor: pointer;
94
+ }
95
+
96
+ .clipped-text {
97
+ text-overflow: ellipsis;
98
+ width: 100%;
99
+ overflow: hidden;
100
+ white-space: nowrap;
101
+ }
102
+
103
+ .options {
104
+ list-style: none;
105
+ padding: 0;
106
+ margin: 0;
107
+ overflow-y: hidden;
108
+ transition: height ease-in-out 0.3s;
109
+ scrollbar-gutter: stable;
110
+
111
+ li {
112
+ display: flex;
113
+ flex-direction: row;
114
+ align-items: center;
115
+ padding: 9px;
116
+ border-top: 1px solid ${theme.color.light[600]};
117
+ cursor: pointer;
118
+ transition: background-color 0.2s;
119
+ &:hover, &:focus {
120
+ background-color: ${theme.color.light[500]};
121
+ }
122
+ outline: none;
123
+ }
124
+
125
+ .detailed {
126
+ display: flex;
127
+ flex-direction: row;
128
+ gap: 10px;
129
+ align-items: center;
130
+ .image {
131
+ width: 40px;
132
+ height: 40px;
133
+ display: flex;
134
+ align-items: center;
135
+ justify-content: center;
136
+ flex-shrink: 0;
137
+ overflow: hidden;
138
+ & > * {
139
+ max-width: 100%;
140
+ max-height: 100%;
141
+ }
142
+ }
143
+ .text-content {
144
+ display: flex;
145
+ flex-direction: column;
146
+ gap: 5px;
147
+ .description {
148
+ color: ${theme.color.light[700]};
149
+ }
150
+ }
151
+ }
152
+
153
+ /* the list is inert when, and only when, its having its height measured */
154
+ &[inert] {
155
+ opacity: 0;
156
+ pointer-events: none;
157
+ position: absolute;
158
+ /* we don't want to have the height measured over the height of $maxItems */
159
+ li:nth-child(n+${({ $maxItems }) => $maxItems + 1}) {
160
+ display: none;
161
+ }
162
+ }
163
+ }
164
+ }
165
+ `