@verdaccio/ui-components 3.0.0-next-7.6 → 3.0.0-next-7.8

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 (302) hide show
  1. package/.eslintrc +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/build/Theme/theme.js +3 -3
  4. package/build/components/ActionBar/ActionBar.js +3 -1
  5. package/build/components/ActionBar/ActionBarAction.js +7 -6
  6. package/build/components/Author/Author.js +13 -42
  7. package/build/components/CopyClipboard/utils.js +3 -12
  8. package/build/components/Dependencies/Dependencies.js +27 -18
  9. package/build/components/Dependencies/DependencyBlock.js +34 -19
  10. package/build/components/Developers/DeveloperType.js +12 -0
  11. package/build/components/Developers/Developers.js +20 -22
  12. package/build/components/Developers/Title.js +5 -9
  13. package/build/components/Developers/index.js +4 -4
  14. package/build/components/Distribution/Dist.js +1 -1
  15. package/build/components/Distribution/styles.js +10 -23
  16. package/build/components/Engines/Engines.js +7 -1
  17. package/build/components/Engines/styles.js +6 -6
  18. package/build/components/FundButton/FundButton.js +7 -8
  19. package/build/components/HeaderInfoDialog/HeaderInfoDialog.js +2 -1
  20. package/build/components/Icons/DevsIcons/CommonJS.js +1 -12
  21. package/build/components/Icons/DevsIcons/ES6Module.js +2 -13
  22. package/build/components/Icons/DevsIcons/Git.js +1 -12
  23. package/build/components/Icons/DevsIcons/NodeJS.js +1 -12
  24. package/build/components/Icons/DevsIcons/TypeScript.js +1 -12
  25. package/build/components/Icons/FileBinary.js +11 -5
  26. package/build/components/Icons/Law.js +12 -6
  27. package/build/components/Icons/Managers/Npm.js +1 -12
  28. package/build/components/Icons/Managers/Pnpm.js +1 -12
  29. package/build/components/Icons/Managers/Yarn.js +1 -12
  30. package/build/components/Install/Install.js +23 -18
  31. package/build/components/Install/InstallListItem.js +19 -18
  32. package/build/components/Keywords/KeywordListItems.js +34 -0
  33. package/build/components/Keywords/Keywords.js +39 -0
  34. package/build/{sections/Header/RegistryInfoDialog → components/Keywords}/index.js +2 -2
  35. package/build/components/Link/Link.js +4 -16
  36. package/build/components/Link/index.js +1 -1
  37. package/build/components/LinkExternal/LinkExternal.js +32 -0
  38. package/build/components/LinkExternal/index.js +14 -0
  39. package/build/components/Loading/styles.js +3 -2
  40. package/build/components/LoginDialog/LoginDialog.js +12 -1
  41. package/build/components/LoginDialog/LoginDialogCloseButton.js +3 -2
  42. package/build/components/LoginDialog/LoginDialogForm.js +2 -14
  43. package/build/components/LoginDialog/LoginDialogFormError.js +3 -2
  44. package/build/components/Logo/Logo.js +13 -6
  45. package/build/components/Package/Package.js +43 -34
  46. package/build/components/Package/styles.js +46 -58
  47. package/build/components/Person/Person.js +48 -0
  48. package/build/components/Person/PersonTooltip.js +22 -0
  49. package/build/components/{Package/Tag → Person}/index.js +2 -2
  50. package/build/components/Person/utils.js +17 -0
  51. package/build/components/RawViewer/RawViewer.js +11 -5
  52. package/build/components/Readme/Readme.js +15 -6
  53. package/build/components/Readme/Readme.js.map +1 -1
  54. package/build/components/Readme/Readme.spec.js +19 -0
  55. package/build/components/Readme/Readme.spec.js.map +1 -1
  56. package/build/components/Readme/utils.js +2 -5
  57. package/build/components/RegistryInfoDialog/styles.js +9 -10
  58. package/build/components/Repository/Repository.js +19 -31
  59. package/build/components/Search/AutoComplete/styles.js +4 -41
  60. package/build/components/Search/Search.js +4 -2
  61. package/build/components/Search/SearchItem.js +51 -38
  62. package/build/components/Search/utils.js +23 -0
  63. package/build/components/SettingsMenu/SettingsMenu.js +35 -9
  64. package/build/components/SideBarTitle/SideBarTitle.js +16 -6
  65. package/build/components/UpLinks/UpLinks.js +35 -6
  66. package/build/components/UpLinks/UplinkLink.js +22 -0
  67. package/build/components/UpLinks/styles.js +5 -4
  68. package/build/components/Versions/HistoryList.js +8 -5
  69. package/build/components/Versions/TagList.js +8 -4
  70. package/build/components/Versions/Versions.js +30 -7
  71. package/build/index.js +27 -3
  72. package/build/providers/AppConfigurationProvider/AppConfigurationProvider.js +1 -0
  73. package/build/providers/PersistenceSettingProvider/PersistenceSettingProvider.js +1 -0
  74. package/build/sections/Detail/Detail.js +6 -2
  75. package/build/sections/Detail/Tabs.js +4 -3
  76. package/build/sections/Footer/Footer.js +17 -8
  77. package/build/sections/Footer/styles.js +13 -10
  78. package/build/sections/Header/HeaderLeft.js +14 -17
  79. package/build/sections/Header/HeaderMenu.js +1 -1
  80. package/build/sections/Header/HeaderRight.js +12 -3
  81. package/build/sections/Header/HeaderSettingsDialog.js +2 -2
  82. package/build/sections/Header/styles.js +43 -82
  83. package/build/sections/SideBar/Sidebar.js +10 -15
  84. package/build/src/Theme/ThemeProvider.test.d.ts +1 -0
  85. package/build/src/components/Author/Author.d.ts +0 -5
  86. package/build/src/components/CopyClipboard/CopyToClipBoard.test.d.ts +1 -0
  87. package/build/src/components/Dependencies/Dependencies.d.ts +0 -1
  88. package/build/src/components/Developers/DeveloperType.d.ts +4 -0
  89. package/build/src/components/Developers/Developers.d.ts +1 -4
  90. package/build/src/components/Developers/Title.d.ts +1 -4
  91. package/build/src/components/Developers/index.d.ts +2 -1
  92. package/build/src/components/Distribution/styles.d.ts +0 -1
  93. package/build/src/components/ErrorBoundary/ErrorBoundary.test.d.ts +1 -0
  94. package/build/src/components/HeaderInfoDialog/HeaderInfoDialog.test.d.ts +1 -0
  95. package/build/src/components/Heading/Heading.test.d.ts +1 -0
  96. package/build/src/components/Icons/Icons.test.d.ts +1 -0
  97. package/build/src/components/Install/InstallListItem.d.ts +1 -1
  98. package/build/src/components/Keywords/KeywordListItems.d.ts +5 -0
  99. package/build/src/components/Keywords/Keywords.d.ts +6 -0
  100. package/build/src/components/Keywords/Keywords.stories.d.ts +5 -0
  101. package/build/src/components/Keywords/Keywords.test.d.ts +1 -0
  102. package/build/src/components/Keywords/index.d.ts +1 -0
  103. package/build/src/components/Link/Link.d.ts +0 -3
  104. package/build/src/components/Link/Link.test.d.ts +1 -0
  105. package/build/src/components/Link/index.d.ts +1 -1
  106. package/build/src/components/LinkExternal/LinkExternal.d.ts +3 -0
  107. package/build/src/components/LinkExternal/LinkExternal.test.d.ts +1 -0
  108. package/build/src/components/LinkExternal/index.d.ts +1 -0
  109. package/build/src/components/Logo/Logo.d.ts +1 -0
  110. package/build/src/components/Logo/Logo.stories.d.ts +5 -0
  111. package/build/src/components/Package/Package.d.ts +1 -1
  112. package/build/src/components/Package/styles.d.ts +0 -8
  113. package/build/src/components/Person/Person.d.ts +9 -0
  114. package/build/src/components/Person/Person.test.d.ts +1 -0
  115. package/build/src/components/Person/PersonTooltip.d.ts +6 -0
  116. package/build/src/components/Person/index.d.ts +1 -0
  117. package/build/src/components/Person/utils.d.ts +3 -0
  118. package/build/src/components/Readme/Readme.d.ts +0 -1
  119. package/build/src/components/Search/AutoComplete/styles.d.ts +0 -8
  120. package/build/src/components/Search/utils.d.ts +1 -0
  121. package/build/src/components/SettingsMenu/SettingsMenu.d.ts +2 -2
  122. package/build/src/components/SettingsMenu/SettingsMenu.test.d.ts +1 -0
  123. package/build/src/components/UpLinks/UplinkLink.d.ts +6 -0
  124. package/build/src/components/Versions/Versions.d.ts +1 -0
  125. package/build/src/index.d.ts +4 -1
  126. package/build/src/sections/Detail/Tabs.d.ts +1 -0
  127. package/build/src/sections/Header/styles.d.ts +0 -7
  128. package/build/src/store/models/routes.d.ts +8 -0
  129. package/build/src/store/models/utils.d.ts +1 -0
  130. package/build/src/types/packageMeta.d.ts +7 -5
  131. package/build/src/utils/routes.d.ts +1 -0
  132. package/build/src/utils/utils.d.ts +1 -0
  133. package/build/store/models/configuration.js +2 -1
  134. package/build/store/models/login.js +4 -2
  135. package/build/store/models/manifest.js +5 -3
  136. package/build/store/models/packages.js +4 -2
  137. package/build/store/models/routes.js +18 -0
  138. package/build/store/models/search.js +4 -2
  139. package/build/store/models/utils.js +10 -0
  140. package/build/utils/routes.js +1 -0
  141. package/build/utils/utils.js +20 -1
  142. package/package.json +4 -4
  143. package/src/AppTest/AppRoute.test.tsx +65 -0
  144. package/src/Theme/ThemeProvider.test.tsx +27 -0
  145. package/src/Theme/theme.ts +3 -3
  146. package/src/components/ActionBar/ActionBar.test.tsx +36 -15
  147. package/src/components/ActionBar/ActionBar.tsx +1 -1
  148. package/src/components/ActionBar/ActionBarAction.tsx +8 -5
  149. package/src/components/Author/Author.test.tsx +7 -1
  150. package/src/components/Author/Author.tsx +3 -41
  151. package/src/components/CopyClipboard/CopyToClipBoard.test.tsx +23 -0
  152. package/src/components/CopyClipboard/utils.ts +2 -13
  153. package/src/components/Dependencies/Dependencies.stories.tsx +11 -11
  154. package/src/components/Dependencies/Dependencies.tsx +28 -25
  155. package/src/components/Dependencies/DependencyBlock.test.tsx +19 -6
  156. package/src/components/Dependencies/DependencyBlock.tsx +28 -14
  157. package/src/components/Developers/DeveloperType.tsx +4 -0
  158. package/src/components/Developers/Developers.test.tsx +37 -10
  159. package/src/components/Developers/Developers.tsx +13 -13
  160. package/src/components/Developers/Title.tsx +1 -5
  161. package/src/components/Developers/index.ts +2 -1
  162. package/src/components/Distribution/Dist.test.tsx +1 -1
  163. package/src/components/Distribution/Dist.tsx +2 -4
  164. package/src/components/Distribution/styles.ts +0 -6
  165. package/src/components/Engines/Engines.tsx +6 -2
  166. package/src/components/Engines/styles.ts +1 -1
  167. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +39 -0
  168. package/src/components/FundButton/FundButton.tsx +5 -5
  169. package/src/components/HeaderInfoDialog/HeaderInfoDialog.test.tsx +51 -0
  170. package/src/components/HeaderInfoDialog/HeaderInfoDialog.tsx +2 -2
  171. package/src/components/Heading/Heading.test.tsx +20 -0
  172. package/src/components/Icons/DevsIcons/CommonJS.tsx +1 -7
  173. package/src/components/Icons/DevsIcons/ES6Module.tsx +2 -8
  174. package/src/components/Icons/DevsIcons/Git.tsx +1 -7
  175. package/src/components/Icons/DevsIcons/NodeJS.tsx +1 -7
  176. package/src/components/Icons/DevsIcons/TypeScript.tsx +1 -7
  177. package/src/components/Icons/FileBinary.tsx +2 -1
  178. package/src/components/Icons/Icons.stories.tsx +21 -5
  179. package/src/components/Icons/Icons.test.tsx +70 -0
  180. package/src/components/Icons/Law.tsx +2 -1
  181. package/src/components/Icons/Managers/Npm.tsx +1 -7
  182. package/src/components/Icons/Managers/Pnpm.tsx +1 -7
  183. package/src/components/Icons/Managers/Yarn.tsx +1 -7
  184. package/src/components/Install/Install.test.tsx +54 -13
  185. package/src/components/Install/Install.tsx +12 -11
  186. package/src/components/Install/InstallListItem.tsx +12 -11
  187. package/src/components/Keywords/KeywordListItems.tsx +22 -0
  188. package/src/components/Keywords/Keywords.stories.tsx +19 -0
  189. package/src/components/Keywords/Keywords.test.tsx +57 -0
  190. package/src/components/Keywords/Keywords.tsx +35 -0
  191. package/src/components/Keywords/index.ts +1 -0
  192. package/src/components/Link/Link.test.tsx +25 -0
  193. package/src/components/Link/Link.tsx +4 -18
  194. package/src/components/Link/index.ts +1 -1
  195. package/src/components/LinkExternal/LinkExternal.test.tsx +18 -0
  196. package/src/components/LinkExternal/LinkExternal.tsx +22 -0
  197. package/src/components/LinkExternal/index.ts +1 -0
  198. package/src/components/Loading/Loading.test.tsx +8 -5
  199. package/src/components/Loading/styles.ts +1 -0
  200. package/src/components/LoginDialog/LoginDialog.test.tsx +1 -1
  201. package/src/components/LoginDialog/LoginDialog.tsx +10 -1
  202. package/src/components/LoginDialog/LoginDialogCloseButton.tsx +1 -0
  203. package/src/components/LoginDialog/LoginDialogForm.tsx +0 -2
  204. package/src/components/LoginDialog/LoginDialogFormError.tsx +1 -1
  205. package/src/components/Logo/Logo.stories.tsx +16 -0
  206. package/src/components/Logo/Logo.test.tsx +8 -1
  207. package/src/components/Logo/Logo.tsx +19 -3
  208. package/src/components/Package/Package.test.tsx +47 -14
  209. package/src/components/Package/Package.tsx +41 -44
  210. package/src/components/Package/styles.ts +7 -17
  211. package/src/components/PackageList/Packagelist.test.tsx +1 -0
  212. package/src/components/Person/Person.test.tsx +50 -0
  213. package/src/components/Person/Person.tsx +45 -0
  214. package/src/components/Person/PersonTooltip.tsx +26 -0
  215. package/src/components/Person/index.ts +1 -0
  216. package/src/components/Person/utils.ts +20 -0
  217. package/src/components/RawViewer/RawViewer.tsx +8 -4
  218. package/src/components/Readme/Readme.spec.tsx +17 -0
  219. package/src/components/Readme/Readme.tsx +10 -5
  220. package/src/components/Readme/utils.ts +2 -5
  221. package/src/components/RegistryInfoDialog/styles.ts +5 -4
  222. package/src/components/Repository/Repository.tsx +9 -14
  223. package/src/components/Search/AutoComplete/styles.ts +7 -0
  224. package/src/components/Search/Search.test.tsx +36 -9
  225. package/src/components/Search/Search.tsx +4 -2
  226. package/src/components/Search/SearchItem.tsx +19 -8
  227. package/src/components/Search/utils.ts +17 -0
  228. package/src/components/SettingsMenu/SettingsMenu.test.tsx +41 -0
  229. package/src/components/SettingsMenu/SettingsMenu.tsx +48 -10
  230. package/src/components/SideBarTitle/SideBarTitle.tsx +19 -3
  231. package/src/components/UpLinks/UpLinks.test.tsx +7 -0
  232. package/src/components/UpLinks/UpLinks.tsx +38 -15
  233. package/src/components/UpLinks/UplinkLink.tsx +20 -0
  234. package/src/components/UpLinks/styles.ts +1 -0
  235. package/src/components/Versions/HistoryList.tsx +9 -4
  236. package/src/components/Versions/TagList.tsx +4 -3
  237. package/src/components/Versions/Versions.test.tsx +2 -0
  238. package/src/components/Versions/Versions.tsx +57 -32
  239. package/src/index.ts +4 -1
  240. package/src/providers/AppConfigurationProvider/AppConfigurationProvider.tsx +1 -0
  241. package/src/providers/PersistenceSettingProvider/PersistenceSettingProvider.tsx +2 -0
  242. package/src/providers/VersionProvider/VersionProvider.test.tsx +3 -3
  243. package/src/sections/Detail/Detail.test.tsx +8 -1
  244. package/src/sections/Detail/Detail.tsx +11 -4
  245. package/src/sections/Detail/Tabs.tsx +5 -2
  246. package/src/sections/Footer/Footer.tsx +15 -4
  247. package/src/sections/Footer/styles.ts +4 -0
  248. package/src/sections/Header/Header.test.tsx +12 -10
  249. package/src/sections/Header/HeaderLeft.tsx +19 -11
  250. package/src/sections/Header/HeaderMenu.tsx +1 -1
  251. package/src/sections/Header/HeaderRight.tsx +14 -3
  252. package/src/sections/Header/HeaderSettingsDialog.tsx +1 -1
  253. package/src/sections/Header/RegistryInfoContent/RegistryInfoContent.test.tsx +1 -1
  254. package/src/sections/Header/styles.ts +4 -16
  255. package/src/sections/Home/Home.test.tsx +12 -4
  256. package/src/sections/SideBar/Sidebar.test.tsx +20 -13
  257. package/src/sections/SideBar/Sidebar.tsx +5 -9
  258. package/src/store/api.test.ts +16 -0
  259. package/src/store/models/configuration.ts +2 -1
  260. package/src/store/models/login.ts +10 -12
  261. package/src/store/models/manifest.ts +6 -5
  262. package/src/store/models/packages.ts +4 -5
  263. package/src/store/models/routes.ts +10 -0
  264. package/src/store/models/search.ts +4 -2
  265. package/src/store/models/utils.ts +3 -0
  266. package/src/types/packageMeta.ts +7 -5
  267. package/src/utils/routes.ts +1 -0
  268. package/src/utils/utils.test.ts +34 -0
  269. package/src/utils/utils.ts +20 -5
  270. package/tsconfig.json +1 -1
  271. package/build/components/Developers/styles.js +0 -65
  272. package/build/components/Distribution/utils.js +0 -25
  273. package/build/components/Distribution/utilts.spec.js +0 -18
  274. package/build/components/Package/Tag/Tag.js +0 -14
  275. package/build/components/Package/Tag/styles.js +0 -24
  276. package/build/sections/Header/RegistryInfoDialog/RegistryInfoDialog.js +0 -36
  277. package/build/sections/Header/RegistryInfoDialog/styles.js +0 -46
  278. package/build/sections/Header/RegistryInfoDialog/types.js +0 -6
  279. package/build/src/components/Developers/styles.d.ts +0 -11
  280. package/build/src/components/Distribution/utils.d.ts +0 -7
  281. package/build/src/components/Package/Tag/Tag.d.ts +0 -6
  282. package/build/src/components/Package/Tag/index.d.ts +0 -1
  283. package/build/src/components/Package/Tag/styles.d.ts +0 -5
  284. package/build/src/sections/Header/RegistryInfoDialog/RegistryInfoDialog.d.ts +0 -4
  285. package/build/src/sections/Header/RegistryInfoDialog/index.d.ts +0 -1
  286. package/build/src/sections/Header/RegistryInfoDialog/styles.d.ts +0 -13
  287. package/build/src/sections/Header/RegistryInfoDialog/types.d.ts +0 -7
  288. package/src/components/Developers/styles.ts +0 -31
  289. package/src/components/Distribution/utils.ts +0 -32
  290. package/src/components/Distribution/utilts.spec.ts +0 -17
  291. package/src/components/Package/Tag/Tag.test.tsx +0 -15
  292. package/src/components/Package/Tag/Tag.tsx +0 -11
  293. package/src/components/Package/Tag/index.ts +0 -1
  294. package/src/components/Package/Tag/styles.ts +0 -11
  295. package/src/components/Search/AutoComplete/styles.tsx +0 -42
  296. package/src/sections/Header/RegistryInfoDialog/RegistryInfoDialog.tsx +0 -31
  297. package/src/sections/Header/RegistryInfoDialog/index.ts +0 -1
  298. package/src/sections/Header/RegistryInfoDialog/styles.ts +0 -21
  299. package/src/sections/Header/RegistryInfoDialog/types.ts +0 -8
  300. /package/build/components/Icons/DevsIcons/{es6modules.svg → es6module.svg} +0 -0
  301. /package/build/src/{components/Package/Tag/Tag.test.d.ts → AppTest/AppRoute.test.d.ts} +0 -0
  302. /package/src/components/Icons/DevsIcons/{es6modules.svg → es6module.svg} +0 -0
