@databiosphere/findable-ui 29.0.2 → 31.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (283) hide show
  1. package/.release-please-manifest.json +1 -1
  2. package/CHANGELOG.md +35 -0
  3. package/lib/common/categories/config/types.d.ts +28 -0
  4. package/lib/common/categories/config/utils.d.ts +31 -0
  5. package/lib/common/categories/config/utils.js +29 -0
  6. package/lib/common/categories/models/range/typeGuards.d.ts +14 -0
  7. package/lib/common/categories/models/range/typeGuards.js +18 -0
  8. package/lib/common/categories/models/range/types.d.ts +15 -0
  9. package/lib/common/categories/models/range/types.js +1 -0
  10. package/lib/common/categories/models/range/utils.d.ts +23 -0
  11. package/lib/common/categories/models/range/utils.js +41 -0
  12. package/lib/common/categories/models/select/utils.d.ts +8 -0
  13. package/lib/common/categories/models/select/utils.js +16 -0
  14. package/lib/common/categories/models/types.d.ts +6 -0
  15. package/lib/common/categories/models/types.js +1 -0
  16. package/lib/common/categories/views/common/types.d.ts +10 -0
  17. package/lib/common/categories/views/common/types.js +1 -0
  18. package/lib/common/categories/views/range/typeGuards.d.ts +8 -0
  19. package/lib/common/categories/views/range/typeGuards.js +8 -0
  20. package/lib/common/categories/views/range/types.d.ts +19 -0
  21. package/lib/common/categories/views/range/types.js +1 -0
  22. package/lib/common/categories/views/range/utils.d.ts +12 -0
  23. package/lib/common/categories/views/range/utils.js +24 -0
  24. package/lib/common/categories/views/select/typeGuards.d.ts +8 -0
  25. package/lib/common/categories/views/select/typeGuards.js +8 -0
  26. package/lib/common/categories/views/select/types.d.ts +7 -0
  27. package/lib/common/categories/views/select/types.js +1 -0
  28. package/lib/common/categories/views/types.d.ts +13 -0
  29. package/lib/common/categories/views/types.js +8 -0
  30. package/lib/common/entities.d.ts +8 -23
  31. package/lib/components/DataDictionary/components/Entities/entities.d.ts +3 -1
  32. package/lib/components/DataDictionary/components/Entities/entities.js +1 -1
  33. package/lib/components/DataDictionary/components/Entities/types.d.ts +5 -5
  34. package/lib/components/DataDictionary/components/Entity/entity.d.ts +3 -1
  35. package/lib/components/DataDictionary/components/Entity/entity.js +6 -6
  36. package/lib/components/DataDictionary/components/Entity/types.d.ts +5 -5
  37. package/lib/components/DataDictionary/components/Outline/utils.d.ts +3 -2
  38. package/lib/components/DataDictionary/components/Table/components/BasicCell/basicCell.d.ts +3 -2
  39. package/lib/components/DataDictionary/components/Table/components/BasicCell/basicCell.js +6 -2
  40. package/lib/components/DataDictionary/components/Table/components/BasicCell/utils.d.ts +9 -0
  41. package/lib/components/DataDictionary/components/Table/components/BasicCell/utils.js +12 -0
  42. package/lib/components/DataDictionary/components/Table/hook.d.ts +3 -3
  43. package/lib/components/DataDictionary/components/Table/hook.js +3 -1
  44. package/lib/components/DataDictionary/components/Table/options/core/constants.d.ts +2 -3
  45. package/lib/components/DataDictionary/components/Table/options/hook.d.ts +2 -2
  46. package/lib/components/DataDictionary/components/Table/options/sorting/constants.d.ts +2 -3
  47. package/lib/components/DataDictionary/components/Table/table.d.ts +2 -1
  48. package/lib/components/DataDictionary/components/Table/table.js +1 -1
  49. package/lib/components/DataDictionary/components/Table/types.d.ts +3 -4
  50. package/lib/components/DataDictionary/dataDictionary.d.ts +3 -1
  51. package/lib/components/DataDictionary/dataDictionary.styles.js +2 -3
  52. package/lib/components/DataDictionary/hooks/UseDataDictionary/hook.d.ts +3 -1
  53. package/lib/components/DataDictionary/hooks/UseDataDictionary/types.d.ts +5 -5
  54. package/lib/components/Filter/components/Filter/filter.d.ts +2 -2
  55. package/lib/components/Filter/components/Filter/filter.js +11 -3
  56. package/lib/components/Filter/components/Filter/stories/args.d.ts +5 -0
  57. package/lib/components/Filter/components/Filter/stories/args.js +19 -0
  58. package/lib/components/Filter/components/Filter/stories/filter.stories.d.ts +8 -0
  59. package/lib/components/Filter/components/Filter/stories/filter.stories.js +21 -0
  60. package/lib/components/Filter/components/FilterMenu/filterMenu.js +2 -2
  61. package/lib/components/Filter/components/FilterMenu/filterMenu.styles.d.ts +1 -1
  62. package/lib/components/Filter/components/FilterMenu/filterMenu.styles.js +1 -1
  63. package/lib/components/Filter/components/FilterRange/constants.d.ts +0 -2
  64. package/lib/components/Filter/components/FilterRange/constants.js +0 -5
  65. package/lib/components/Filter/components/FilterRange/filterRange.d.ts +1 -1
  66. package/lib/components/Filter/components/FilterRange/filterRange.js +50 -21
  67. package/lib/components/Filter/components/FilterRange/filterRange.styles.js +58 -10
  68. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/constants.d.ts +5 -0
  69. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/constants.js +5 -0
  70. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/hook.d.ts +2 -2
  71. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/hook.js +32 -7
  72. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/schema.d.ts +6 -0
  73. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/schema.js +50 -0
  74. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/types.d.ts +26 -3
  75. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/types.js +6 -1
  76. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/utils.d.ts +15 -0
  77. package/lib/components/Filter/components/FilterRange/hooks/UseFilterRange/utils.js +25 -0
  78. package/lib/components/Filter/components/FilterRange/stories/args.d.ts +3 -0
  79. package/lib/components/Filter/components/FilterRange/stories/args.js +13 -0
  80. package/lib/components/Filter/components/FilterRange/stories/filterRange.stories.js +2 -2
  81. package/lib/components/Filter/components/FilterRange/types.d.ts +10 -6
  82. package/lib/components/Filter/components/FilterRange/types.js +1 -6
  83. package/lib/components/Filter/components/FilterRange/utils.d.ts +8 -0
  84. package/lib/components/Filter/components/FilterRange/utils.js +15 -0
  85. package/lib/components/Filter/components/FilterTag/stories/args.d.ts +5 -0
  86. package/lib/components/Filter/components/FilterTag/stories/args.js +17 -0
  87. package/lib/components/Filter/components/FilterTag/stories/filterTag.stories.d.ts +8 -0
  88. package/lib/components/Filter/components/FilterTag/stories/filterTag.stories.js +21 -0
  89. package/lib/components/Filter/components/FilterTag/utils.d.ts +10 -0
  90. package/lib/components/Filter/components/FilterTag/utils.js +40 -0
  91. package/lib/components/Filter/components/Filters/filters.d.ts +2 -2
  92. package/lib/components/Filter/components/Filters/filters.js +15 -8
  93. package/lib/components/Filter/components/Filters/stories/args.d.ts +3 -0
  94. package/lib/components/Filter/components/Filters/stories/args.js +15 -0
  95. package/lib/components/Filter/components/Filters/stories/constants.d.ts +22 -0
  96. package/lib/components/Filter/components/Filters/stories/constants.js +134 -0
  97. package/lib/components/Filter/components/Filters/stories/filters.stories.d.ts +6 -0
  98. package/lib/components/Filter/components/Filters/stories/filters.stories.js +15 -0
  99. package/lib/components/Filter/components/SearchAllFilters/components/VariableSizeList/VariableSizeList.d.ts +1 -1
  100. package/lib/components/Filter/components/SearchAllFilters/components/VariableSizeList/VariableSizeList.js +5 -5
  101. package/lib/components/Filter/components/SearchAllFilters/components/VariableSizeListItem/variableSizeListItem.js +2 -1
  102. package/lib/components/Filter/components/SearchAllFilters/searchAllFilters.d.ts +3 -2
  103. package/lib/components/Filter/components/SearchAllFilters/searchAllFilters.js +6 -4
  104. package/lib/components/Filter/components/VariableSizeListItem/variableSizeListItem.js +2 -1
  105. package/lib/components/Index/components/EntitiesView/components/ChartView/utils.js +2 -0
  106. package/lib/components/Index/table/hook.js +4 -0
  107. package/lib/components/MarkdownRenderer/components/Anchor/anchor.d.ts +19 -0
  108. package/lib/components/MarkdownRenderer/components/Anchor/anchor.js +21 -0
  109. package/lib/components/MarkdownRenderer/components/Table/table.d.ts +3 -0
  110. package/lib/components/MarkdownRenderer/components/Table/table.js +5 -0
  111. package/lib/components/MarkdownRenderer/components/Table/table.styles.d.ts +4 -0
  112. package/lib/components/MarkdownRenderer/components/Table/table.styles.js +40 -0
  113. package/lib/components/MarkdownRenderer/constants.d.ts +2 -0
  114. package/lib/components/MarkdownRenderer/constants.js +6 -0
  115. package/lib/components/MarkdownRenderer/markdownRenderer.d.ts +2 -0
  116. package/lib/components/MarkdownRenderer/markdownRenderer.js +45 -0
  117. package/lib/components/MarkdownRenderer/markdownRenderer.styles.d.ts +4 -0
  118. package/lib/components/MarkdownRenderer/markdownRenderer.styles.js +13 -0
  119. package/lib/components/MarkdownRenderer/types.d.ts +6 -0
  120. package/lib/components/MarkdownRenderer/types.js +1 -0
  121. package/lib/components/Table/columnDef/accessorFn/typeGuards.d.ts +9 -0
  122. package/lib/components/Table/columnDef/accessorFn/typeGuards.js +10 -0
  123. package/lib/components/Table/common/utils.d.ts +2 -2
  124. package/lib/components/Table/common/utils.js +28 -13
  125. package/lib/components/Table/components/TableCell/components/ChipCell/chipCell.d.ts +4 -0
  126. package/lib/components/Table/components/TableCell/components/ChipCell/chipCell.js +8 -0
  127. package/lib/components/Table/components/TableCell/components/LinkCell/linkCell.d.ts +4 -0
  128. package/lib/components/Table/components/TableCell/components/LinkCell/linkCell.js +21 -0
  129. package/lib/components/Table/components/TableCell/components/LinkCell/stories/args.d.ts +6 -0
  130. package/lib/components/Table/components/TableCell/components/LinkCell/stories/args.js +27 -0
  131. package/lib/components/Table/components/TableCell/components/LinkCell/stories/linkCell.stories.d.ts +9 -0
  132. package/lib/components/Table/components/TableCell/components/LinkCell/stories/linkCell.stories.js +18 -0
  133. package/lib/components/Table/components/TableCell/components/LinkCell/stories/types.d.ts +3 -0
  134. package/lib/components/Table/components/TableCell/components/LinkCell/stories/types.js +1 -0
  135. package/lib/components/Table/components/TableCell/components/LinkCell/utils.d.ts +22 -0
  136. package/lib/components/Table/components/TableCell/components/LinkCell/utils.js +45 -0
  137. package/lib/components/Table/components/TableCell/components/MarkdownCell/markdownCell.d.ts +4 -0
  138. package/lib/components/Table/components/TableCell/components/MarkdownCell/markdownCell.js +13 -0
  139. package/lib/components/Table/components/TableCell/components/MarkdownCell/markdownCell.styles.d.ts +3 -0
  140. package/lib/components/Table/components/TableCell/components/MarkdownCell/markdownCell.styles.js +30 -0
  141. package/lib/components/Table/components/TableCell/components/MarkdownCell/stories/args.d.ts +4 -0
  142. package/lib/components/Table/components/TableCell/components/MarkdownCell/stories/args.js +10 -0
  143. package/lib/components/Table/components/TableCell/components/MarkdownCell/stories/constants.d.ts +5 -0
  144. package/lib/components/Table/components/TableCell/components/MarkdownCell/stories/constants.js +9 -0
  145. package/lib/components/Table/components/TableCell/components/MarkdownCell/stories/markdownCell.stories.d.ts +7 -0
  146. package/lib/components/Table/components/TableCell/components/MarkdownCell/stories/markdownCell.stories.js +25 -0
  147. package/lib/components/Table/components/TableCell/components/MarkdownCell/stories/types.d.ts +3 -0
  148. package/lib/components/Table/components/TableCell/components/MarkdownCell/stories/types.js +1 -0
  149. package/lib/components/Table/components/TableCell/components/MarkdownCell/types.d.ts +3 -0
  150. package/lib/components/Table/components/TableCell/components/MarkdownCell/types.js +1 -0
  151. package/lib/components/Table/featureOptions/facetedColumn/getFacetedMinMaxValues.d.ts +8 -0
  152. package/lib/components/Table/featureOptions/facetedColumn/getFacetedMinMaxValues.js +46 -0
  153. package/lib/components/common/Link/typeGuards.d.ts +13 -0
  154. package/lib/components/common/Link/typeGuards.js +21 -0
  155. package/lib/config/entities.d.ts +2 -11
  156. package/lib/hooks/useCategoryFilter.d.ts +8 -13
  157. package/lib/hooks/useCategoryFilter.js +31 -28
  158. package/lib/providers/exploreState/entities.d.ts +5 -3
  159. package/lib/providers/exploreState/payloads/entities.d.ts +6 -2
  160. package/lib/providers/exploreState.d.ts +3 -2
  161. package/lib/providers/exploreState.js +1 -1
  162. package/lib/styles/common/mui/typography.js +1 -0
  163. package/lib/tests/utils.d.ts +24 -0
  164. package/lib/tests/utils.js +34 -0
  165. package/lib/theme/common/components.js +19 -1
  166. package/lib/views/ExploreView/exploreView.js +10 -8
  167. package/package.json +7 -1
  168. package/src/common/categories/config/types.ts +42 -0
  169. package/src/common/categories/config/utils.ts +47 -0
  170. package/src/common/categories/models/range/typeGuards.ts +24 -0
  171. package/src/common/categories/models/range/types.ts +17 -0
  172. package/src/common/categories/models/range/utils.ts +51 -0
  173. package/src/common/categories/models/select/utils.ts +23 -0
  174. package/src/common/categories/models/types.ts +7 -0
  175. package/src/common/categories/views/common/types.ts +11 -0
  176. package/src/common/categories/views/range/typeGuards.ts +13 -0
  177. package/src/common/categories/views/range/types.ts +21 -0
  178. package/src/common/categories/views/range/utils.ts +35 -0
  179. package/src/common/categories/views/select/typeGuards.ts +13 -0
  180. package/src/common/categories/views/select/types.ts +8 -0
  181. package/src/common/categories/views/types.ts +15 -0
  182. package/src/common/entities.ts +8 -26
  183. package/src/components/DataDictionary/components/Entities/entities.tsx +7 -5
  184. package/src/components/DataDictionary/components/Entities/types.ts +5 -9
  185. package/src/components/DataDictionary/components/Entity/entity.tsx +9 -7
  186. package/src/components/DataDictionary/components/Entity/types.ts +5 -9
  187. package/src/components/DataDictionary/components/Outline/utils.ts +5 -2
  188. package/src/components/DataDictionary/components/Table/components/BasicCell/basicCell.tsx +12 -4
  189. package/src/components/DataDictionary/components/Table/components/BasicCell/utils.ts +13 -0
  190. package/src/components/DataDictionary/components/Table/hook.ts +16 -8
  191. package/src/components/DataDictionary/components/Table/options/core/constants.ts +2 -3
  192. package/src/components/DataDictionary/components/Table/options/hook.ts +3 -3
  193. package/src/components/DataDictionary/components/Table/options/sorting/constants.ts +2 -6
  194. package/src/components/DataDictionary/components/Table/table.tsx +4 -1
  195. package/src/components/DataDictionary/components/Table/types.ts +3 -4
  196. package/src/components/DataDictionary/dataDictionary.styles.ts +2 -3
  197. package/src/components/DataDictionary/dataDictionary.tsx +4 -2
  198. package/src/components/DataDictionary/hooks/UseDataDictionary/hook.ts +9 -2
  199. package/src/components/DataDictionary/hooks/UseDataDictionary/types.ts +5 -9
  200. package/src/components/Filter/components/Filter/filter.tsx +38 -13
  201. package/src/components/Filter/components/Filter/stories/args.ts +24 -0
  202. package/src/components/Filter/components/Filter/stories/filter.stories.tsx +32 -0
  203. package/src/components/Filter/components/FilterMenu/filterMenu.styles.ts +1 -1
  204. package/src/components/Filter/components/FilterMenu/filterMenu.tsx +7 -3
  205. package/src/components/Filter/components/FilterRange/constants.ts +0 -7
  206. package/src/components/Filter/components/FilterRange/filterRange.styles.ts +58 -14
  207. package/src/components/Filter/components/FilterRange/filterRange.tsx +112 -40
  208. package/src/components/Filter/components/FilterRange/hooks/UseFilterRange/constants.ts +5 -0
  209. package/src/components/Filter/components/FilterRange/hooks/UseFilterRange/hook.ts +51 -10
  210. package/src/components/Filter/components/FilterRange/hooks/UseFilterRange/schema.ts +60 -0
  211. package/src/components/Filter/components/FilterRange/hooks/UseFilterRange/types.ts +34 -3
  212. package/src/components/Filter/components/FilterRange/hooks/UseFilterRange/utils.ts +32 -0
  213. package/src/components/Filter/components/FilterRange/stories/args.ts +16 -0
  214. package/src/components/Filter/components/FilterRange/stories/filterRange.stories.tsx +2 -2
  215. package/src/components/Filter/components/FilterRange/types.ts +12 -6
  216. package/src/components/Filter/components/FilterRange/utils.ts +16 -0
  217. package/src/components/Filter/components/FilterTag/stories/args.ts +22 -0
  218. package/src/components/Filter/components/FilterTag/stories/filterTag.stories.tsx +32 -0
  219. package/src/components/Filter/components/FilterTag/utils.ts +57 -0
  220. package/src/components/Filter/components/Filters/filters.tsx +21 -12
  221. package/src/components/Filter/components/Filters/stories/args.ts +24 -0
  222. package/src/components/Filter/components/Filters/stories/constants.ts +151 -0
  223. package/src/components/Filter/components/Filters/stories/filters.stories.tsx +24 -0
  224. package/src/components/Filter/components/SearchAllFilters/components/VariableSizeList/VariableSizeList.tsx +32 -29
  225. package/src/components/Filter/components/SearchAllFilters/components/VariableSizeListItem/variableSizeListItem.tsx +9 -1
  226. package/src/components/Filter/components/SearchAllFilters/searchAllFilters.tsx +12 -6
  227. package/src/components/Filter/components/VariableSizeListItem/variableSizeListItem.tsx +2 -1
  228. package/src/components/Index/components/EntitiesView/components/ChartView/utils.ts +2 -0
  229. package/src/components/Index/table/hook.ts +4 -0
  230. package/src/components/MarkdownRenderer/components/Anchor/anchor.tsx +34 -0
  231. package/src/components/MarkdownRenderer/components/Table/table.styles.ts +41 -0
  232. package/src/components/MarkdownRenderer/components/Table/table.tsx +13 -0
  233. package/src/components/MarkdownRenderer/constants.ts +8 -0
  234. package/src/components/MarkdownRenderer/markdownRenderer.styles.ts +16 -0
  235. package/src/components/MarkdownRenderer/markdownRenderer.tsx +62 -0
  236. package/src/components/MarkdownRenderer/types.ts +7 -0
  237. package/src/components/Table/columnDef/accessorFn/typeGuards.ts +15 -0
  238. package/src/components/Table/common/utils.ts +37 -16
  239. package/src/components/Table/components/TableCell/components/ChipCell/chipCell.tsx +16 -0
  240. package/src/components/Table/components/TableCell/components/LinkCell/linkCell.tsx +64 -0
  241. package/src/components/Table/components/TableCell/components/LinkCell/stories/args.ts +35 -0
  242. package/src/components/Table/components/TableCell/components/LinkCell/stories/linkCell.stories.tsx +32 -0
  243. package/src/components/Table/components/TableCell/components/LinkCell/stories/types.ts +4 -0
  244. package/src/components/Table/components/TableCell/components/LinkCell/utils.ts +59 -0
  245. package/src/components/Table/components/TableCell/components/MarkdownCell/markdownCell.styles.ts +31 -0
  246. package/src/components/Table/components/TableCell/components/MarkdownCell/markdownCell.tsx +29 -0
  247. package/src/components/Table/components/TableCell/components/MarkdownCell/stories/args.ts +17 -0
  248. package/src/components/Table/components/TableCell/components/MarkdownCell/stories/constants.ts +11 -0
  249. package/src/components/Table/components/TableCell/components/MarkdownCell/stories/markdownCell.stories.tsx +37 -0
  250. package/src/components/Table/components/TableCell/components/MarkdownCell/stories/types.ts +4 -0
  251. package/src/components/Table/components/TableCell/components/MarkdownCell/types.ts +3 -0
  252. package/src/components/Table/featureOptions/facetedColumn/getFacetedMinMaxValues.ts +64 -0
  253. package/src/components/common/Link/typeGuards.ts +35 -0
  254. package/src/config/entities.ts +1 -14
  255. package/src/hooks/useCategoryFilter.ts +56 -53
  256. package/src/providers/exploreState/entities.ts +3 -3
  257. package/src/providers/exploreState/initializer/utils.ts +1 -1
  258. package/src/providers/exploreState/payloads/entities.ts +5 -2
  259. package/src/providers/exploreState.tsx +5 -3
  260. package/src/styles/common/mui/typography.ts +1 -0
  261. package/src/tests/utils.ts +44 -0
  262. package/src/theme/common/components.ts +19 -1
  263. package/src/views/ExploreView/exploreView.tsx +17 -22
  264. package/tests/filter.test.tsx +100 -0
  265. package/tests/filterRange.test.tsx +331 -46
  266. package/tests/filters.test.tsx +61 -0
  267. package/tests/getFacetedMinMaxValues.test.ts +166 -0
  268. package/tests/linkCell.test.tsx +89 -0
  269. package/tests/markdownCell.test.tsx +53 -0
  270. package/types/data-explorer-ui.d.ts +2 -0
  271. package/lib/components/DataDictionary/components/Table/components/BasicCell/types.d.ts +0 -3
  272. package/lib/components/Filter/components/Filter/filter.stories.d.ts +0 -25
  273. package/lib/components/Filter/components/Filter/filter.stories.js +0 -42
  274. package/lib/components/Filter/components/FilterTag/filterTag.stories.d.ts +0 -16
  275. package/lib/components/Filter/components/FilterTag/filterTag.stories.js +0 -17
  276. package/lib/components/Filter/components/Filters/filters.stories.d.ts +0 -6
  277. package/lib/components/Filter/components/Filters/filters.stories.js +0 -91
  278. package/src/components/DataDictionary/components/Table/components/BasicCell/types.ts +0 -7
  279. package/src/components/Filter/components/Filter/filter.stories.tsx +0 -52
  280. package/src/components/Filter/components/FilterTag/filterTag.stories.tsx +0 -23
  281. package/src/components/Filter/components/Filters/filters.stories.tsx +0 -101
  282. package/tests/filterRangeMock.test.tsx +0 -38
  283. /package/lib/{components/DataDictionary/components/Table/components/BasicCell → common/categories/config}/types.js +0 -0
