From ba53d1edc8147c90a3b882ebd83301562dc105fc Mon Sep 17 00:00:00 2001 From: Juan Picado Date: Sat, 23 Mar 2024 20:41:19 +0100 Subject: [PATCH] feat: versions filter by semver range (#4555) * fix: improve dark mode styles * fix background color * feat: ui improvements * add tests --- .changeset/eighty-lobsters-study.md | 6 ++ .../plugins/ui-theme/src/i18n/crowdin/ui.json | 1 + .../.storybook/preview-head.html | 2 +- packages/ui-components/jest/jest.config.js | 2 +- packages/ui-components/package.json | 1 + packages/ui-components/src/Theme/theme.ts | 2 +- .../src/components/Engines/Engines.tsx | 2 +- .../components/Install/InstallListItem.tsx | 1 + .../src/components/Repository/Repository.tsx | 4 +- .../src/components/Versions/HistoryList.tsx | 4 +- .../src/components/Versions/Versions.test.tsx | 34 +++++++--- .../src/components/Versions/Versions.tsx | 68 +++++++++++++------ .../src/sections/Detail/Tabs.tsx | 8 +-- .../Detail/__snapshots__/Detail.test.tsx.snap | 1 - pnpm-lock.yaml | 33 +-------- 15 files changed, 93 insertions(+), 76 deletions(-) create mode 100644 .changeset/eighty-lobsters-study.md diff --git a/.changeset/eighty-lobsters-study.md b/.changeset/eighty-lobsters-study.md new file mode 100644 index 000000000..d75e11495 --- /dev/null +++ b/.changeset/eighty-lobsters-study.md @@ -0,0 +1,6 @@ +--- +'@verdaccio/ui-theme': patch +'@verdaccio/ui-components': patch +--- + +feat: versions filter by semver range diff --git a/packages/plugins/ui-theme/src/i18n/crowdin/ui.json b/packages/plugins/ui-theme/src/i18n/crowdin/ui.json index 941c02ce2..6f5c59d80 100644 --- a/packages/plugins/ui-theme/src/i18n/crowdin/ui.json +++ b/packages/plugins/ui-theme/src/i18n/crowdin/ui.json @@ -55,6 +55,7 @@ "version-history": "Version History", "not-available": "Not available", "deprecated": "Deprecated", + "search.placeholder": "Search for version by semver range, e.g. ^1.0.0", "hide-deprecated": "All deprecated versions are hidden by global configuration" }, "package": { diff --git a/packages/ui-components/.storybook/preview-head.html b/packages/ui-components/.storybook/preview-head.html index b6f9d513f..a6c040fa3 100644 --- a/packages/ui-components/.storybook/preview-head.html +++ b/packages/ui-components/.storybook/preview-head.html @@ -7,4 +7,4 @@ "filename": "index.html", "base": "http://localhost:9000/" } - + \ No newline at end of file diff --git a/packages/ui-components/jest/jest.config.js b/packages/ui-components/jest/jest.config.js index 08aa8cf07..4524fb3ec 100644 --- a/packages/ui-components/jest/jest.config.js +++ b/packages/ui-components/jest/jest.config.js @@ -35,7 +35,7 @@ module.exports = Object.assign({}, config, { coverageThreshold: { global: { branches: 70, - functions: 76, + functions: 75, lines: 80, statements: 81, }, diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 5c17c7704..6e36e05b8 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -53,6 +53,7 @@ "react-redux": "8.1.3", "react-router": "5.3.4", "react-router-dom": "5.3.4", + "semver": "7.6.0", "react-virtualized": "9.22.5", "redux": "4.2.1", "validator": "13.11.0" diff --git a/packages/ui-components/src/Theme/theme.ts b/packages/ui-components/src/Theme/theme.ts index ced407c27..eb534c803 100644 --- a/packages/ui-components/src/Theme/theme.ts +++ b/packages/ui-components/src/Theme/theme.ts @@ -36,7 +36,7 @@ const themeModes = { }, dark: { ...colors, - primary: '#24394e', + primary: '#ffffff', secondary: '#424242', background: '#1A202C', }, diff --git a/packages/ui-components/src/components/Engines/Engines.tsx b/packages/ui-components/src/components/Engines/Engines.tsx index dcf46d0d0..34803cdeb 100644 --- a/packages/ui-components/src/components/Engines/Engines.tsx +++ b/packages/ui-components/src/components/Engines/Engines.tsx @@ -23,7 +23,7 @@ const EngineItem: FC = ({ title, element, engineText }) => ( {title}}> - {element} + {element} {engineText} diff --git a/packages/ui-components/src/components/Install/InstallListItem.tsx b/packages/ui-components/src/components/Install/InstallListItem.tsx index 083547187..11427d21b 100644 --- a/packages/ui-components/src/components/Install/InstallListItem.tsx +++ b/packages/ui-components/src/components/Install/InstallListItem.tsx @@ -23,6 +23,7 @@ const InstallListItemText = styled(ListItemText)({ const PackageMangerAvatar = styled(Avatar)({ borderRadius: '0px', + backgroundColor: 'transparent', padding: 0, }); diff --git a/packages/ui-components/src/components/Repository/Repository.tsx b/packages/ui-components/src/components/Repository/Repository.tsx index 098ddb619..fd96d4698 100644 --- a/packages/ui-components/src/components/Repository/Repository.tsx +++ b/packages/ui-components/src/components/Repository/Repository.tsx @@ -40,9 +40,7 @@ const RepositoryListItemText = styled(ListItemText)({ const RepositoryAvatar = styled(Avatar)({ borderRadius: '0px', padding: '0', - img: { - backgroundColor: 'transparent', - }, + backgroundColor: 'transparent', }); const Repository: React.FC<{ packageMeta: any }> = ({ packageMeta }) => { diff --git a/packages/ui-components/src/components/Versions/HistoryList.tsx b/packages/ui-components/src/components/Versions/HistoryList.tsx index 25f3005f5..4792c736e 100644 --- a/packages/ui-components/src/components/Versions/HistoryList.tsx +++ b/packages/ui-components/src/components/Versions/HistoryList.tsx @@ -44,7 +44,7 @@ const VersionsHistoryList: React.FC = ({ versions, packageName, time }) = - {typeof versions[version].deprecated === 'string' ? ( + {typeof versions[version]?.deprecated === 'string' ? ( = ({ versions, packageName, time }) = /> ) : null} - + {time[version] ? utils.formatDateDistance(time[version]) : t('versions.not-available')} diff --git a/packages/ui-components/src/components/Versions/Versions.test.tsx b/packages/ui-components/src/components/Versions/Versions.test.tsx index e85dff922..fc16cf40b 100644 --- a/packages/ui-components/src/components/Versions/Versions.test.tsx +++ b/packages/ui-components/src/components/Versions/Versions.test.tsx @@ -2,24 +2,31 @@ import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { cleanup, render, screen } from '../../test/test-react-testing-library'; +import { fireEvent, render, screen } from '../../test/test-react-testing-library'; import Versions, { Props } from './Versions'; import data from './__partials__/data.json'; import dataDeprecated from './__partials__/deprecated-versions.json'; -const ComponentToBeRendered: React.FC = (props) => ( +const VersionsComponent: React.FC = (props) => ( ); +jest.mock('lodash/debounce', () => + jest.fn((fn) => { + fn.cancel = jest.fn(); + return fn; + }) +); + describe(' component', () => { afterEach(() => { - cleanup(); + jest.clearAllMocks(); }); test('should render versions', () => { - const { getByText } = render(); + const { getByText } = render(); expect(getByText('versions.version-history')).toBeTruthy(); expect(getByText('versions.current-tags')).toBeTruthy(); @@ -31,17 +38,26 @@ describe(' component', () => { expect(screen.queryByTestId('deprecated-badge')).toBeInTheDocument(); }); - test('should not render versions', () => { - const { queryByText } = render(); + test('should filter by version', () => { + render(); + expect(screen.getByText('versions.version-history')).toBeTruthy(); + expect(screen.getByText('versions.current-tags')).toBeTruthy(); + expect(screen.queryAllByTestId('version-list-text')).toHaveLength(65); + fireEvent.change(screen.getByRole('textbox'), { target: { value: '2.3.0' } }); + expect(screen.queryAllByTestId('version-list-text')).toHaveLength(1); + }); - expect(queryByText('versions.version-history')).toBeFalsy(); - expect(queryByText('versions.current-tags')).toBeFalsy(); + test('should not render versions', () => { + render(); + + expect(screen.queryByText('versions.version-history')).toBeFalsy(); + expect(screen.queryByText('versions.current-tags')).toBeFalsy(); }); test('should render versions deprecated settings', () => { window.__VERDACCIO_BASENAME_UI_OPTIONS.hideDeprecatedVersions = true; const { getByText } = render( - + ); expect(getByText('versions.hide-deprecated')).toBeTruthy(); diff --git a/packages/ui-components/src/components/Versions/Versions.tsx b/packages/ui-components/src/components/Versions/Versions.tsx index eae4dfa24..c02140e8c 100644 --- a/packages/ui-components/src/components/Versions/Versions.tsx +++ b/packages/ui-components/src/components/Versions/Versions.tsx @@ -1,8 +1,11 @@ import Alert from '@mui/material/Alert'; +import TextField from '@mui/material/TextField'; import Typography from '@mui/material/Typography'; import { useTheme } from '@mui/styles'; -import React from 'react'; +import debounce from 'lodash/debounce'; +import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; +import semver from 'semver'; import { useConfig } from '../../providers'; import VersionsHistoryList from './HistoryList'; @@ -14,16 +17,33 @@ const Versions: React.FC = ({ packageMeta, packageName }) => { const { t } = useTranslation(); const { configOptions } = useConfig(); const theme = useTheme(); - - if (!packageMeta) { - return null; - } - const { versions = {}, time = {}, ['dist-tags']: distTags = {} } = packageMeta; - const hasDistTags = distTags && Object.keys(distTags).length > 0 && packageName; - const hasVersionHistory = versions && Object.keys(versions).length > 0 && packageName; + const [packageVersions, setPackageVersions] = useState(versions); + if (!packageMeta || Object.keys(packageMeta).length === 0) { + return null; + } const hideDeprecatedVersions = configOptions.hideDeprecatedVersions; + const hasDistTags = distTags && Object.keys(distTags).length > 0 && packageName; + const hasVersionHistory = + packageVersions && Object.keys(packageVersions).length > 0 && packageName; + + const filterVersions = (textSearch) => { + const filteredVersions = Object.keys(versions).reduce((acc, version) => { + if (textSearch !== '') { + if (typeof versions[version] !== 'undefined') { + if (semver.satisfies(version, textSearch, { includePrerelease: true, loose: true })) { + acc[version] = versions[version]; + } + } + } else { + acc[version] = versions[version]; + } + return acc; + }, {}); + + setPackageVersions(filteredVersions); + }; return ( <> @@ -33,20 +53,28 @@ const Versions: React.FC = ({ packageMeta, packageName }) => { ) : null} + <> + {t('versions.version-history')} + { + filterVersions(e.target.value); + }, 200)} + size="small" + variant="standard" + /> + {hasVersionHistory ? ( <> - {t('versions.version-history')} - <> - {hideDeprecatedVersions && ( - - {t('versions.hide-deprecated')} - - )} - - + {hideDeprecatedVersions === true && ( + + {t('versions.hide-deprecated')} + + )} + ) : null} diff --git a/packages/ui-components/src/sections/Detail/Tabs.tsx b/packages/ui-components/src/sections/Detail/Tabs.tsx index a5bb1f686..ffe3672f5 100644 --- a/packages/ui-components/src/sections/Detail/Tabs.tsx +++ b/packages/ui-components/src/sections/Detail/Tabs.tsx @@ -14,13 +14,7 @@ interface Props { const DetailContainerTabs: React.FC = ({ tabPosition, onChange }) => { const { t } = useTranslation(); return ( - + diff --git a/packages/ui-components/src/sections/Detail/__snapshots__/Detail.test.tsx.snap b/packages/ui-components/src/sections/Detail/__snapshots__/Detail.test.tsx.snap index db35dd482..1a70b7291 100644 --- a/packages/ui-components/src/sections/Detail/__snapshots__/Detail.test.tsx.snap +++ b/packages/ui-components/src/sections/Detail/__snapshots__/Detail.test.tsx.snap @@ -160,7 +160,6 @@ exports[`DetailContainer renders correctly 1`] = ` >
=12.17'} - hasBin: true - dev: false - - /pnpm@7.32.0: - resolution: {integrity: sha512-XkLEMinrF4046cWGvvam7dsCKeRdJ9i2SeDiKNodoZEPmJp1KrzQe1qYC5Vs/v9qBXJqyI0vLzjoMHjXgphP6g==} - engines: {node: '>=14.6'} - hasBin: true - dev: false - /pnpm@8.15.5: resolution: {integrity: sha512-sFGjLH5pWDO4SSbTspuMylclS1ifBknYmcbp0O22cLkex+KkNFm65zdZu1zmGcMmbxFr+THOItHvF1mn5Fqpbw==} engines: {node: '>=16.14'}