@@ -3,6 +3,7 @@ import Dialog from '@mui/material/Dialog';
3
3
  import DialogContent from '@mui/material/DialogContent';
4
4
  import DialogTitle from '@mui/material/DialogTitle';
5
5
  import IconButton from '@mui/material/IconButton';
6
+ import { useTheme } from '@mui/styles';
6
7
  import React from 'react';
7
8
  import { useTranslation } from 'react-i18next';
8
9
  import ReactJson from 'react-json-view';
@@ -22,6 +23,7 @@ const ViewerTitle = (props: ViewerTitleProps) => {
22
23
  {onClose ? (
23
24
  <IconButton
24
25
  aria-label="close"
26
+ data-testid="close-raw-viewer"
25
27
  onClick={onClose}
26
28
  sx={{
27
29
  position: 'absolute',
@@ -46,18 +48,20 @@ type Props = {
46
48
  /* eslint-disable verdaccio/jsx-spread */
47
49
  const RawViewer: React.FC<Props> = ({ isOpen = false, onClose, packageMeta }) => {
48
50
  const { t } = useTranslation();
51
+ const theme = useTheme();
49
52
  return (
50
53
  <Dialog data-testid={'rawViewer--dialog'} fullScreen={true} open={isOpen}>
51
54
  <ViewerTitle id="viewer-title" onClose={onClose}>
52
- {t('action-bar-action.raw')}
55
+ {t('action-bar-action.raw-title', { package: packageMeta.latest.name })}
53
56
  </ViewerTitle>
54
57
  <DialogContent>
55
58
  <ReactJson
56
- collapseStringsAfterLength={40}
57
- collapsed={true}
58
- enableClipboard={false}
59
+ collapseStringsAfterLength={200}
60
+ collapsed={2}
61
+ enableClipboard={true}
59
62
  groupArraysAfterLength={10}
60
63
  src={packageMeta as any}
64
+ theme={theme?.palette.mode == 'light' ? 'bright:inverted' : 'bright'}
61
65
  />
62
66
  </DialogContent>
63
67
  </Dialog>
@@ -15,4 +15,21 @@ describe('<Readme /> component', () => {
15
15
  const wrapper = render(<Readme description="<h1>This is a test string</h1>" />);
16
16
  expect(wrapper.getByText('This is a test string')).toBeInTheDocument();
17
17
  });
18
+
19
+ test('should sanitize html', () => {
20
+ const markdown = `<script>alert('test')</script>`;
21
+ const wrapper = render(<Readme description={markdown} />);
22
+ expect(wrapper.queryAllByText('test')).toHaveLength(0);
23
+ });
24
+
25
+ test('should highlight code', () => {
26
+ const markdown = `\`\`\`js\nconst test = 1 + 2;\n\`\`\``;
27
+ const wrapper = render(<Readme description={markdown} />);
28
+ expect(wrapper.getByText('const')).toBeInTheDocument();
29
+ expect(wrapper.getByText('const')).toHaveClass('hljs-keyword');
30
+ expect(wrapper.getByText('1')).toBeInTheDocument();
31
+ expect(wrapper.getByText('1')).toHaveClass('hljs-number');
32
+ expect(wrapper.getByText('2')).toBeInTheDocument();
33
+ expect(wrapper.getByText('2')).toHaveClass('hljs-number');
34
+ });
18
35
  });
@@ -2,8 +2,6 @@ import styled from '@emotion/styled';
2
2
  import Box from '@mui/material/Box';
3
3
  import Card from '@mui/material/Card';
4
4
  import CardContent from '@mui/material/CardContent';
5
- import { useTheme } from '@mui/styles';
6
- import 'highlight.js/styles/default.css';
7
5
  import React from 'react';
8
6
 
9
7
  import { useCustomTheme } from '../../';
@@ -14,12 +12,19 @@ import { parseReadme } from './utils';
14
12
  const Readme: React.FC<Props> = ({ description }) => {
15
13
  // @ts-ignore
16
14
  const { isDarkMode } = useCustomTheme();
17
- const theme = useTheme();
15
+
16
+ // Stackoverflow theme fits well to Verdaccio dark colors
17
+ // https://highlightjs.org/examples
18
+ if (isDarkMode) {
19
+ require('highlight.js/styles/github-dark.css');
20
+ } else {
21
+ require('highlight.js/styles/github.css');
22
+ }
18
23
 
19
24
  return (
20
- <Card>
25
+ <Card sx={{ mb: 2 }}>
21
26
  <CardContent>
22
- <Box data-testid="readme" sx={{ margin: theme.spacing(1) }}>
27
+ <Box data-testid="readme" sx={{ m: 2 }}>
23
28
  <Wrapper
24
29
  className={`markdown-body ${isDarkMode ? 'markdown-dark' : 'markdown-light'}`}
25
30
  dangerouslySetInnerHTML={{ __html: parseReadme(description) as string }}
@@ -21,9 +21,6 @@ marked.setOptions({
21
21
  });
22
22
 
23
23
  export function parseReadme(readme: string): string | void {
24
- if (typeof readme === 'string') {
25
- const html = marked.parse(readme);
26
- return DOMPurify.sanitize(html);
27
- }
28
- return '';
24
+ const html = marked.parse(readme);
25
+ return DOMPurify.sanitize(html);
29
26
  }
@@ -4,10 +4,11 @@ import DialogTitle from '@mui/material/DialogTitle';
4
4
 
5
5
  import { Theme } from '../../';
6
6
 
7
- export const Title = styled(DialogTitle)<{ theme?: Theme }>((props) => ({
8
- backgroundColor: props.theme?.palette.primary.main,
9
- color: props.theme?.palette.white,
10
- fontSize: props.theme?.fontSize.lg,
7
+ export const Title = styled(DialogTitle)<{ theme?: Theme }>(({ theme }) => ({
8
+ backgroundColor:
9
+ theme?.palette.mode === 'light' ? theme?.palette.primary.main : theme?.palette.cyanBlue,
10
+ color: theme?.palette.white,
11
+ fontSize: theme?.fontSize.lg,
11
12
  }));
12
13
 
13
14
  export const Content = styled(DialogContent)<{ theme?: Theme }>(({ theme }) => ({
@@ -4,6 +4,7 @@ import List from '@mui/material/List';
4
4
  import ListItem from '@mui/material/ListItem';
5
5
  import ListItemText from '@mui/material/ListItemText';
6
6
  import Typography from '@mui/material/Typography';
7
+ import { useTheme } from '@mui/styles';
7
8
  import React from 'react';
8
9
  import { useTranslation } from 'react-i18next';
9
10
 
@@ -11,20 +12,13 @@ import { Theme } from '../../Theme';
11
12
  import { url as urlUtils } from '../../utils';
12
13
  import CopyClipboard from '../CopyClipboard';
13
14
  import { Git } from '../Icons';
14
- import { Link } from '../Link';
15
+ import LinkExternal from '../LinkExternal';
15
16
 
16
17
  const StyledText = styled(Typography)<{ theme?: Theme }>((props) => ({
17
18
  fontWeight: props.theme?.fontWeight.bold,
18
19
  textTransform: 'capitalize',
19
20
  }));
20
21
 
21
- const GithubLink = styled(Link)<{ theme?: Theme }>(({ theme }) => ({
22
- color: theme?.palette.mode === 'light' ? theme?.palette.primary.main : theme?.palette.white,
23
- ':hover': {
24
- color: theme?.palette.dodgerBlue,
25
- },
26
- }));
27
-
28
22
  const RepositoryListItem = styled(ListItem)({
29
23
  padding: 0,
30
24
  ':hover': {
@@ -33,18 +27,19 @@ const RepositoryListItem = styled(ListItem)({
33
27
  });
34
28
 
35
29
  const RepositoryListItemText = styled(ListItemText)({
36
- padding: '0 10px',
30
+ padding: '0 0 0 10px',
37
31
  margin: 0,
38
32
  });
39
33
 
40
34
  const RepositoryAvatar = styled(Avatar)({
41
- borderRadius: '0px',
42
- padding: '0',
35
+ padding: 0,
36
+ marginLeft: 0,
43
37
  backgroundColor: 'transparent',
44
38
  });
45
39
 
46
40
  const Repository: React.FC<{ packageMeta: any }> = ({ packageMeta }) => {
47
41
  const { t } = useTranslation();
42
+ const theme = useTheme();
48
43
  const url = packageMeta?.latest?.repository?.url;
49
44
  if (!url || !urlUtils.isURL(url)) {
50
45
  return null;
@@ -66,15 +61,15 @@ const Repository: React.FC<{ packageMeta: any }> = ({ packageMeta }) => {
66
61
  subheader={<StyledText variant="subtitle1">{t('sidebar.repository.title')}</StyledText>}
67
62
  >
68
63
  <RepositoryListItem>
69
- <RepositoryAvatar sx={{ backgroundColor: '#fff' }}>
64
+ <RepositoryAvatar sx={{ bgcolor: theme.palette.white }}>
70
65
  <Git />
71
66
  </RepositoryAvatar>
72
67
  <RepositoryListItemText
73
68
  primary={
74
69
  <CopyClipboard dataTestId="repositoryID" text={repositoryURL} title={repositoryURL}>
75
- <GithubLink external={true} to={repositoryURL} variant="outline">
70
+ <LinkExternal to={repositoryURL} variant="outline">
76
71
  {repositoryURL}
77
- </GithubLink>
72
+ </LinkExternal>
78
73
  </CopyClipboard>
79
74
  }
80
75
  />
@@ -0,0 +1,7 @@
1
+ import styled from '@emotion/styled';
2
+
3
+ export const Wrapper = styled('div')({
4
+ width: '100%',
5
+ position: 'relative',
6
+ zIndex: 1,
7
+ });
@@ -4,6 +4,7 @@ import { BrowserRouter as Router } from 'react-router-dom';
4
4
  import { api, store } from '../../';
5
5
  import { fireEvent, renderWithStore, screen, waitFor } from '../../test/test-react-testing-library';
6
6
  import Search from './Search';
7
+ import { cleanDescription } from './utils';
7
8
 
8
9
  jest.mock('lodash/debounce', () =>
9
10
  jest.fn((fn) => {
@@ -45,7 +46,7 @@ describe('<Search /> component', () => {
45
46
  });
46
47
 
47
48
  test('handleSearch: when user type package name in search component, show suggestions', async () => {
48
- const { getByPlaceholderText, getAllByText } = renderWithStore(
49
+ const { getByPlaceholderText, findAllByText } = renderWithStore(
49
50
  <ComponentToBeRendered />,
50
51
  store
51
52
  );
@@ -57,14 +58,14 @@ describe('<Search /> component', () => {
57
58
 
58
59
  expect(autoCompleteInput).toHaveAttribute('value', 'verdaccio');
59
60
 
60
- const suggestionsElements = await waitFor(() => getAllByText('verdaccio', { exact: true }));
61
+ const suggestionsElements = await waitFor(() => findAllByText('verdaccio', { exact: true }));
61
62
 
62
63
  expect(suggestionsElements).toHaveLength(1);
63
64
  expect(api.request).toHaveBeenCalledTimes(1);
64
65
  });
65
66
 
66
67
  test('onBlur: should cancel all search requests', async () => {
67
- const { getByPlaceholderText, getAllByText } = renderWithStore(
68
+ const { getByPlaceholderText, findAllByText } = renderWithStore(
68
69
  <ComponentToBeRendered />,
69
70
  store
70
71
  );
@@ -75,7 +76,7 @@ describe('<Search /> component', () => {
75
76
  fireEvent.change(autoCompleteInput, { target: { value: 'verdaccio' } });
76
77
  expect(autoCompleteInput).toHaveAttribute('value', 'verdaccio');
77
78
 
78
- const suggestionsElements = await waitFor(() => getAllByText('verdaccio', { exact: true }));
79
+ const suggestionsElements = await waitFor(() => findAllByText('verdaccio', { exact: true }));
79
80
  expect(suggestionsElements).toHaveLength(1);
80
81
  expect(api.request).toHaveBeenCalledTimes(1);
81
82
 
@@ -110,7 +111,7 @@ describe('<Search /> component', () => {
110
111
  });
111
112
 
112
113
  test('handlePackagesClearRequested: should clear suggestions', async () => {
113
- const { getByPlaceholderText, getAllByText } = renderWithStore(
114
+ const { getByPlaceholderText, findAllByText } = renderWithStore(
114
115
  <ComponentToBeRendered />,
115
116
  store
116
117
  );
@@ -120,18 +121,18 @@ describe('<Search /> component', () => {
120
121
  fireEvent.change(autoCompleteInput, { target: { value: 'verdaccio' } });
121
122
  expect(autoCompleteInput).toHaveAttribute('value', 'verdaccio');
122
123
 
123
- const suggestionsElements = await waitFor(() => getAllByText('verdaccio', { exact: true }));
124
+ const suggestionsElements = await waitFor(() => findAllByText('verdaccio', { exact: true }));
124
125
  expect(suggestionsElements).toHaveLength(1);
125
126
 
126
127
  fireEvent.change(autoCompleteInput, { target: { value: ' ' } });
127
128
  const listBoxElement = screen.queryAllByRole('listbox');
128
- // // when the page redirects, the list box should be empty again
129
+ // when the page redirects, the list box should be empty again
129
130
  expect(listBoxElement).toHaveLength(0);
130
131
  expect(api.request).toHaveBeenCalledTimes(1);
131
132
  });
132
133
 
133
134
  test('handleClickSearch: should change the window location on click or return key', async () => {
134
- const { getByPlaceholderText, getAllByText } = renderWithStore(
135
+ const { getByPlaceholderText, findAllByText } = renderWithStore(
135
136
  <ComponentToBeRendered />,
136
137
  store
137
138
  );
@@ -141,7 +142,7 @@ describe('<Search /> component', () => {
141
142
  fireEvent.change(autoCompleteInput, { target: { value: 'verdaccio' } });
142
143
  expect(autoCompleteInput).toHaveAttribute('value', 'verdaccio');
143
144
 
144
- const suggestionsElements = await waitFor(() => getAllByText('verdaccio', { exact: true }));
145
+ const suggestionsElements = await waitFor(() => findAllByText('verdaccio', { exact: true }));
145
146
  // console.log('suggestionsElements', suggestionsElements);
146
147
  expect(suggestionsElements).toHaveLength(1);
147
148
  // click on the second suggestion
@@ -151,3 +152,29 @@ describe('<Search /> component', () => {
151
152
  expect(listBoxElement).toHaveLength(0);
152
153
  });
153
154
  });
155
+
156
+ describe('cleanDescription', () => {
157
+ test('should return plain text', () => {
158
+ const description = 'Hello, Mars!';
159
+ const output = cleanDescription(description);
160
+ expect(output).toBe(description);
161
+ });
162
+
163
+ test('should remove html tags from description', () => {
164
+ const description = '<h1>verdaccio</h1>';
165
+ const output = cleanDescription(description);
166
+ expect(output).toBe('verdaccio');
167
+ });
168
+
169
+ test('should remove markdown links from description', () => {
170
+ const description = '[verdaccio](https://verdaccio.org)';
171
+ const output = cleanDescription(description);
172
+ expect(output).toBe('verdaccio');
173
+ });
174
+
175
+ test('should remove markdown links', () => {
176
+ const description = '[![NPM version](https://img.shields.io/npm/latest.svg)]';
177
+ const output = cleanDescription(description);
178
+ expect(output).toBe('NPM version');
179
+ });
180
+ });
@@ -9,6 +9,7 @@ import { RouteComponentProps, withRouter } from 'react-router';
9
9
  import { SearchResultWeb } from '@verdaccio/types';
10
10
 
11
11
  import { Dispatch, RootState, useConfig } from '../../';
12
+ import { Route } from '../../utils';
12
13
  import AutoComplete from './AutoComplete';
13
14
  import SearchItem from './SearchItem';
14
15
  import { StyledInputAdornment, StyledTextField } from './styles';
@@ -27,6 +28,7 @@ const Search: React.FC<RouteComponentProps> = ({ history }) => {
27
28
  const { suggestions } = useSelector((state: RootState) => state.search);
28
29
  const isLoading = useSelector((state: RootState) => state?.loading?.models.search);
29
30
  const dispatch = useDispatch<Dispatch>();
31
+
30
32
  /**
31
33
  * Cancel all the requests which are in pending state.
32
34
  */
@@ -60,9 +62,9 @@ const Search: React.FC<RouteComponentProps> = ({ history }) => {
60
62
  if (searchRemote) {
61
63
  // TODO: check this part
62
64
  // @ts-ignore
63
- history.push(`/-/web/detail/${value.package.name}`);
65
+ history.push(`${Route.DETAIL}${value.package.name}`);
64
66
  } else {
65
- history.push(`/-/web/detail/${value.name}`);
67
+ history.push(`${Route.DETAIL}${value.name}`);
66
68
  }
67
69
  break;
68
70
  }
@@ -9,6 +9,8 @@ import Stack from '@mui/material/Stack';
9
9
  import React from 'react';
10
10
  import { useTranslation } from 'react-i18next';
11
11
 
12
+ import { cleanDescription } from './utils';
13
+
12
14
  type SearchItemProps = {
13
15
  name: string;
14
16
  version?: string;
@@ -28,15 +30,23 @@ export const Description = styled('div')<{ theme?: Theme }>(({ theme }) => ({
28
30
  display: 'none',
29
31
  color: theme?.palette?.greyLight2,
30
32
  lineHeight: '1.5rem',
33
+ whiteSpace: 'nowrap',
34
+ textOverflow: 'ellipsis',
35
+ alignItems: 'center',
36
+ overflow: 'hidden',
37
+ paddingLeft: theme.spacing(),
38
+ fontSize: theme?.fontSize.ssm,
39
+ [`@media (min-width: ${theme?.breakPoints.medium}px)`]: {
40
+ display: 'block',
41
+ width: '300px',
42
+ },
31
43
  [`@media (min-width: ${theme?.breakPoints.large}px)`]: {
32
44
  display: 'block',
33
- whiteSpace: 'nowrap',
34
- textOverflow: 'ellipsis',
35
- width: '200px',
36
- alignItems: 'center',
37
- overflow: 'hidden',
38
- paddingLeft: theme.spacing(),
39
- fontSize: theme?.fontSize.ssm,
45
+ width: '500px',
46
+ },
47
+ [`@media (min-width: 1440px)`]: {
48
+ display: 'block',
49
+ width: '600px',
40
50
  },
41
51
  }));
42
52
 
@@ -68,11 +78,12 @@ const SearchItem: React.FC<SearchItemProps> = ({
68
78
  // no action assigned by default
69
79
  };
70
80
  return (
81
+ // eslint-disable-next-line verdaccio/jsx-no-style
71
82
  <li {...props} style={{ flexDirection: 'column' }}>
72
83
  <Wrapper>
73
84
  <NameGroup>
74
85
  <Name>{name}</Name>
75
- {description && <Description>{description}</Description>}
86
+ {description && <Description>{cleanDescription(description)}</Description>}
76
87
  </NameGroup>
77
88
  {version && <Version>{version}</Version>}
78
89
  </Wrapper>
@@ -0,0 +1,17 @@
1
+ function removeHtmlTags(input: string): string {
2
+ let previous;
3
+ do {
4
+ previous = input;
5
+ input = input.replace(/<[^>]*>?/gm, '');
6
+ } while (input !== previous);
7
+ return input;
8
+ }
9
+
10
+ export function cleanDescription(description: string): string {
11
+ let output = description;
12
+ // remove html tags from description (e.g. <h1...>)
13
+ output = removeHtmlTags(output);
14
+ // remove markdown links from description (e.g. [link](url))
15
+ output = output.replace(/\(.*?\)/gm, '').replace(/(\[!?|\])/gm, '');
16
+ return output;
17
+ }
@@ -0,0 +1,41 @@
1
+ import React from 'react';
2
+
3
+ import { fireEvent, render, screen } from '../../test/test-react-testing-library';
4
+ import SettingsMenu from './SettingsMenu';
5
+
6
+ describe('<SettingsMenu />', () => {
7
+ test('should handle menu open and close', async () => {
8
+ render(<SettingsMenu packageName="foo" />);
9
+ const button = screen.getByRole('button');
10
+ fireEvent.click(button);
11
+ await screen.findByRole('menu');
12
+ // TODO onClose
13
+ });
14
+
15
+ test('should handle latest select', () => {
16
+ const { getByRole, getByText } = render(<SettingsMenu packageName="foo" />);
17
+ const button = getByRole('button');
18
+ fireEvent.click(button);
19
+ const menuItem = getByText('sidebar.installation.latest');
20
+ fireEvent.click(menuItem);
21
+ expect(getByText('sidebar.installation.latest')).toBeInTheDocument();
22
+ });
23
+
24
+ test('should handle global select', () => {
25
+ const { getByRole, getByText } = render(<SettingsMenu packageName="foo" />);
26
+ const button = getByRole('button');
27
+ fireEvent.click(button);
28
+ const menuItem = getByText('sidebar.installation.global');
29
+ fireEvent.click(menuItem);
30
+ expect(getByText('sidebar.installation.global')).toBeInTheDocument();
31
+ });
32
+
33
+ test('should handle yarn modern select', () => {
34
+ const { getByRole, getByText } = render(<SettingsMenu packageName="foo" />);
35
+ const button = getByRole('button');
36
+ fireEvent.click(button);
37
+ const menuItem = getByText('sidebar.installation.yarnModern');
38
+ fireEvent.click(menuItem);
39
+ expect(getByText('sidebar.installation.yarnModern')).toBeInTheDocument();
40
+ });
41
+ });
@@ -7,24 +7,42 @@ import MenuItem from '@mui/material/MenuItem';
7
7
  import React from 'react';
8
8
  import { useTranslation } from 'react-i18next';
9
9
 
10
+ import { useConfig } from '../../providers';
10
11
  import { useSettings } from '../../providers/PersistenceSettingProvider';
11
12
 
12
13
  interface Props {
13
14
  packageName: string;
14
15
  }
15
16
 
16
- const InstallListItem: React.FC<Props> = ({ packageName }) => {
17
+ const SettingsMenu: React.FC<Props> = ({ packageName }) => {
17
18
  const { t } = useTranslation();
18
19
  const { localSettings, updateSettings } = useSettings();
20
+ const { configOptions } = useConfig();
19
21
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
20
22
  const open = Boolean(anchorEl);
21
23
  const handleOpenMenu = (event: React.MouseEvent<HTMLButtonElement>) => {
22
24
  setAnchorEl(event.currentTarget);
23
25
  };
24
26
 
27
+ const handleLatestSelect = () => {
28
+ updateSettings({
29
+ ...localSettings,
30
+ [packageName]: {
31
+ global: localSettings[packageName]?.global,
32
+ latest: !localSettings[packageName]?.latest,
33
+ },
34
+ });
35
+ setAnchorEl(null);
36
+ };
37
+
25
38
  const handleGlobalSelect = () => {
26
- const statusGlobal = !localSettings[packageName]?.global;
27
- updateSettings({ ...localSettings, [packageName]: { global: statusGlobal } });
39
+ updateSettings({
40
+ ...localSettings,
41
+ [packageName]: {
42
+ global: !localSettings[packageName]?.global,
43
+ latest: localSettings[packageName]?.latest,
44
+ },
45
+ });
28
46
  setAnchorEl(null);
29
47
  };
30
48
 
@@ -38,6 +56,7 @@ const InstallListItem: React.FC<Props> = ({ packageName }) => {
38
56
  setAnchorEl(null);
39
57
  };
40
58
 
59
+ const statusLatest = localSettings[packageName]?.latest;
41
60
  const statusGlobal = localSettings[packageName]?.global;
42
61
  return (
43
62
  <>
@@ -56,31 +75,50 @@ const InstallListItem: React.FC<Props> = ({ packageName }) => {
56
75
  'aria-labelledby': 'basic-button',
57
76
  }}
58
77
  anchorEl={anchorEl}
78
+ anchorOrigin={{
79
+ vertical: 'bottom',
80
+ horizontal: 'right',
81
+ }}
59
82
  id="basic-menu"
60
83
  onClose={handleClose}
61
84
  open={open}
85
+ transformOrigin={{
86
+ vertical: 'top',
87
+ horizontal: 'right',
88
+ }}
62
89
  >
63
- <MenuItem onClick={handleGlobalSelect}>
90
+ <MenuItem onClick={handleLatestSelect}>
64
91
  {' '}
65
- {statusGlobal === true ? (
92
+ {statusLatest === true ? (
66
93
  <ListItemIcon>
67
94
  <Check />
68
95
  </ListItemIcon>
69
96
  ) : null}
70
- {t('sidebar.installation.global')}
97
+ {t('sidebar.installation.latest')}
71
98
  </MenuItem>
72
- <MenuItem onClick={handleGlobalYarnModern}>
99
+ <MenuItem onClick={handleGlobalSelect}>
73
100
  {' '}
74
- {localSettings?.yarnModern ? (
101
+ {statusGlobal === true ? (
75
102
  <ListItemIcon>
76
103
  <Check />
77
104
  </ListItemIcon>
78
105
  ) : null}
79
- {t('sidebar.installation.yarnModern')}
106
+ {t('sidebar.installation.global')}
80
107
  </MenuItem>
108
+ {configOptions?.pkgManagers?.includes('yarn') && (
109
+ <MenuItem onClick={handleGlobalYarnModern}>
110
+ {' '}
111
+ {localSettings?.yarnModern ? (
112
+ <ListItemIcon>
113
+ <Check />
114
+ </ListItemIcon>
115
+ ) : null}
116
+ {t('sidebar.installation.yarnModern')}
117
+ </MenuItem>
118
+ )}
81
119
  </Menu>
82
120
  </>
83
121
  );
84
122
  };
85
123
 
86
- export default InstallListItem;
124
+ export default SettingsMenu;
@@ -20,11 +20,23 @@ interface Props {
20
20
  time: string;
21
21
  }
22
22
 
23
+ const Icon = styled.div<{ theme?: Theme }>(({ theme }) => ({
24
+ marginLeft: theme?.spacing(1),
25
+ }));
26
+
23
27
  const ModuleJS: React.FC<{ module: ModuleType | void }> = ({ module }) => {
24
28
  if (module === 'commonjs') {
25
- return <CommonJS />;
29
+ return (
30
+ <Icon>
31
+ <CommonJS />
32
+ </Icon>
33
+ );
26
34
  } else if (module === 'module') {
27
- return <ES6Modules />;
35
+ return (
36
+ <Icon>
37
+ <ES6Modules />
38
+ </Icon>
39
+ );
28
40
  } else {
29
41
  return null;
30
42
  }
@@ -46,7 +58,11 @@ const DetailSidebarTitle: React.FC<Props> = ({
46
58
  <TitleWrapper>
47
59
  <>
48
60
  {packageName}
49
- {hasTypes && <TypeScript />}
61
+ {hasTypes && (
62
+ <Icon>
63
+ <TypeScript />
64
+ </Icon>
65
+ )}
50
66
  <ModuleJS module={moduleType} />
51
67
  </>
52
68
  </TitleWrapper>
@@ -44,4 +44,11 @@ describe('<UpLinks /> component', () => {
44
44
 
45
45
  expect(wrapper).toMatchSnapshot();
46
46
  });
47
+
48
+ test('should not render if input is missing', () => {
49
+ const wrapper = render(<UpLinks packageMeta={undefined} />);
50
+ // expect nothing to be rendered
51
+ expect(wrapper.queryByTestId('no-uplinks-npm')).toBeNull();
52
+ expect(wrapper.queryByTestId('uplinks')).toBeNull();
53
+ });
47
54
  });