@@ -0,0 +1,24 @@
1
+ import { fn } from "@storybook/test";
2
+ import { ComponentProps } from "react";
3
+ import { Filters } from "../filters";
4
+ import {
5
+ BIOLOGICAL_SEX,
6
+ DONOR_COUNT,
7
+ FILE_FORMAT,
8
+ FILE_TYPE,
9
+ GENUS_SPECIES,
10
+ } from "./constants";
11
+
12
+ export const DEFAULT_ARGS: ComponentProps<typeof Filters> = {
13
+ categoryFilters: [
14
+ {
15
+ categoryViews: [BIOLOGICAL_SEX, GENUS_SPECIES, DONOR_COUNT],
16
+ label: "Donor",
17
+ },
18
+ {
19
+ categoryViews: [FILE_FORMAT, FILE_TYPE],
20
+ label: "File",
21
+ },
22
+ ],
23
+ onFilter: fn(),
24
+ };
@@ -0,0 +1,151 @@
1
+ import { RangeCategoryView } from "../../../../../common/categories/views/range/types";
2
+ import { SelectCategoryView } from "../../../../../common/entities";
3
+
4
+ /**
5
+ * Biological Sex
6
+ */
7
+ const MALE = {
8
+ count: 14,
9
+ key: "male",
10
+ label: "Male",
11
+ selected: false,
12
+ };
13
+
14
+ /**
15
+ * Biological Sex
16
+ */
17
+ const FEMALE = {
18
+ count: 12,
19
+ key: "female",
20
+ label: "Female",
21
+ selected: false,
22
+ };
23
+
24
+ /**
25
+ * Genus Species
26
+ */
27
+ const HOMO_SAPIENS = {
28
+ count: 471,
29
+ key: "Homo sapiens",
30
+ label: "Homo sapiens",
31
+ selected: false,
32
+ };
33
+
34
+ /**
35
+ * Genus Species
36
+ */
37
+ const MUS_MUSCLES = {
38
+ count: 55,
39
+ key: "Mus musculus",
40
+ label: "Mus musculus",
41
+ selected: false,
42
+ };
43
+
44
+ /**
45
+ * File Format
46
+ */
47
+ const BAM = {
48
+ count: 7,
49
+ key: "bam",
50
+ label: "bam",
51
+ selected: false,
52
+ };
53
+
54
+ /**
55
+ * File Format
56
+ */
57
+ const CSV = {
58
+ count: 5,
59
+ key: "csv",
60
+ label: "csv",
61
+ selected: false,
62
+ };
63
+
64
+ /**
65
+ * File Format
66
+ */
67
+ const FASTQ = {
68
+ count: 38,
69
+ key: "fastq",
70
+ label: "fastq",
71
+ selected: false,
72
+ };
73
+
74
+ /**
75
+ * File Format
76
+ */
77
+ const TSV = {
78
+ count: 3,
79
+ key: "tsv",
80
+ label: "tsv",
81
+ selected: false,
82
+ };
83
+
84
+ /**
85
+ * File Type
86
+ */
87
+ const RAW = {
88
+ count: 1,
89
+ key: "raw",
90
+ label: "raw",
91
+ selected: false,
92
+ };
93
+
94
+ /**
95
+ * File Type
96
+ */
97
+ const PROCESSED = {
98
+ count: 1,
99
+ key: "processed",
100
+ label: "processed",
101
+ selected: false,
102
+ };
103
+
104
+ /**
105
+ * Biological Sex select category view
106
+ */
107
+ export const BIOLOGICAL_SEX: SelectCategoryView = {
108
+ key: "biologicalSex",
109
+ label: "Biological Sex",
110
+ values: [MALE, FEMALE],
111
+ };
112
+
113
+ /**
114
+ * Genus Species select category view
115
+ */
116
+ export const GENUS_SPECIES: SelectCategoryView = {
117
+ key: "genusSpecies",
118
+ label: "Genus Species",
119
+ values: [HOMO_SAPIENS, MUS_MUSCLES],
120
+ };
121
+
122
+ /**
123
+ * Donor Count range category view
124
+ */
125
+ export const DONOR_COUNT: RangeCategoryView = {
126
+ key: "Donor Count",
127
+ label: "Donor Count",
128
+ max: 200,
129
+ min: 10,
130
+ selectedMax: null,
131
+ selectedMin: null,
132
+ };
133
+
134
+ /**
135
+ * File Format select category view
136
+ */
137
+ export const FILE_FORMAT: SelectCategoryView = {
138
+ key: "fileFormat",
139
+ label: "File Format",
140
+ values: [BAM, CSV, FASTQ, TSV],
141
+ };
142
+
143
+ /**
144
+ * File Type select category view
145
+ */
146
+ export const FILE_TYPE: SelectCategoryView = {
147
+ isDisabled: true,
148
+ key: "fileType",
149
+ label: "File Type",
150
+ values: [RAW, PROCESSED],
151
+ };
@@ -0,0 +1,24 @@
1
+ import { Box } from "@mui/material";
2
+ import { Meta, StoryObj } from "@storybook/react";
3
+ import React from "react";
4
+ import { Filters } from "../filters";
5
+ import { DEFAULT_ARGS } from "./args";
6
+
7
+ const meta: Meta<typeof Filters> = {
8
+ component: Filters,
9
+ decorators: [
10
+ (Story): JSX.Element => (
11
+ <Box sx={{ width: 264 }}>
12
+ <Story />
13
+ </Box>
14
+ ),
15
+ ],
16
+ };
17
+
18
+ export default meta;
19
+
20
+ type Story = StoryObj<typeof Filters>;
21
+
22
+ export const Default: Story = {
23
+ args: DEFAULT_ARGS,
24
+ };
@@ -46,12 +46,12 @@ export type ItemSizeByItemKey = Map<string, number>;
46
46
 
47
47
  export interface VariableSizeListProps {
48
48
  autocompleteListProps: Omit<MListProps, "children">;
49
- categoryViews: SelectCategoryView[];
50
49
  height?: number; // Height of list; vertical list must be a number.
51
50
  itemSize?: number; // Default item size.
52
51
  onFilter: OnFilterFn;
53
52
  overscanCount?: ListProps["overscanCount"];
54
53
  searchTerm: string;
54
+ selectCategoryViews: SelectCategoryView[];
55
55
  width?: ListProps["width"]; // Width of list; default to 100% width of parent element.
56
56
  }
57
57
 
@@ -120,17 +120,17 @@ export const VariableSizeList = forwardRef<
120
120
  >(function VariableSizeList(
121
121
  {
122
122
  autocompleteListProps,
123
- categoryViews,
124
123
  height: initHeight = MAX_LIST_HEIGHT_PX,
125
124
  itemSize = LIST_ITEM_HEIGHT,
126
125
  onFilter,
127
126
  overscanCount = MAX_DISPLAYABLE_LIST_ITEMS * 2,
128
127
  searchTerm,
128
+ selectCategoryViews,
129
129
  width = "100%",
130
130
  }: VariableSizeListProps,
131
131
  autocompleteListRef
132
132
  ): JSX.Element {
133
- const filteredItems = applyMenuFilter(categoryViews, searchTerm);
133
+ const filteredItems = applyMenuFilter(selectCategoryViews, searchTerm);
134
134
  let resizeRequired = true;
135
135
  const desktopSmDown = useBreakpointHelper(
136
136
  BREAKPOINT_FN_NAME.DOWN,
@@ -223,40 +223,43 @@ export const VariableSizeList = forwardRef<
223
223
 
224
224
  /**
225
225
  * Filter categories' values by a search term and return model of list items
226
- * @param categoryViews - Select category views
226
+ * @param selectCategoryViews - Select category views
227
227
  * @param inputValue - Search term
228
228
  * @returns array of objects representing list items to be rendered
229
229
  */
230
230
  function applyMenuFilter(
231
- categoryViews: SelectCategoryView[],
231
+ selectCategoryViews: SelectCategoryView[],
232
232
  inputValue: string
233
233
  ): SearchAllFiltersItem[] {
234
234
  const sortMatches = getSortMatchesFn(inputValue);
235
- const filteredItems = categoryViews.reduce((filteredItems, category) => {
236
- if (!category.isDisabled) {
237
- const categoryValueKeyPrefix =
238
- "value_" + category.key.replaceAll(";", ";;") + ";_"; // Terminating the category key with a semicolon (and escaping preceding semicolons) ensures a unique prefix
239
- const filteredCategoryValues = sortMatches(category.values).map(
240
- (match): ValueItem => ({
241
- categoryKey: category.key,
242
- key: categoryValueKeyPrefix + match.value.key,
243
- matchRanges: match.labelRanges,
244
- type: ITEM_TYPE.VALUE,
245
- value: match.value,
246
- })
247
- );
248
- if (filteredCategoryValues.length) {
249
- if (filteredItems.length) filteredItems.push(DIVIDER_ITEM);
250
- filteredItems.push({
251
- categoryLabel: category.label,
252
- key: "category_" + category.key,
253
- type: ITEM_TYPE.CATEGORY,
254
- });
255
- filteredItems.push(...filteredCategoryValues);
235
+ const filteredItems = selectCategoryViews.reduce(
236
+ (filteredItems, category) => {
237
+ if (!category.isDisabled) {
238
+ const categoryValueKeyPrefix =
239
+ "value_" + category.key.replaceAll(";", ";;") + ";_"; // Terminating the category key with a semicolon (and escaping preceding semicolons) ensures a unique prefix
240
+ const filteredCategoryValues = sortMatches(category.values).map(
241
+ (match): ValueItem => ({
242
+ categoryKey: category.key,
243
+ key: categoryValueKeyPrefix + match.value.key,
244
+ matchRanges: match.labelRanges,
245
+ type: ITEM_TYPE.VALUE,
246
+ value: match.value,
247
+ })
248
+ );
249
+ if (filteredCategoryValues.length) {
250
+ if (filteredItems.length) filteredItems.push(DIVIDER_ITEM);
251
+ filteredItems.push({
252
+ categoryLabel: category.label,
253
+ key: "category_" + category.key,
254
+ type: ITEM_TYPE.CATEGORY,
255
+ });
256
+ filteredItems.push(...filteredCategoryValues);
257
+ }
256
258
  }
257
- }
258
- return filteredItems;
259
- }, [] as SearchAllFiltersItem[]);
259
+ return filteredItems;
260
+ },
261
+ [] as SearchAllFiltersItem[]
262
+ );
260
263
  if (filteredItems.length === 0) filteredItems.push(NO_RESULTS_ITEM);
261
264
  return filteredItems;
262
265
  }
@@ -6,6 +6,7 @@ import {
6
6
  Typography,
7
7
  } from "@mui/material";
8
8
  import React, { useEffect, useRef } from "react";
9
+ import { VIEW_KIND } from "../../../../../../common/categories/views/types";
9
10
  import { OnFilterFn } from "../../../../../../hooks/useCategoryFilter";
10
11
  import { TEST_IDS } from "../../../../../../tests/testIds";
11
12
  import { TEXT_BODY_SMALL_400 } from "../../../../../../theme/common/typography";
@@ -54,7 +55,14 @@ export default function VariableSizeListItem({
54
55
  ref={setRef}
55
56
  key={key}
56
57
  onClick={(): void =>
57
- onFilter(categoryKey, valueKey, !selected, undefined, searchTerm)
58
+ onFilter(
59
+ categoryKey,
60
+ valueKey,
61
+ !selected,
62
+ undefined,
63
+ VIEW_KIND.SELECT,
64
+ searchTerm
65
+ )
58
66
  }
59
67
  selected={selected}
60
68
  style={style}
@@ -10,6 +10,8 @@ import React, {
10
10
  useRef,
11
11
  useState,
12
12
  } from "react";
13
+ import { isSelectCategoryView } from "../../../../common/categories/views/select/typeGuards";
14
+ import { CategoryView } from "../../../../common/categories/views/types";
13
15
  import { SelectCategoryView } from "../../../../common/entities";
14
16
  import { SELECTOR } from "../../../../common/selectors";
15
17
  import {
@@ -29,18 +31,18 @@ import { VariableSizeList } from "./components/VariableSizeList/VariableSizeList
29
31
  import { Autocomplete } from "./searchAllFilters.styles";
30
32
 
31
33
  export interface SearchAllFiltersProps {
32
- categoryViews: SelectCategoryView[];
34
+ categoryViews: CategoryView[];
33
35
  drawerOpen?: boolean;
34
36
  onFilter: OnFilterFn;
35
37
  }
36
38
 
37
39
  interface ListboxContextValue {
38
- categoryViews: SelectCategoryView[];
39
40
  onClearSearch: () => void;
40
41
  onCloseSearch: () => void;
41
42
  onFilter: OnFilterFn;
42
43
  open: boolean;
43
44
  searchTerm: string;
45
+ selectCategoryViews: SelectCategoryView[];
44
46
  }
45
47
 
46
48
  const renderInput = (params: AutocompleteRenderInputParams): JSX.Element => (
@@ -48,12 +50,12 @@ const renderInput = (params: AutocompleteRenderInputParams): JSX.Element => (
48
50
  );
49
51
 
50
52
  export const ListboxContext = createContext<ListboxContextValue>({
51
- categoryViews: [],
52
53
  onClearSearch: (): void => undefined,
53
54
  onCloseSearch: (): void => undefined,
54
55
  onFilter: (): void => undefined,
55
56
  open: false,
56
57
  searchTerm: "",
58
+ selectCategoryViews: [],
57
59
  });
58
60
 
59
61
  const Listbox = React.forwardRef<HTMLUListElement, MListProps>(function Listbox(
@@ -63,14 +65,15 @@ const Listbox = React.forwardRef<HTMLUListElement, MListProps>(function Listbox(
63
65
  props = Object.assign({}, props, {
64
66
  children: undefined, // Content is controlled by VariableSizeList
65
67
  });
66
- const { categoryViews, onFilter, searchTerm } = useContext(ListboxContext);
68
+ const { onFilter, searchTerm, selectCategoryViews } =
69
+ useContext(ListboxContext);
67
70
  return (
68
71
  <VariableSizeList
69
72
  autocompleteListProps={props}
70
- categoryViews={categoryViews}
71
73
  onFilter={onFilter}
72
74
  ref={ref}
73
75
  searchTerm={searchTerm}
76
+ selectCategoryViews={selectCategoryViews}
74
77
  />
75
78
  );
76
79
  });
@@ -84,6 +87,9 @@ export const SearchAllFilters = ({
84
87
  const autocompleteRef = useRef<HTMLDivElement>(null);
85
88
  const [open, setOpen] = useState(false);
86
89
  const [searchTerm, setSearchTerm] = useState("");
90
+ const selectCategoryViews = categoryViews.filter((view) =>
91
+ isSelectCategoryView(view)
92
+ );
87
93
 
88
94
  // Handles background scroll action (desktop only).
89
95
  const handleBackgroundScroll = (overflowStyle: OVERFLOW_STYLE): void => {
@@ -145,12 +151,12 @@ export const SearchAllFilters = ({
145
151
  return (
146
152
  <ListboxContext.Provider
147
153
  value={{
148
- categoryViews,
149
154
  onClearSearch,
150
155
  onCloseSearch,
151
156
  onFilter,
152
157
  open,
153
158
  searchTerm,
159
+ selectCategoryViews,
154
160
  }}
155
161
  >
156
162
  <Autocomplete
@@ -5,6 +5,7 @@ import {
5
5
  Typography,
6
6
  } from "@mui/material";
7
7
  import React, { CSSProperties, useEffect, useRef } from "react";
8
+ import { VIEW_KIND } from "../../../../common/categories/views/types";
8
9
  import { CategoryKey } from "../../../../common/entities";
9
10
  import { OnFilterFn } from "../../../../hooks/useCategoryFilter";
10
11
  import { SELECT_CATEGORY_KEY } from "../../../../providers/exploreState/constants";
@@ -45,7 +46,7 @@ export default function VariableSizeListItem({
45
46
  }, [key, onUpdateItemSizeByItemKey]);
46
47
 
47
48
  const handleItemClicked = (): void => {
48
- onFilter(categoryKey, key, !selected, categorySection);
49
+ onFilter(categoryKey, key, !selected, categorySection, VIEW_KIND.SELECT);
49
50
  };
50
51
 
51
52
  return (
@@ -1,3 +1,4 @@
1
+ import { isSelectCategoryView } from "../../../../../../common/categories/views/select/typeGuards";
1
2
  import { SelectCategoryView } from "../../../../../../common/entities";
2
3
  import { CategoryFilter } from "../../../../../Filter/components/Filters/filters";
3
4
 
@@ -11,6 +12,7 @@ export function getSelectCategoryViews(
11
12
  ): SelectCategoryView[] {
12
13
  return categoryFilters
13
14
  .flatMap(({ categoryViews }) => categoryViews)
15
+ .filter(isSelectCategoryView)
14
16
  .filter(({ enableChartView = true }) => enableChartView)
15
17
  .filter(({ values }) => values.length > 0);
16
18
  }
@@ -33,6 +33,7 @@ import {
33
33
  isClientFilteringEnabled,
34
34
  sortingFn,
35
35
  } from "../../Table/common/utils";
36
+ import { getFacetedMinMaxValues } from "../../Table/featureOptions/facetedColumn/getFacetedMinMaxValues";
36
37
  import { ROW_POSITION } from "../../Table/features/RowPosition/constants";
37
38
  import { ROW_PREVIEW } from "../../Table/features/RowPreview/constants";
38
39
  import { RowPreviewState } from "../../Table/features/RowPreview/entities";
@@ -167,6 +168,9 @@ export const useTable = <T extends RowData>(): UseTable<T> => {
167
168
  enableMultiSort: clientFiltering, // TODO(cc) move to sorting options; default to false and let the table options in config flag this value.
168
169
  filterFns: { arrIncludesSome },
169
170
  getCoreRowModel: getCoreRowModel(),
171
+ getFacetedMinMaxValues: clientFiltering
172
+ ? getFacetedMinMaxValues()
173
+ : undefined,
170
174
  getFacetedRowModel: clientFiltering ? getFacetedRowModel() : undefined,
171
175
  getFacetedUniqueValues: clientFiltering
172
176
  ? getFacetedUniqueValuesWithArrayValues()
@@ -0,0 +1,34 @@
1
+ import React, { AnchorHTMLAttributes, ClassAttributes } from "react";
2
+ import { Link } from "../../../Links/components/Link/link";
3
+ import { BaseComponentProps } from "../../../types";
4
+
5
+ /**
6
+ * rehype-sanitize's default schema allows only a limited set of attributes on <a> elements:
7
+ * - aria-*
8
+ * - className
9
+ * - data-footnote-backref
10
+ * - data-footnote-ref
11
+ * - href
12
+ *
13
+ * By default, attributes such as:
14
+ * - download
15
+ * - rel
16
+ * - target
17
+ * are not permitted and will be stripped from anchor tags during sanitization.
18
+ *
19
+ * Note: This component currently does not support these excluded attributes.
20
+ */
21
+
22
+ export const Anchor = (
23
+ props: BaseComponentProps &
24
+ ClassAttributes<HTMLAnchorElement> &
25
+ AnchorHTMLAttributes<HTMLAnchorElement>
26
+ ): JSX.Element => {
27
+ return (
28
+ <Link
29
+ className={props.className}
30
+ label={props.children}
31
+ url={props.href || ""}
32
+ />
33
+ );
34
+ };
@@ -0,0 +1,41 @@
1
+ import styled from "@emotion/styled";
2
+ import { PALETTE } from "../../../../styles/common/constants/palette";
3
+ import { textBody500 } from "../../../../styles/common/mixins/fonts";
4
+
5
+ export const StyledTable = styled("table")`
6
+ border-collapse: collapse;
7
+ margin: 16px 0;
8
+ table-layout: fixed;
9
+ width: 100%;
10
+
11
+ thead {
12
+ all: unset;
13
+ display: table-header-group;
14
+ }
15
+
16
+ tbody {
17
+ all: unset;
18
+ display: table-row-group;
19
+ }
20
+
21
+ tr {
22
+ all: unset;
23
+ display: table-row;
24
+ }
25
+
26
+ td,
27
+ th {
28
+ display: table-cell;
29
+ padding: 2px 4px;
30
+ }
31
+
32
+ th {
33
+ ${textBody500}
34
+ border-bottom: 1px solid ${PALETTE.SMOKE_MAIN};
35
+ }
36
+
37
+ td {
38
+ font: inherit;
39
+ overflow-wrap: break-word;
40
+ }
41
+ `;
@@ -0,0 +1,13 @@
1
+ import { BaseComponentProps } from "components/types";
2
+ import React, { ClassAttributes, TableHTMLAttributes } from "react";
3
+ import { StyledTable } from "./table.styles";
4
+
5
+ export const Table = (
6
+ props: BaseComponentProps &
7
+ ClassAttributes<HTMLTableElement> &
8
+ TableHTMLAttributes<HTMLTableElement>
9
+ ): JSX.Element => {
10
+ return (
11
+ <StyledTable className={props.className}>{props.children}</StyledTable>
12
+ );
13
+ };
@@ -0,0 +1,8 @@
1
+ import { Components } from "rehype-react";
2
+ import { Anchor } from "./components/Anchor/anchor";
3
+ import { Table } from "./components/Table/table";
4
+
5
+ export const COMPONENTS: Partial<Components> = {
6
+ a: Anchor,
7
+ table: Table,
8
+ };
@@ -0,0 +1,16 @@
1
+ import styled from "@emotion/styled";
2
+
3
+ // See https://github.com/emotion-js/emotion/issues/1105.
4
+ // See https://github.com/emotion-js/emotion/releases/tag/%40emotion%2Fcache%4011.10.2.
5
+ const ignoreSsrWarning =
6
+ "/* emotion-disable-server-rendering-unsafe-selector-warning-please-do-not-use-this-the-warning-exists-for-a-reason */";
7
+
8
+ export const StyledContainer = styled("div")`
9
+ > *:first-child:not(style)${ignoreSsrWarning} {
10
+ margin-top: 0;
11
+ }
12
+
13
+ > *:last-child:not(style)${ignoreSsrWarning} {
14
+ margin-bottom: 0;
15
+ }
16
+ `;
@@ -0,0 +1,62 @@
1
+ import { Typography } from "@mui/material";
2
+ import React, { useEffect, useState } from "react";
3
+ import * as production from "react/jsx-runtime";
4
+ import rehypeRaw from "rehype-raw";
5
+ import rehypeReact, { Components } from "rehype-react";
6
+ import rehypeSanitize, { defaultSchema } from "rehype-sanitize";
7
+ import remarkGfm from "remark-gfm";
8
+ import remarkParse from "remark-parse";
9
+ import remarkRehype from "remark-rehype";
10
+ import { unified } from "unified";
11
+ import { TYPOGRAPHY_PROPS } from "../../styles/common/mui/typography";
12
+ import { COMPONENTS } from "./constants";
13
+ import { StyledContainer } from "./markdownRenderer.styles";
14
+ import { MarkdownRendererProps } from "./types";
15
+
16
+ export const MarkdownRenderer = ({
17
+ className,
18
+ components = COMPONENTS,
19
+ value,
20
+ }: MarkdownRendererProps): JSX.Element => {
21
+ const [element, setElement] = useState<JSX.Element | null>(null);
22
+ const [error, setError] = useState<string | null>(null);
23
+ const [componentOptions] = useState<Partial<Components>>(components);
24
+
25
+ useEffect(() => {
26
+ let cancelled = false;
27
+ setError(null);
28
+
29
+ const processor = unified()
30
+ .use(remarkParse)
31
+ .use(remarkGfm)
32
+ .use(remarkRehype, { allowDangerousHtml: true })
33
+ .use(rehypeRaw)
34
+ .use(rehypeSanitize, defaultSchema)
35
+ .use(rehypeReact, { ...production, components: componentOptions });
36
+
37
+ processor
38
+ .process(value)
39
+ .then((file) => {
40
+ if (!cancelled) setElement(file?.result);
41
+ })
42
+ .catch((err) => {
43
+ if (!cancelled) setError(err.message);
44
+ });
45
+
46
+ return (): void => {
47
+ cancelled = true;
48
+ };
49
+ }, [componentOptions, value]);
50
+
51
+ if (error)
52
+ return (
53
+ <Typography
54
+ color={TYPOGRAPHY_PROPS.COLOR.ERROR}
55
+ variant={TYPOGRAPHY_PROPS.VARIANT.TEXT_BODY_SMALL_400}
56
+ >
57
+ {error}
58
+ </Typography>
59
+ );
60
+
61
+ return <StyledContainer className={className}>{element}</StyledContainer>;
62
+ };
@@ -0,0 +1,7 @@
1
+ import { Components } from "rehype-react";
2
+ import { BaseComponentProps } from "../types";
3
+
4
+ export interface MarkdownRendererProps extends BaseComponentProps {
5
+ components?: Partial<Components>;
6
+ value: string;
7
+ }
@@ -0,0 +1,15 @@
1
+ import { RowData } from "@tanstack/react-table";
2
+
3
+ /**
4
+ * Type guard to check if a row has a specific key.
5
+ * Useful for generic accessor functions.
6
+ * @param row - The row to check.
7
+ * @param key - The key to check.
8
+ * @returns True if the row has the specified key, false otherwise.
9
+ */
10
+ export function rowHasKey<T extends RowData, K extends PropertyKey, TValue>(
11
+ row: T,
12
+ key: K
13
+ ): row is T & Record<K, TValue> {
14
+ return row != null && typeof row === "object" && key in row;
15
+ }