diff --git a/.changeset/khaki-carrots-crash.md b/.changeset/khaki-carrots-crash.md new file mode 100644 index 000000000..e73ed4db0 --- /dev/null +++ b/.changeset/khaki-carrots-crash.md @@ -0,0 +1,6 @@ +--- +'@verdaccio/ui-theme': minor +'@verdaccio/ui-components': minor +--- + +feat: ui bugfixes and improvements diff --git a/e2e/ui/package.json b/e2e/ui/package.json index 46689690b..28afcedae 100644 --- a/e2e/ui/package.json +++ b/e2e/ui/package.json @@ -8,7 +8,7 @@ "@verdaccio/config": "workspace:6.0.0-6-next.70", "@verdaccio/test-helper": "workspace:2.0.0-6-next.8", "debug": "4.3.4", - "cypress": "11.2.0", + "cypress": "^11.2.0", "get-port": "5.1.1" }, "scripts": { diff --git a/e2e/ui/pre-setup.js b/e2e/ui/pre-setup.js deleted file mode 100644 index fc9e89437..000000000 --- a/e2e/ui/pre-setup.js +++ /dev/null @@ -1,4 +0,0 @@ -require('@babel/register')({ - extensions: ['.ts', '.js'], -}); -module.exports = require('./setup'); diff --git a/e2e/ui/setup.js b/e2e/ui/setup.js deleted file mode 100644 index db2a36be9..000000000 --- a/e2e/ui/setup.js +++ /dev/null @@ -1,26 +0,0 @@ -const fs = require('fs'); -const os = require('os'); -const path = require('path'); - -const { green } = require('colorette'); -const puppeteer = require('puppeteer'); - -const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup'); - -module.exports = async function () { - // eslint-disable-next-line no-console - console.log(green('Setup Puppeteer')); - const browser = await puppeteer.launch({ - isMobile: false, - ignoreHTTPSErrors: true, - // invert values for local testing - devtools: false, - headless: true, - // slowMo: 6000, - // invert values for local testing - args: ['--no-sandbox'], - }); - global.__BROWSER__ = browser; - fs.mkdirSync(DIR, { recursive: true, force: true }); - fs.writeFileSync(path.join(DIR, 'wsEndpoint'), browser.wsEndpoint()); -}; diff --git a/e2e/ui/teardown.js b/e2e/ui/teardown.js deleted file mode 100644 index aeea17034..000000000 --- a/e2e/ui/teardown.js +++ /dev/null @@ -1,14 +0,0 @@ -const os = require('os'); -const path = require('path'); - -const { green } = require('kleur'); -const rimraf = require('rimraf'); - -const DIR = path.join(os.tmpdir(), 'jest_puppeteer_global_setup'); - -module.exports = async function () { - // eslint-disable-next-line no-console - console.log(green('Teardown Puppeteer')); - await global.__BROWSER__.close(); - rimraf.sync(DIR); -}; diff --git a/packages/plugins/ui-theme/src/i18n/crowdin/ui.json b/packages/plugins/ui-theme/src/i18n/crowdin/ui.json index 49b175d1d..c83132052 100644 --- a/packages/plugins/ui-theme/src/i18n/crowdin/ui.json +++ b/packages/plugins/ui-theme/src/i18n/crowdin/ui.json @@ -106,7 +106,8 @@ "install-using-npm": "Install using npm", "install-using-npm-command": "npm install {{packageName}}", "install-using-pnpm": "Install using pnpm", - "install-using-pnpm-command": "pnpm install {{packageName}}" + "install-using-pnpm-command": "pnpm install {{packageName}}", + "global": "View as global" }, "repository": { "title": "Repository" diff --git a/packages/plugins/ui-theme/src/index.tsx b/packages/plugins/ui-theme/src/index.tsx index 34427755f..558d2b5ab 100644 --- a/packages/plugins/ui-theme/src/index.tsx +++ b/packages/plugins/ui-theme/src/index.tsx @@ -5,6 +5,7 @@ import { Provider } from 'react-redux'; import { AppConfigurationProvider, + PersistenceSettingProvider, StyleBaseline, ThemeProvider, store, @@ -20,7 +21,9 @@ const AppContainer = () => ( <AppConfigurationProvider> <ThemeProvider> <StyleBaseline /> - <App /> + <PersistenceSettingProvider> + <App /> + </PersistenceSettingProvider> </ThemeProvider> </AppConfigurationProvider> </Provider> diff --git a/packages/ui-components/.babelrc b/packages/ui-components/.babelrc index f0cfa3d73..7488c40e0 100644 --- a/packages/ui-components/.babelrc +++ b/packages/ui-components/.babelrc @@ -1,16 +1,8 @@ { "extends": "../../.babelrc", "plugins": ["@emotion"], + "sourceMaps" : "inline", "presets": [ - [ - "@babel/preset-env", - { - "targets": ["last 5 versions"], - "bugfixes": true, - "modules": "auto", - "debug": false - } - ], "@babel/preset-react" ] } diff --git a/packages/ui-components/.storybook/preview.js b/packages/ui-components/.storybook/preview.js index 1e3da07cd..7e6bbc12c 100644 --- a/packages/ui-components/.storybook/preview.js +++ b/packages/ui-components/.storybook/preview.js @@ -4,6 +4,7 @@ import { Provider } from 'react-redux'; import config from '../../plugins/ui-theme/src/i18n/config'; import { AppConfigurationProvider, + PersistenceSettingProvider, StyleBaseline, ThemeProvider, TranslatorProvider, @@ -34,12 +35,14 @@ export const parameters = { export const withMuiTheme = (Story) => ( <Provider store={store}> <TranslatorProvider onMount={() => {}} i18n={config} listLanguages={listLanguages}> - <AppConfigurationProvider> - <ThemeProvider> - <StyleBaseline /> - <Story /> - </ThemeProvider> - </AppConfigurationProvider> + <PersistenceSettingProvider> + <AppConfigurationProvider> + <ThemeProvider> + <StyleBaseline /> + <Story /> + </ThemeProvider> + </AppConfigurationProvider> + </PersistenceSettingProvider> </TranslatorProvider> </Provider> ); diff --git a/packages/ui-components/package.json b/packages/ui-components/package.json index 5c98761c3..7cc8052dd 100644 --- a/packages/ui-components/package.json +++ b/packages/ui-components/package.json @@ -21,6 +21,8 @@ "dependencies": { "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", + "@fontsource/material-icons": "^4.5.4", + "@fontsource/roboto": "^4.5.8", "@mui/icons-material": "5.11.16", "@mui/material": "5.12.0", "@mui/styles": "5.12.0", @@ -28,8 +30,6 @@ "@rematch/core": "2.2.0", "@rematch/loading": "2.1.2", "@rematch/persist": "2.1.2", - "@fontsource/material-icons": "^4.5.4", - "@fontsource/roboto": "^4.5.8", "country-flag-icons": "1.5.5", "dayjs": "1.11.7", "dompurify": "2.4.5", @@ -56,7 +56,6 @@ "validator": "13.9.0" }, "devDependencies": { - "@verdaccio/types": "workspace:11.0.0-6-next.25", "@babel/core": "^7.20.7", "@emotion/babel-plugin": "11.10.6", "@emotion/jest": "11.10.5", @@ -74,6 +73,7 @@ "@types/hast": "^2.0.0", "@types/react-router": "^5.1.20", "@types/unist": "^2.0.0", + "@verdaccio/types": "workspace:11.0.0-6-next.25", "babel-loader": "^8.3.0", "mockdate": "3.0.5", "msw": "0.49.2" diff --git a/packages/ui-components/src/components/ActionBar/ActionBar.stories.tsx b/packages/ui-components/src/components/ActionBar/ActionBar.stories.tsx index 6890e5436..1dec898d6 100644 --- a/packages/ui-components/src/components/ActionBar/ActionBar.stories.tsx +++ b/packages/ui-components/src/components/ActionBar/ActionBar.stories.tsx @@ -1,50 +1,64 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import { clone, merge } from 'lodash'; import React from 'react'; import { default as ActionBar } from '.'; -export default { - title: 'ActionBar ', +const meta: Meta<typeof ActionBar> = { + title: 'Components/Sidebar/ActionBar', + component: ActionBar, }; -export const ActionBarAll: any = () => ( - <ActionBar - packageMeta={{ - _uplinks: {}, - latest: { - name: 'verdaccio-ui/local-storage', - version: '8.0.1-next.1', - dist: { - fileCount: 0, - unpackedSize: 0, - tarball: 'http://localhost:8080/bootstrap/-/bootstrap-4.3.1.tgz', - }, - homepage: 'https://verdaccio.org', - bugs: { - url: 'https://github.com/verdaccio/monorepo/issues', - }, - }, - }} - /> -); +export default meta; +type Story = StoryObj<typeof ActionBar>; -export const RawViewer: any = () => ( - <ActionBar - packageMeta={{ - _uplinks: {}, - latest: { - name: 'verdaccio-ui/local-storage', - version: '8.0.1-next.1', - dist: { - fileCount: 0, - unpackedSize: 0, - tarball: 'http://localhost:8080/bootstrap/-/bootstrap-4.3.1.tgz', - }, - homepage: 'https://verdaccio.org', - bugs: { - url: 'https://github.com/verdaccio/monorepo/issues', - }, - }, - }} - showRaw={true} - /> -); +const packageMeta = { + _uplinks: {}, + latest: { + name: 'verdaccio-ui/local-storage', + version: '8.0.1-next.1', + dist: { + fileCount: 0, + unpackedSize: 0, + tarball: 'https://registry.verdaccio.org/storybook/-/storybook-0.2.0.tgz', + }, + homepage: 'https://verdaccio.org', + bugs: { + url: 'https://github.com/verdaccio/monorepo/issues', + }, + }, +}; + +export const Primary: Story = { + name: 'Default', + render: () => <ActionBar packageMeta={packageMeta} />, +}; + +export const Raw: Story = { + name: 'Raw viewer', + render: () => <ActionBar packageMeta={packageMeta} showRaw={true} />, +}; + +export const NoLatest: Story = { + name: 'No latest (empty)', + render: () => <ActionBar packageMeta={{}} showRaw={true} />, +}; + +export const Download: Story = { + name: 'No show download', + render: () => <ActionBar packageMeta={{ ...clone(packageMeta) }} showDownloadTarball={false} />, +}; + +export const Home: Story = { + name: 'No home', + render: () => ( + <ActionBar packageMeta={{ ...merge(clone(packageMeta), { latest: { homepage: null } }) }} /> + ), +}; + +export const Bugs: Story = { + name: 'No bugs', + render: () => ( + <ActionBar packageMeta={{ ...merge(clone(packageMeta), { latest: { bugs: null } }) }} /> + ), +}; diff --git a/packages/ui-components/src/components/Author/Author.stories.tsx b/packages/ui-components/src/components/Author/Author.stories.tsx index 74707cd67..250df61ca 100644 --- a/packages/ui-components/src/components/Author/Author.stories.tsx +++ b/packages/ui-components/src/components/Author/Author.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { default as Author } from '.'; export default { - title: 'Author ', + title: 'Components/Sidebar/Author', }; export const AuthorAll: any = () => ( diff --git a/packages/ui-components/src/components/CopyClipboard/CopyToClipBoard.stories.tsx b/packages/ui-components/src/components/CopyClipboard/CopyToClipBoard.stories.tsx index eeb848765..bc719d819 100644 --- a/packages/ui-components/src/components/CopyClipboard/CopyToClipBoard.stories.tsx +++ b/packages/ui-components/src/components/CopyClipboard/CopyToClipBoard.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import CopyToClipBoard from './CopyToClipBoard'; export default { - title: 'CopyToClipBoard', + title: 'Components/Sidebar/CopyToClipBoard', component: CopyToClipBoard, argTypes: { backgroundColor: { control: 'color' }, diff --git a/packages/ui-components/src/components/CopyClipboard/CopyToClipBoard.tsx b/packages/ui-components/src/components/CopyClipboard/CopyToClipBoard.tsx index 679410dc5..2c53548d3 100644 --- a/packages/ui-components/src/components/CopyClipboard/CopyToClipBoard.tsx +++ b/packages/ui-components/src/components/CopyClipboard/CopyToClipBoard.tsx @@ -34,12 +34,12 @@ function CopyToClipBoard({ text, children, dataTestId, title, ...props }: Props) <Content>{children ?? text}</Content> {title ? ( <Tooltip disableFocusListener={true} title={title}> - <IconButton data-testid={dataTestId} onClick={copyToClipBoardUtility(text)} size="large"> + <IconButton data-testid={dataTestId} onClick={copyToClipBoardUtility(text)} size="small"> <FileCopy fontSize="small" /> </IconButton> </Tooltip> ) : ( - <IconButton data-testid={dataTestId} onClick={copyToClipBoardUtility(text)} size="large"> + <IconButton data-testid={dataTestId} onClick={copyToClipBoardUtility(text)} size="small"> <FileCopy fontSize="small" /> </IconButton> )} diff --git a/packages/ui-components/src/components/Dependencies/Dependencies.stories.tsx b/packages/ui-components/src/components/Dependencies/Dependencies.stories.tsx index e110fe8de..3d0f7dd4e 100644 --- a/packages/ui-components/src/components/Dependencies/Dependencies.stories.tsx +++ b/packages/ui-components/src/components/Dependencies/Dependencies.stories.tsx @@ -1,58 +1,158 @@ +import type { Meta, StoryObj } from '@storybook/react'; import React from 'react'; import { default as Dependencies } from '.'; -export default { - title: 'Dependencies ', +const meta: Meta<typeof Dependencies> = { + title: 'Components/Detail/Dependencies', + component: Dependencies, }; -export const DeprecatedAll: any = () => ( - <Dependencies - packageMeta={{ - latest: { - name: 'verdaccio', - version: '4.0.0', - author: { - name: 'verdaccio user', - email: 'verdaccio.user@verdaccio.org', - url: '', - avatar: 'https://www.gravatar.com/avatar/000000', - }, - dist: { fileCount: 0, unpackedSize: 0 }, - dependencies: { - react: '16.9.0', - 'react-dom': '16.9.0', - }, - devDependencies: { - 'babel-core': '7.0.0-beta6', - }, - peerDependencies: { - 'styled-components': '5.0.0', - }, - }, - _uplinks: {}, - }} - /> -); +export default meta; +type Story = StoryObj<typeof Dependencies>; -export const NoDependencies: any = () => ( - <Dependencies - packageMeta={{ - latest: { - name: 'verdaccio', - version: '4.0.0', - author: { - name: 'verdaccio user', - email: 'verdaccio.user@verdaccio.org', - url: '', - avatar: 'https://www.gravatar.com/avatar/000000', - }, - dist: { fileCount: 0, unpackedSize: 0 }, - dependencies: {}, - devDependencies: {}, - peerDependencies: {}, - }, - _uplinks: {}, - }} - /> -); +const packageMeta = { + latest: { + name: 'verdaccio', + version: '4.0.0', + author: { + name: 'verdaccio user', + email: 'verdaccio.user@verdaccio.org', + url: '', + avatar: 'https://www.gravatar.com/avatar/000000', + }, + dist: { fileCount: 0, unpackedSize: 0 }, + dependencies: { + react: '16.9.0', + 'react-dom': '16.9.0', + '@storybook/codemod': '^3.2.6', + chalk: '^2.1.0', + 'child-process-promise': '^2.2.1', + commander: '^2.11.0', + 'cross-spawn': '^5.0.1', + jscodeshift: '^0.3.30', + json5: '^0.5.1', + 'latest-version': '^3.1.0', + 'merge-dirs': '^0.2.1', + opencollective: '^1.0.3', + shelljs: '^0.7.8', + 'update-notifier': '^2.1.0', + }, + devDependencies: { + 'babel-core': '7.0.0-beta6', + 'cross-spawn': '^5.0.1', + jscodeshift: '^0.3.30', + json5: '^0.5.1', + 'latest-version': '^3.1.0', + 'merge-dirs': '^0.2.1', + opencollective: '^1.0.3', + shelljs: '^0.7.8', + 'update-notifier': '^2.1.0', + }, + peerDependencies: { + 'styled-components': '5.0.0', + 'cross-spawn': '^5.0.1', + jscodeshift: '^0.3.30', + json5: '^0.5.1', + 'latest-version': '^3.1.0', + 'merge-dirs': '^0.2.1', + opencollective: '^1.0.3', + shelljs: '^0.7.8', + 'update-notifier': '^2.1.0', + }, + optionalDependencies: { + 'styled-components': '5.0.0', + 'cross-spawn': '^5.0.1', + jscodeshift: '^0.3.30', + json5: '^0.5.1', + 'latest-version': '^3.1.0', + 'merge-dirs': '^0.2.1', + opencollective: '^1.0.3', + shelljs: '^0.7.8', + 'update-notifier': '^2.1.0', + }, + bundleDependencies: { + 'styled-components': '5.0.0', + 'cross-spawn': '^5.0.1', + jscodeshift: '^0.3.30', + json5: '^0.5.1', + 'latest-version': '^3.1.0', + 'merge-dirs': '^0.2.1', + opencollective: '^1.0.3', + shelljs: '^0.7.8', + 'update-notifier': '^2.1.0', + }, + }, + _uplinks: {}, +}; + +export const Primary: Story = { + name: 'Default', + render: () => { + return <Dependencies packageMeta={packageMeta} />; + }, +}; + +export const OnlyDeps: Story = { + name: 'Only dependencies', + render: () => { + return ( + <Dependencies + packageMeta={{ + latest: { + name: 'verdaccio', + version: '4.0.0', + author: { + name: 'verdaccio user', + email: 'verdaccio.user@verdaccio.org', + url: '', + avatar: 'https://www.gravatar.com/avatar/000000', + }, + dist: { fileCount: 0, unpackedSize: 0 }, + dependencies: { + 'styled-components': '5.0.0', + 'cross-spawn': '^5.0.1', + jscodeshift: '^0.3.30', + json5: '^0.5.1', + 'latest-version': '^3.1.0', + 'merge-dirs': '^0.2.1', + opencollective: '^1.0.3', + shelljs: '^0.7.8', + 'update-notifier': '^2.1.0', + }, + devDependencies: { jscodeshift: '^0.3.30', json5: '^0.5.1' }, + peerDependencies: {}, + }, + _uplinks: {}, + }} + /> + ); + }, +}; + +export const NoDependencies: Story = { + name: 'No dependencies', + render: () => { + return ( + <Dependencies + packageMeta={{ + latest: { + name: 'verdaccio', + version: '4.0.0', + author: { + name: 'verdaccio user', + email: 'verdaccio.user@verdaccio.org', + url: '', + avatar: 'https://www.gravatar.com/avatar/000000', + }, + dist: { fileCount: 0, unpackedSize: 0 }, + dependencies: {}, + devDependencies: {}, + peerDependencies: {}, + }, + _uplinks: {}, + }} + /> + ); + }, +}; diff --git a/packages/ui-components/src/components/Dependencies/Dependencies.tsx b/packages/ui-components/src/components/Dependencies/Dependencies.tsx index 16ab57e3d..220e594bc 100644 --- a/packages/ui-components/src/components/Dependencies/Dependencies.tsx +++ b/packages/ui-components/src/components/Dependencies/Dependencies.tsx @@ -1,4 +1,6 @@ +import Box from '@mui/material/Box'; import CardContent from '@mui/material/CardContent'; +import { useTheme } from '@mui/styles'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { useHistory } from 'react-router-dom'; @@ -10,44 +12,38 @@ import { CardWrap, StyledText, Tag, Tags } from './styles'; interface DependencyBlockProps { title: string; dependencies: PackageDependencies; - enableLoading?: () => void; } -const DependencyBlock: React.FC<DependencyBlockProps> = ({ - title, - dependencies, - enableLoading, -}) => { +const DependencyBlock: React.FC<DependencyBlockProps> = ({ title, dependencies }) => { const history = useHistory(); const { t } = useTranslation(); + const theme = useTheme(); const deps = Object.entries(dependencies); function handleClick(name: string): void { - enableLoading?.(); - history.push(`/-/web/detail/${name}`); } return ( - <CardWrap data-testid={title}> - <CardContent> - <StyledText variant="subtitle1">{`${title} (${deps.length})`}</StyledText> - <Tags> - {deps.map(([name, version]) => ( - <Tag - className={'dep-tag'} - clickable={true} - data-testid={name} - key={name} - label={t('dependencies.dependency-block', { package: name, version })} - // eslint-disable-next-line - onClick={() => handleClick(name)} - /> - ))} - </Tags> - </CardContent> - </CardWrap> + <Box data-testid={title} sx={{ margin: theme.spacing(2) }}> + <StyledText sx={{ marginBottom: theme.spacing(1) }} variant="subtitle1"> + {`${title} (${deps.length})`} + </StyledText> + <Tags> + {deps.map(([name, version]) => ( + <Tag + className={'dep-tag'} + clickable={true} + data-testid={name} + key={name} + label={t('dependencies.dependency-block', { package: name, version })} + // eslint-disable-next-line + onClick={() => handleClick(name)} + /> + ))} + </Tags> + </Box> ); }; @@ -65,37 +61,51 @@ const Dependencies: React.FC<{ packageMeta: any }> = ({ packageMeta }) => { const { latest } = packageMeta; // FIXME: add dependencies to package meta type // @ts-ignore - const { dependencies, devDependencies, peerDependencies, name } = latest; - const dependencyMap = { dependencies, devDependencies, peerDependencies }; + const { + dependencies, + devDependencies, + peerDependencies, + optionalDependencies, + bundleDependencies, + name, + } = latest; + const dependencyMap = { + dependencies, + devDependencies, + peerDependencies, + bundleDependencies, + optionalDependencies, + }; const hasDependencies = - hasKeys(dependencies) || hasKeys(devDependencies) || hasKeys(peerDependencies); - + hasKeys(dependencies) || + hasKeys(bundleDependencies) || + hasKeys(optionalDependencies) || + hasKeys(devDependencies) || + hasKeys(peerDependencies); if (hasDependencies) { return ( - <> - {Object.entries(dependencyMap).map(([dependencyType, dependencies]) => { - if (!dependencies || Object.keys(dependencies).length === 0) { - return null; - } - - return ( - <DependencyBlock - dependencies={dependencies} - key={dependencyType} - title={dependencyType} - /> - ); - })} - </> + <CardWrap> + <CardContent> + {Object.entries(dependencyMap).map(([dependencyType, dependencies]) => { + if (!dependencies || Object.keys(dependencies).length === 0) { + return null; + } + return ( + <> + <DependencyBlock + dependencies={dependencies} + key={dependencyType} + title={dependencyType} + /> + </> + ); + })} + </CardContent> + </CardWrap> ); } - return ( - <NoItems - className="no-dependencies" - text={t('dependencies.has-no-dependencies', { package: name })} - /> - ); + return <NoItems text={t('dependencies.has-no-dependencies', { package: name })} />; }; export default Dependencies; diff --git a/packages/ui-components/src/components/Deprecated/Deprecated.stories.tsx b/packages/ui-components/src/components/Deprecated/Deprecated.stories.tsx index 999421f40..65cb500c6 100644 --- a/packages/ui-components/src/components/Deprecated/Deprecated.stories.tsx +++ b/packages/ui-components/src/components/Deprecated/Deprecated.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { default as Deprecated } from '.'; export default { - title: 'Deprecated', + title: 'Components/Detail/Deprecated', }; export const DeprecatedAll: any = () => <Deprecated message="this is deprecated" />; diff --git a/packages/ui-components/src/components/Developers/Developers.stories.tsx b/packages/ui-components/src/components/Developers/Developers.stories.tsx index 4510d4bd0..83062a9a2 100644 --- a/packages/ui-components/src/components/Developers/Developers.stories.tsx +++ b/packages/ui-components/src/components/Developers/Developers.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { DeveloperType, default as Developers } from '.'; export default { - title: 'Developers', + title: 'Components/Sidebar/Developers', }; export const DevelopersAll: any = () => ( diff --git a/packages/ui-components/src/components/Distribution/Dist.stories.tsx b/packages/ui-components/src/components/Distribution/Dist.stories.tsx index 8b6b7ada9..1f1abbc44 100644 --- a/packages/ui-components/src/components/Distribution/Dist.stories.tsx +++ b/packages/ui-components/src/components/Distribution/Dist.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { default as Dist } from '.'; export default { - title: 'Dist', + title: 'Components/Sidebar/Dist', }; export const AllProperties: any = () => ( diff --git a/packages/ui-components/src/components/Engines/Engines.stories.tsx b/packages/ui-components/src/components/Engines/Engines.stories.tsx index 56dce501a..bec1905ed 100644 --- a/packages/ui-components/src/components/Engines/Engines.stories.tsx +++ b/packages/ui-components/src/components/Engines/Engines.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { default as Engines } from '.'; export default { - title: 'Engines', + title: 'Components/Sidebar/Engines', }; export const EnginesNpmNode: any = () => ( diff --git a/packages/ui-components/src/components/FundButton/FundButton.stories.tsx b/packages/ui-components/src/components/FundButton/FundButton.stories.tsx index e30cda10e..402d6a958 100644 --- a/packages/ui-components/src/components/FundButton/FundButton.stories.tsx +++ b/packages/ui-components/src/components/FundButton/FundButton.stories.tsx @@ -1,25 +1,56 @@ import Box from '@mui/material/Box'; import Stack from '@mui/material/Stack'; +import type { Meta, StoryObj } from '@storybook/react'; import React from 'react'; import { default as FundButton } from '.'; -export default { - title: 'FundButton', +const meta: Meta<typeof FundButton> = { + title: 'Components/Sidebar/FundButton', + component: FundButton, }; -export const FundButtonUrl: any = () => ( - <Box sx={{ width: '100%' }}> - <Stack spacing={2}> - <FundButton - packageMeta={{ - latest: { - funding: { - url: 'https://opencollective.com/verdaccio', - }, - }, - }} - /> - </Stack> - </Box> -); +export default meta; +type Story = StoryObj<typeof FundButton>; + +export const Primary: Story = { + name: 'Default', + render: () => { + return ( + <Box sx={{ width: '100%' }}> + <Stack spacing={2}> + <FundButton + packageMeta={{ + latest: { + funding: { + url: 'https://opencollective.com/verdaccio', + }, + }, + }} + /> + </Stack> + </Box> + ); + }, +}; + +export const Bad: Story = { + name: 'Bad Link (empty)', + render: () => { + return ( + <Box sx={{ width: '100%' }}> + <Stack spacing={2}> + <FundButton + packageMeta={{ + latest: { + funding: { + url: 'bad_link', + }, + }, + }} + /> + </Stack> + </Box> + ); + }, +}; diff --git a/packages/ui-components/src/components/Help/Help.stories.tsx b/packages/ui-components/src/components/Help/Help.stories.tsx index 06c175683..5dd6b8f11 100644 --- a/packages/ui-components/src/components/Help/Help.stories.tsx +++ b/packages/ui-components/src/components/Help/Help.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { default as Help } from '.'; export default { - title: 'Help', + title: 'Components/Home/Help', }; export const HelpMessage: any = () => <Help />; diff --git a/packages/ui-components/src/components/Help/__snapshots__/Help.test.tsx.snap b/packages/ui-components/src/components/Help/__snapshots__/Help.test.tsx.snap index cb3f07b2d..aeeeab29d 100644 --- a/packages/ui-components/src/components/Help/__snapshots__/Help.test.tsx.snap +++ b/packages/ui-components/src/components/Help/__snapshots__/Help.test.tsx.snap @@ -116,8 +116,8 @@ exports[`<Help /> component should load the component in default state 1`] = ` color: rgba(0, 0, 0, 0.54); -webkit-transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; - padding: 12px; - font-size: 1.75rem; + padding: 5px; + font-size: 1.125rem; } .emotion-10::-moz-focus-inner { @@ -315,7 +315,7 @@ exports[`<Help /> component should load the component in default state 1`] = ` help.first-step-command-line </span> <button - class="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeLarge emotion-10" + class="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeSmall emotion-10" data-testid="segments" tabindex="0" type="button" @@ -350,7 +350,7 @@ exports[`<Help /> component should load the component in default state 1`] = ` help.second-step-command-line </span> <button - class="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeLarge emotion-10" + class="MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeSmall emotion-10" data-testid="segments" tabindex="0" type="button" diff --git a/packages/ui-components/src/components/Icons/Icons.stories.tsx b/packages/ui-components/src/components/Icons/Icons.stories.tsx index b29743a7c..c5559248c 100644 --- a/packages/ui-components/src/components/Icons/Icons.stories.tsx +++ b/packages/ui-components/src/components/Icons/Icons.stories.tsx @@ -16,7 +16,7 @@ import { } from '.'; export default { - title: 'Icons', + title: 'Components/Icons/Overview', }; export const Icons: any = () => ( diff --git a/packages/ui-components/src/components/Install/Install.stories.tsx b/packages/ui-components/src/components/Install/Install.stories.tsx index 67221b095..940993f85 100644 --- a/packages/ui-components/src/components/Install/Install.stories.tsx +++ b/packages/ui-components/src/components/Install/Install.stories.tsx @@ -1,34 +1,87 @@ +/* eslint-disable react-hooks/rules-of-hooks */ +import type { Meta, StoryObj } from '@storybook/react'; import React from 'react'; import { default as Install } from '.'; import { useConfig } from '../../'; -export default { - title: 'Install ', +const meta: Meta<typeof Install> = { + title: 'Components/Sidebar/Install', + component: Install, }; -export const ActionBarAll: any = () => { - const { configOptions } = useConfig(); - return ( - <Install - configOptions={{ ...configOptions, pkgManagers: ['npm', 'yarn', 'pnpm'] }} - packageMeta={{ - _uplinks: {}, - latest: { - name: 'verdaccio-ui/local-storage', - version: '8.0.1-next.1', - dist: { - fileCount: 0, - unpackedSize: 0, - tarball: 'http://localhost:8080/bootstrap/-/bootstrap-4.3.1.tgz', - }, - homepage: 'https://verdaccio.org', - bugs: { - url: 'https://github.com/verdaccio/monorepo/issues', - }, - }, - }} - packageName="jquery" - /> - ); +export default meta; +type Story = StoryObj<typeof Install>; + +const packageMeta = { + _uplinks: {}, + latest: { + name: 'verdaccio-ui/local-storage', + version: '8.0.1-next.1', + dist: { + fileCount: 0, + unpackedSize: 0, + tarball: 'http://localhost:8080/bootstrap/-/bootstrap-4.3.1.tgz', + }, + homepage: 'https://verdaccio.org', + bugs: { + url: 'https://github.com/verdaccio/monorepo/issues', + }, + }, +}; + +export const Primary: Story = { + name: 'Default', + render: () => { + const { configOptions } = useConfig(); + return ( + <Install + configOptions={{ ...configOptions, pkgManagers: ['npm', 'yarn', 'pnpm'] }} + packageMeta={packageMeta} + packageName="jquery" + /> + ); + }, +}; + +export const npmOnly: Story = { + name: 'Only NPM', + render: () => { + const { configOptions } = useConfig(); + return ( + <Install + configOptions={{ ...configOptions, pkgManagers: ['npm'] }} + packageMeta={packageMeta} + packageName="jquery" + /> + ); + }, +}; + +export const yarnOnly: Story = { + name: 'Only Yarn', + render: () => { + const { configOptions } = useConfig(); + return ( + <Install + configOptions={{ ...configOptions, pkgManagers: ['yarn'] }} + packageMeta={packageMeta} + packageName="jquery" + /> + ); + }, +}; + +export const pnpmOnly: Story = { + name: 'Only Pnpm', + render: () => { + const { configOptions } = useConfig(); + return ( + <Install + configOptions={{ ...configOptions, pkgManagers: ['pnpm'] }} + packageMeta={packageMeta} + packageName="jquery" + /> + ); + }, }; diff --git a/packages/ui-components/src/components/Install/Install.tsx b/packages/ui-components/src/components/Install/Install.tsx index f19a3df11..519b95edd 100644 --- a/packages/ui-components/src/components/Install/Install.tsx +++ b/packages/ui-components/src/components/Install/Install.tsx @@ -1,6 +1,8 @@ import styled from '@emotion/styled'; import { Typography } from '@mui/material'; +import Grid from '@mui/material/Grid'; import List from '@mui/material/List'; +import { useTheme } from '@mui/styles'; import React from 'react'; import { useTranslation } from 'react-i18next'; @@ -8,6 +10,7 @@ import { TemplateUIOptions } from '@verdaccio/types'; import { Theme } from '../../Theme'; import { PackageMetaInterface } from '../../types/packageMeta'; +import { SettingsMenu } from '../SettingsMenu'; import InstallListItem, { DependencyManager } from './InstallListItem'; const StyledText = styled(Typography)<{ theme?: Theme }>((props) => ({ @@ -23,30 +26,41 @@ export type Props = { const Install: React.FC<Props> = ({ packageMeta, packageName, configOptions }) => { const { t } = useTranslation(); + const theme = useTheme(); if (!packageMeta || !packageName) { return null; } + const hasNpm = configOptions?.pkgManagers?.includes('npm'); const hasYarn = configOptions?.pkgManagers?.includes('yarn'); const hasPnpm = configOptions?.pkgManagers?.includes('pnpm') ?? true; const hasPkgManagers = hasNpm || hasPnpm || hasYarn; return hasPkgManagers ? ( - <List - data-testid={'installList'} - subheader={<StyledText variant={'subtitle1'}>{t('sidebar.installation.title')}</StyledText>} - > - {hasNpm && ( - <InstallListItem dependencyManager={DependencyManager.NPM} packageName={packageName} /> - )} - {hasYarn && ( - <InstallListItem dependencyManager={DependencyManager.YARN} packageName={packageName} /> - )} - {hasPnpm && ( - <InstallListItem dependencyManager={DependencyManager.PNPM} packageName={packageName} /> - )} - </List> + <> + <Grid + container={true} + justifyContent="flex-end" + sx={{ marginRight: theme.spacing(10), alingText: 'right' }} + > + <SettingsMenu packageName={packageName} /> + </Grid> + <List + data-testid={'installList'} + subheader={<StyledText variant={'subtitle1'}>{t('sidebar.installation.title')}</StyledText>} + > + {hasNpm && ( + <InstallListItem dependencyManager={DependencyManager.NPM} packageName={packageName} /> + )} + {hasYarn && ( + <InstallListItem dependencyManager={DependencyManager.YARN} packageName={packageName} /> + )} + {hasPnpm && ( + <InstallListItem dependencyManager={DependencyManager.PNPM} packageName={packageName} /> + )} + </List> + </> ) : null; }; diff --git a/packages/ui-components/src/components/Install/InstallListItem.tsx b/packages/ui-components/src/components/Install/InstallListItem.tsx index 85a508ae1..042d4e0cc 100644 --- a/packages/ui-components/src/components/Install/InstallListItem.tsx +++ b/packages/ui-components/src/components/Install/InstallListItem.tsx @@ -5,6 +5,7 @@ import ListItemText from '@mui/material/ListItemText'; import React from 'react'; import { useTranslation } from 'react-i18next'; +import { useSettings } from '../../providers/PersistenceSettingProvider'; import CopyToClipBoard from '../CopyClipboard'; import { Npm, Pnpm, Yarn } from '../Icons'; @@ -16,13 +17,13 @@ const InstallItem = styled(ListItem)({ }); const InstallListItemText = styled(ListItemText)({ - padding: '0 10px', + padding: '0 0 0 10px', margin: 0, }); const PackageMangerAvatar = styled(Avatar)({ borderRadius: '0px', - padding: '0', + padding: 0, }); export enum DependencyManager { @@ -38,6 +39,9 @@ interface Interface { const InstallListItem: React.FC<Interface> = ({ packageName, dependencyManager }) => { const { t } = useTranslation(); + const { localSettings } = useSettings(); + const isGlobal = localSettings[packageName]?.global ?? false; + const pkgName = isGlobal ? `-g ${packageName}` : packageName; switch (dependencyManager) { case DependencyManager.NPM: @@ -50,8 +54,12 @@ const InstallListItem: React.FC<Interface> = ({ packageName, dependencyManager } primary={ <CopyToClipBoard dataTestId="installYarn" - text={t('sidebar.installation.install-using-npm-command', { packageName })} - title={t('sidebar.installation.install-using-npm-command', { packageName })} + text={t('sidebar.installation.install-using-npm-command', { + packageName: pkgName, + })} + title={t('sidebar.installation.install-using-npm-command', { + packageName: pkgName, + })} /> } secondary={t('sidebar.installation.install-using-npm')} @@ -68,8 +76,12 @@ const InstallListItem: React.FC<Interface> = ({ packageName, dependencyManager } primary={ <CopyToClipBoard dataTestId="installYarn" - text={t('sidebar.installation.install-using-yarn-command', { packageName })} - title={t('sidebar.installation.install-using-yarn-command', { packageName })} + text={t('sidebar.installation.install-using-yarn-command', { + packageName: pkgName, + })} + title={t('sidebar.installation.install-using-yarn-command', { + packageName: pkgName, + })} /> } secondary={t('sidebar.installation.install-using-yarn')} @@ -86,8 +98,12 @@ const InstallListItem: React.FC<Interface> = ({ packageName, dependencyManager } primary={ <CopyToClipBoard dataTestId="installPnpm" - text={t('sidebar.installation.install-using-pnpm-command', { packageName })} - title={t('sidebar.installation.install-using-pnpm-command', { packageName })} + text={t('sidebar.installation.install-using-pnpm-command', { + packageName: pkgName, + })} + title={t('sidebar.installation.install-using-pnpm-command', { + packageName: pkgName, + })} /> } secondary={t('sidebar.installation.install-using-pnpm')} diff --git a/packages/ui-components/src/components/Loading/Loading.stories.tsx b/packages/ui-components/src/components/Loading/Loading.stories.tsx index f0dcba3d4..a6d23f51e 100644 --- a/packages/ui-components/src/components/Loading/Loading.stories.tsx +++ b/packages/ui-components/src/components/Loading/Loading.stories.tsx @@ -4,7 +4,7 @@ import React from 'react'; import Loading from './Loading'; export default { - title: 'Loading', + title: 'Components/Detail/Loading', }; export const Icons: any = () => ( diff --git a/packages/ui-components/src/components/LoginDialog/LoginDialog.stories.tsx b/packages/ui-components/src/components/LoginDialog/LoginDialog.stories.tsx index 2f24dacfd..01e7e0d83 100644 --- a/packages/ui-components/src/components/LoginDialog/LoginDialog.stories.tsx +++ b/packages/ui-components/src/components/LoginDialog/LoginDialog.stories.tsx @@ -5,7 +5,7 @@ import React from 'react'; import LoginDialog from './LoginDialog'; export default { - title: 'LoginDialog', + title: 'Components/Dialog/LoginDialog', }; export const OpenLoginDialog = { diff --git a/packages/ui-components/src/components/NoItems/NoItems.stories.tsx b/packages/ui-components/src/components/NoItems/NoItems.stories.tsx index 6e20f1b78..9fb11dd86 100644 --- a/packages/ui-components/src/components/NoItems/NoItems.stories.tsx +++ b/packages/ui-components/src/components/NoItems/NoItems.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { default as NoItems } from '.'; export default { - title: 'Install ', + title: 'Components/Detail/No Items', }; export const NoItemsText: any = () => { diff --git a/packages/ui-components/src/components/NoItems/NoItems.tsx b/packages/ui-components/src/components/NoItems/NoItems.tsx index bb3301292..1c99a54ee 100644 --- a/packages/ui-components/src/components/NoItems/NoItems.tsx +++ b/packages/ui-components/src/components/NoItems/NoItems.tsx @@ -1,16 +1,15 @@ +import Alert from '@mui/material/Alert'; import Typography from '@mui/material/Typography'; import React from 'react'; interface Props { text: string; - className?: string; } -const NoItems: React.FC<Props> = ({ className, text, ...props }) => ( - // eslint-disable-next-line verdaccio/jsx-spread - <Typography {...props} className={className} gutterBottom={true} variant="subtitle1"> - {text} - </Typography> +const NoItems: React.FC<Props> = ({ text, ...props }) => ( + <Alert severity="info"> + <Typography {...props}>{text}</Typography> + </Alert> ); export default NoItems; diff --git a/packages/ui-components/src/components/NoItems/__snapshots__/Noitems.test.tsx.snap b/packages/ui-components/src/components/NoItems/__snapshots__/Noitems.test.tsx.snap index 4d6229a09..bfe4ae1be 100644 --- a/packages/ui-components/src/components/NoItems/__snapshots__/Noitems.test.tsx.snap +++ b/packages/ui-components/src/components/NoItems/__snapshots__/Noitems.test.tsx.snap @@ -2,17 +2,98 @@ exports[`<NoItem /> component should load the component in default state 1`] = ` .emotion-0 { + background-color: #fff; + color: rgba(0, 0, 0, 0.87); + -webkit-transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + border-radius: 4px; + box-shadow: none; + font-family: -apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif; + font-weight: 400; + font-size: 0.875rem; + line-height: 1.43; + background-color: rgb(229, 246, 253); + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 6px 16px; + color: rgb(1, 67, 97); +} + +.emotion-0 .MuiAlert-icon { + color: #0288d1; +} + +.emotion-1 { + margin-right: 12px; + padding: 7px 0; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + font-size: 22px; + opacity: 0.9; +} + +.emotion-2 { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 1em; + height: 1em; + display: inline-block; + fill: currentColor; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + -webkit-transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + font-size: inherit; +} + +.emotion-3 { + padding: 8px 0; + min-width: 0; + overflow: auto; +} + +.emotion-4 { margin: 0; font-family: -apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif; font-weight: 400; font-size: 1rem; - line-height: 1.75; - margin-bottom: 0.35em; + line-height: 1.5; } -<h6 - class="MuiTypography-root MuiTypography-subtitle1 MuiTypography-gutterBottom emotion-0" +<div + class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation0 MuiAlert-root MuiAlert-standardInfo MuiAlert-standard emotion-0" + role="alert" > - test -</h6> + <div + class="MuiAlert-icon emotion-1" + > + <svg + aria-hidden="true" + class="MuiSvgIcon-root MuiSvgIcon-fontSizeInherit emotion-2" + data-testid="InfoOutlinedIcon" + focusable="false" + viewBox="0 0 24 24" + > + <path + d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20, 12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10, 10 0 0,0 12,2M11,17H13V11H11V17Z" + /> + </svg> + </div> + <div + class="MuiAlert-message emotion-3" + > + <p + class="MuiTypography-root MuiTypography-body1 emotion-4" + > + test + </p> + </div> +</div> `; diff --git a/packages/ui-components/src/components/Repository/Repository.stories.tsx b/packages/ui-components/src/components/Repository/Repository.stories.tsx index ddcffd3a1..9c5e55439 100644 --- a/packages/ui-components/src/components/Repository/Repository.stories.tsx +++ b/packages/ui-components/src/components/Repository/Repository.stories.tsx @@ -1,31 +1,39 @@ +import type { Meta, StoryObj } from '@storybook/react'; import React from 'react'; import { default as Repository } from '.'; -export default { - title: 'Repository', +const meta: Meta<typeof Repository> = { + title: 'Components/Sidebar/Repository', + component: Repository, }; +export default meta; +type Story = StoryObj<typeof Repository>; -export const RepositoryGit: any = () => { - return ( - <Repository - packageMeta={{ - _uplinks: {}, - latest: { - name: 'verdaccio-ui/local-storage', - version: '8.0.1-next.1', - repository: { - type: 'git', - url: 'git://github.com/verdaccio/ui.git', +export const Primary: Story = { + name: 'Git Repository', + render: () => { + return ( + <Repository + packageMeta={{ + _uplinks: {}, + latest: { + name: 'verdaccio-ui/local-storage', + version: '8.0.1-next.1', + repository: { + type: 'git', + url: 'git://github.com/verdaccio/ui.git', + }, }, - }, - }} - /> - ); + }} + /> + ); + }, }; -export const RepositoryHTTPS: any = () => { - return ( +export const HTTPS: Story = { + name: 'Https repo', + render: () => ( <Repository packageMeta={{ _uplinks: {}, @@ -39,5 +47,24 @@ export const RepositoryHTTPS: any = () => { }, }} /> - ); + ), +}; + +export const HTTP: Story = { + name: 'Http repo', + render: () => ( + <Repository + packageMeta={{ + _uplinks: {}, + latest: { + name: 'verdaccio-ui/local-storage', + version: '8.0.1-next.1', + repository: { + type: 'http', + url: 'http://github.com/verdaccio/ui.git', + }, + }, + }} + /> + ), }; diff --git a/packages/ui-components/src/components/Repository/Repository.tsx b/packages/ui-components/src/components/Repository/Repository.tsx index 7cdd35c49..098ddb619 100644 --- a/packages/ui-components/src/components/Repository/Repository.tsx +++ b/packages/ui-components/src/components/Repository/Repository.tsx @@ -47,13 +47,11 @@ const RepositoryAvatar = styled(Avatar)({ const Repository: React.FC<{ packageMeta: any }> = ({ packageMeta }) => { const { t } = useTranslation(); - - if (!packageMeta?.latest?.repository?.url || !urlUtils.isURL(packageMeta.latest.repository.url)) { + const url = packageMeta?.latest?.repository?.url; + if (!url || !urlUtils.isURL(url)) { return null; } - const { url } = packageMeta.latest.repository; - const getCorrectRepositoryURL = (): string => { if (!url.includes('git+')) { return url; diff --git a/packages/ui-components/src/components/Search/Search.stories.tsx b/packages/ui-components/src/components/Search/Search.stories.tsx index 5974bb955..d8e1943e0 100644 --- a/packages/ui-components/src/components/Search/Search.stories.tsx +++ b/packages/ui-components/src/components/Search/Search.stories.tsx @@ -6,7 +6,7 @@ import { MemoryRouter } from 'react-router'; import Search from './Search'; export default { - title: 'Search', + title: 'Components/Header/Search', }; export const SearchByQuery = { diff --git a/packages/ui-components/src/components/SettingsMenu/SettingsMenu.tsx b/packages/ui-components/src/components/SettingsMenu/SettingsMenu.tsx new file mode 100644 index 000000000..47c248504 --- /dev/null +++ b/packages/ui-components/src/components/SettingsMenu/SettingsMenu.tsx @@ -0,0 +1,67 @@ +import Check from '@mui/icons-material/Check'; +import Settings from '@mui/icons-material/Settings'; +import IconButton from '@mui/material/IconButton'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { useSettings } from '../../providers/PersistenceSettingProvider'; + +interface Props { + packageName: string; +} + +const InstallListItem: React.FC<Props> = ({ packageName }) => { + const { t } = useTranslation(); + const { localSettings, updateSettings } = useSettings(); + const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null); + const open = Boolean(anchorEl); + const handleOpenMenu = (event: React.MouseEvent<HTMLButtonElement>) => { + setAnchorEl(event.currentTarget); + }; + const handleClick = () => { + const statusGlobal = !localSettings[packageName]?.global; + updateSettings({ [packageName]: { global: statusGlobal } }); + setAnchorEl(null); + }; + const handleClose = () => { + setAnchorEl(null); + }; + return ( + <> + <IconButton + aria-controls={open ? 'basic-menu' : undefined} + aria-expanded={open ? 'true' : undefined} + aria-haspopup="true" + id="basic-button" + onClick={handleOpenMenu} + size="small" + > + <Settings fontSize="small" /> + </IconButton> + <Menu + MenuListProps={{ + 'aria-labelledby': 'basic-button', + }} + anchorEl={anchorEl} + id="basic-menu" + onClose={handleClose} + open={open} + > + <MenuItem onClick={handleClick}> + {' '} + {localSettings?.global ? ( + <ListItemIcon> + <Check /> + </ListItemIcon> + ) : null} + {t('sidebar.installation.global')} + </MenuItem> + </Menu> + </> + ); +}; + +export default InstallListItem; diff --git a/packages/ui-components/src/components/SettingsMenu/index.ts b/packages/ui-components/src/components/SettingsMenu/index.ts new file mode 100644 index 000000000..d01f80b4c --- /dev/null +++ b/packages/ui-components/src/components/SettingsMenu/index.ts @@ -0,0 +1 @@ +export { default as SettingsMenu } from './SettingsMenu'; diff --git a/packages/ui-components/src/components/SideBarTittle/SideBarTittle.stories.tsx b/packages/ui-components/src/components/SideBarTittle/SideBarTittle.stories.tsx new file mode 100644 index 000000000..d80c763a4 --- /dev/null +++ b/packages/ui-components/src/components/SideBarTittle/SideBarTittle.stories.tsx @@ -0,0 +1,68 @@ +import type { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; + +import { default as SideBarTittle } from '.'; + +const meta: Meta<typeof SideBarTittle> = { + title: 'Components/Sidebar/Title', + component: SideBarTittle, +}; + +export default meta; + +type Story = StoryObj<typeof SideBarTittle>; + +export const Commonjs: Story = { + name: 'CommonJS package', + render: () => ( + <SideBarTittle + isLatest={false} + moduleType="commonjs" + packageName="jquery" + time="2012-12-31T06:54:14.275Z" + version="1.0.0" + /> + ), +}; + +export const ES6: Story = { + name: 'ES6 package', + render: () => ( + <SideBarTittle + isLatest={true} + moduleType="module" + packageName="react" + time="2012-12-31T06:54:14.275Z" + version="14.0.0" + /> + ), +}; + +export const Description: Story = { + name: 'With description', + render: () => ( + <SideBarTittle + description="Storybook's CLI - easiest method of adding storybook to your projects" + isLatest={true} + moduleType="module" + packageName="storybook" + time="2012-12-31T06:54:14.275Z" + version="14.0.0" + /> + ), +}; + +export const WithTypes: Story = { + name: 'With types declaration', + render: () => ( + <SideBarTittle + description="Storybook's CLI - easiest method of adding storybook to your projects" + hasTypes={true} + isLatest={true} + moduleType="module" + packageName="storybook" + time="2012-12-31T06:54:14.275Z" + version="14.0.0" + /> + ), +}; diff --git a/packages/ui-components/src/components/UpLinks/Uplinks.stories.tsx b/packages/ui-components/src/components/UpLinks/Uplinks.stories.tsx index 6265ca2f9..5977afa83 100644 --- a/packages/ui-components/src/components/UpLinks/Uplinks.stories.tsx +++ b/packages/ui-components/src/components/UpLinks/Uplinks.stories.tsx @@ -3,7 +3,7 @@ import React from 'react'; import { default as Uplinks } from '.'; export default { - title: 'Uplinks', + title: 'Components/Detail/Uplinks', }; export const UplinksAll: any = () => { diff --git a/packages/ui-components/src/components/UpLinks/__snapshots__/UpLinks.test.tsx.snap b/packages/ui-components/src/components/UpLinks/__snapshots__/UpLinks.test.tsx.snap index 768fc62ad..73fec822f 100644 --- a/packages/ui-components/src/components/UpLinks/__snapshots__/UpLinks.test.tsx.snap +++ b/packages/ui-components/src/components/UpLinks/__snapshots__/UpLinks.test.tsx.snap @@ -4,40 +4,202 @@ exports[`<UpLinks /> component should render the component when there is no upli { "asFragment": [Function], "baseElement": .emotion-0 { + background-color: #fff; + color: rgba(0, 0, 0, 0.87); + -webkit-transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + border-radius: 4px; + box-shadow: none; + font-family: -apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif; + font-weight: 400; + font-size: 0.875rem; + line-height: 1.43; + background-color: rgb(229, 246, 253); + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 6px 16px; + color: rgb(1, 67, 97); +} + +.emotion-0 .MuiAlert-icon { + color: #0288d1; +} + +.emotion-1 { + margin-right: 12px; + padding: 7px 0; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + font-size: 22px; + opacity: 0.9; +} + +.emotion-2 { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 1em; + height: 1em; + display: inline-block; + fill: currentColor; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + -webkit-transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + font-size: inherit; +} + +.emotion-3 { + padding: 8px 0; + min-width: 0; + overflow: auto; +} + +.emotion-4 { margin: 0; font-family: -apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif; font-weight: 400; font-size: 1rem; - line-height: 1.75; - margin-bottom: 0.35em; + line-height: 1.5; } <body> <div> - <h6 - class="MuiTypography-root MuiTypography-subtitle1 MuiTypography-gutterBottom emotion-0" - data-testid="no-uplinks" + <div + class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation0 MuiAlert-root MuiAlert-standardInfo MuiAlert-standard emotion-0" + role="alert" > - uplinks.no-items - </h6> + <div + class="MuiAlert-icon emotion-1" + > + <svg + aria-hidden="true" + class="MuiSvgIcon-root MuiSvgIcon-fontSizeInherit emotion-2" + data-testid="InfoOutlinedIcon" + focusable="false" + viewBox="0 0 24 24" + > + <path + d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20, 12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10, 10 0 0,0 12,2M11,17H13V11H11V17Z" + /> + </svg> + </div> + <div + class="MuiAlert-message emotion-3" + > + <p + class="MuiTypography-root MuiTypography-body1 emotion-4" + data-testid="no-uplinks" + > + uplinks.no-items + </p> + </div> + </div> </div> </body>, "container": .emotion-0 { + background-color: #fff; + color: rgba(0, 0, 0, 0.87); + -webkit-transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + transition: box-shadow 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + border-radius: 4px; + box-shadow: none; + font-family: -apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif; + font-weight: 400; + font-size: 0.875rem; + line-height: 1.43; + background-color: rgb(229, 246, 253); + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + padding: 6px 16px; + color: rgb(1, 67, 97); +} + +.emotion-0 .MuiAlert-icon { + color: #0288d1; +} + +.emotion-1 { + margin-right: 12px; + padding: 7px 0; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + font-size: 22px; + opacity: 0.9; +} + +.emotion-2 { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + width: 1em; + height: 1em; + display: inline-block; + fill: currentColor; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + -webkit-transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + transition: fill 200ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; + font-size: inherit; +} + +.emotion-3 { + padding: 8px 0; + min-width: 0; + overflow: auto; +} + +.emotion-4 { margin: 0; font-family: -apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif; font-weight: 400; font-size: 1rem; - line-height: 1.75; - margin-bottom: 0.35em; + line-height: 1.5; } <div> - <h6 - class="MuiTypography-root MuiTypography-subtitle1 MuiTypography-gutterBottom emotion-0" - data-testid="no-uplinks" + <div + class="MuiPaper-root MuiPaper-elevation MuiPaper-rounded MuiPaper-elevation0 MuiAlert-root MuiAlert-standardInfo MuiAlert-standard emotion-0" + role="alert" > - uplinks.no-items - </h6> + <div + class="MuiAlert-icon emotion-1" + > + <svg + aria-hidden="true" + class="MuiSvgIcon-root MuiSvgIcon-fontSizeInherit emotion-2" + data-testid="InfoOutlinedIcon" + focusable="false" + viewBox="0 0 24 24" + > + <path + d="M11,9H13V7H11M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20, 12C20,16.41 16.41,20 12,20M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10, 10 0 0,0 12,2M11,17H13V11H11V17Z" + /> + </svg> + </div> + <div + class="MuiAlert-message emotion-3" + > + <p + class="MuiTypography-root MuiTypography-body1 emotion-4" + data-testid="no-uplinks" + > + uplinks.no-items + </p> + </div> + </div> </div>, "debug": [Function], "findAllByAltText": [Function], diff --git a/packages/ui-components/src/components/Versions/types.ts b/packages/ui-components/src/components/Versions/types.ts index 7ac121228..167747612 100644 --- a/packages/ui-components/src/components/Versions/types.ts +++ b/packages/ui-components/src/components/Versions/types.ts @@ -1,7 +1,6 @@ import { PackageMetaInterface } from '../../types/packageMeta'; export interface DetailContextProps { - enableLoading: () => void; hasNotBeenFound: boolean; isLoading: boolean; packageMeta: PackageMetaInterface; @@ -11,7 +10,6 @@ export interface DetailContextProps { } export interface VersionPageConsumerProps { - enableLoading: () => void; packageMeta: PackageMetaInterface; packageName: string; packageVersion?: string; diff --git a/packages/ui-components/src/index.ts b/packages/ui-components/src/index.ts index 4a5eb2d53..badda808b 100644 --- a/packages/ui-components/src/index.ts +++ b/packages/ui-components/src/index.ts @@ -42,6 +42,8 @@ export { VersionLayout } from './layouts/Version'; // providers export { default as AppConfigurationProvider } from './providers/AppConfigurationProvider'; +export { default as PersistenceSettingProvider } from './providers/PersistenceSettingProvider'; + export * from './providers/AppConfigurationProvider'; export { TranslatorProvider, useLanguage, LanguageItem } from './providers/TranslatorProvider'; export * from './providers/TranslatorProvider'; diff --git a/packages/ui-components/src/layouts/Version/Version.stories.tsx b/packages/ui-components/src/layouts/Version/Version.stories.tsx index 4d215f116..f87d2ca4d 100644 --- a/packages/ui-components/src/layouts/Version/Version.stories.tsx +++ b/packages/ui-components/src/layouts/Version/Version.stories.tsx @@ -1,29 +1,44 @@ +import type { Meta, StoryObj } from '@storybook/react'; import React from 'react'; import { MemoryRouter, Route } from 'react-router'; import { VersionProvider } from '../../providers'; import VersionLayout from './Version'; -export default { - title: 'VersionLayout', +const meta: Meta<typeof VersionLayout> = { + title: 'Layout/Version', + component: VersionLayout, }; -export const VersionLayoutStorybook: any = () => ( - <MemoryRouter initialEntries={[`/-/web/detail/storybook`]}> - <Route exact={true} path="/-/web/detail/:package"> - <VersionProvider> - <VersionLayout /> - </VersionProvider> - </Route> - </MemoryRouter> -); +export default meta; +type Story = StoryObj<typeof VersionLayout>; -export const VersionLayoutJquery: any = () => ( - <MemoryRouter initialEntries={[`/-/web/detail/jquery`]}> - <Route exact={true} path="/-/web/detail/:package"> - <VersionProvider> - <VersionLayout /> - </VersionProvider> - </Route> - </MemoryRouter> -); +export const Primary: Story = { + name: 'Storybook', + render: () => { + return ( + <MemoryRouter initialEntries={[`/-/web/detail/storybook`]}> + <Route exact={true} path="/-/web/detail/:package"> + <VersionProvider> + <VersionLayout /> + </VersionProvider> + </Route> + </MemoryRouter> + ); + }, +}; + +export const jQuery: Story = { + name: 'jQuery', + render: () => { + return ( + <MemoryRouter initialEntries={[`/-/web/detail/jquery`]}> + <Route exact={true} path="/-/web/detail/:package"> + <VersionProvider> + <VersionLayout /> + </VersionProvider> + </Route> + </MemoryRouter> + ); + }, +}; diff --git a/packages/ui-components/src/providers/AppConfigurationProvider/AppConfigurationProvider.tsx b/packages/ui-components/src/providers/AppConfigurationProvider/AppConfigurationProvider.tsx index 0fff8313f..28c004ea7 100644 --- a/packages/ui-components/src/providers/AppConfigurationProvider/AppConfigurationProvider.tsx +++ b/packages/ui-components/src/providers/AppConfigurationProvider/AppConfigurationProvider.tsx @@ -54,7 +54,6 @@ const AppConfigurationContext = createContext<ConfigProviderProps>(defaultValues const AppConfigurationProvider: FunctionComponent<{ children: React.ReactElement<any> }> = ({ children, }) => { - // Read only const [configOptions] = useState<TemplateUIOptions>(getConfiguration()); const value = useMemo( diff --git a/packages/ui-components/src/providers/PersistenceSettingProvider/PersistenceSettingProvider.tsx b/packages/ui-components/src/providers/PersistenceSettingProvider/PersistenceSettingProvider.tsx new file mode 100644 index 000000000..3676dc5fa --- /dev/null +++ b/packages/ui-components/src/providers/PersistenceSettingProvider/PersistenceSettingProvider.tsx @@ -0,0 +1,57 @@ +import React, { + FunctionComponent, + createContext, + useCallback, + useContext, + useMemo, + useState, +} from 'react'; + +import useLocalStorage from '../../hooks/useLocalStorage'; + +type PersistenceSettingsProps = { + isGlobal?: boolean; +}; + +const defaultValues: PersistenceSettingsProps = { + isGlobal: false, +}; + +const PersistenceSettingContext = createContext<any>(defaultValues); + +const PersistenceSettingProvider: FunctionComponent<{ children: React.ReactElement<any> }> = ({ + children, +}) => { + // get the initial state from the local storage + const [settings, setSettings] = useLocalStorage(`settings-ui-verdaccio`, {}); + + const [localSettings, setLocalSettings] = useState<PersistenceSettingsProps>( + settings ? settings : {} + ); + + const updateSettings = useCallback( + (newSettings: React.SetStateAction<PersistenceSettingsProps>) => { + setLocalSettings(newSettings); + setSettings(newSettings); + }, + [setSettings] + ); + + const value = useMemo( + () => ({ + localSettings: localSettings ? localSettings : {}, + updateSettings, + }), + [localSettings, updateSettings] + ); + + return ( + <PersistenceSettingContext.Provider value={value}> + {children} + </PersistenceSettingContext.Provider> + ); +}; + +export default PersistenceSettingProvider; + +export const useSettings = () => useContext(PersistenceSettingContext); diff --git a/packages/ui-components/src/providers/PersistenceSettingProvider/index.ts b/packages/ui-components/src/providers/PersistenceSettingProvider/index.ts new file mode 100644 index 000000000..91ef3140a --- /dev/null +++ b/packages/ui-components/src/providers/PersistenceSettingProvider/index.ts @@ -0,0 +1 @@ +export { default, useSettings } from './PersistenceSettingProvider'; diff --git a/packages/ui-components/src/providers/VersionProvider/VersionProvider.tsx b/packages/ui-components/src/providers/VersionProvider/VersionProvider.tsx index a7962f4d1..e714ffa49 100644 --- a/packages/ui-components/src/providers/VersionProvider/VersionProvider.tsx +++ b/packages/ui-components/src/providers/VersionProvider/VersionProvider.tsx @@ -15,7 +15,6 @@ function getRouterPackageName(packageName: string, scope?: string): string { } export interface DetailContextProps { - enableLoading: () => void; hasNotBeenFound: boolean; isLoading: boolean; packageMeta: PackageMetaInterface; @@ -25,7 +24,6 @@ export interface DetailContextProps { } export interface VersionPageConsumerProps { - enableLoading: () => void; packageMeta: PackageMetaInterface; packageName: string; packageVersion?: string; diff --git a/packages/ui-components/src/sections/Detail/Detail.stories.tsx b/packages/ui-components/src/sections/Detail/Detail.stories.tsx index 49ef02aa1..648c7ad1f 100644 --- a/packages/ui-components/src/sections/Detail/Detail.stories.tsx +++ b/packages/ui-components/src/sections/Detail/Detail.stories.tsx @@ -5,7 +5,7 @@ import { VersionProvider } from '../../providers'; import Detail from './Detail'; export default { - title: 'Detail', + title: 'Sections/Detail', }; export const DetailStorybook: any = () => ( 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..2733961c8 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 @@ -23,7 +23,7 @@ exports[`DetailContainer renders correctly 1`] = ` margin-bottom: 16px; } -@media (max-width:599.95px) { +@media (max-width:399.95px) { .emotion-2 .MuiTabs-scrollButtons { display: none; } diff --git a/packages/ui-components/src/sections/Header/Header.stories.tsx b/packages/ui-components/src/sections/Header/Header.stories.tsx index 725c1cd54..e83cadbf5 100644 --- a/packages/ui-components/src/sections/Header/Header.stories.tsx +++ b/packages/ui-components/src/sections/Header/Header.stories.tsx @@ -6,7 +6,7 @@ import { VersionProvider } from '../../providers'; import Header from './Header'; export default { - title: 'Header', + title: 'Sections/Header', }; function CustomInfoDialog({ onCloseDialog, title, isOpen }) { diff --git a/packages/ui-components/src/sections/Home/Home.stories.tsx b/packages/ui-components/src/sections/Home/Home.stories.tsx index 82605faf7..28831ff7f 100644 --- a/packages/ui-components/src/sections/Home/Home.stories.tsx +++ b/packages/ui-components/src/sections/Home/Home.stories.tsx @@ -4,7 +4,7 @@ import { MemoryRouter, Route } from 'react-router'; import Home from './Home'; export default { - title: 'Home', + title: 'Sections/Home', }; export const HomeDefault: any = () => ( diff --git a/packages/ui-components/src/sections/SideBar/Sidebar.stories.tsx b/packages/ui-components/src/sections/SideBar/Sidebar.stories.tsx index 06e036581..34a7c6d5e 100644 --- a/packages/ui-components/src/sections/SideBar/Sidebar.stories.tsx +++ b/packages/ui-components/src/sections/SideBar/Sidebar.stories.tsx @@ -5,7 +5,7 @@ import { VersionProvider } from '../../providers'; import DetailSidebar from './Sidebar'; export default { - title: 'Sidebar', + title: 'Sections/Sidebar', }; export const SidebarLatestPackage: any = () => ( diff --git a/packages/ui-components/src/sections/SideBar/Sidebar.test.tsx b/packages/ui-components/src/sections/SideBar/Sidebar.test.tsx index eafc34616..4cff2bc6a 100644 --- a/packages/ui-components/src/sections/SideBar/Sidebar.test.tsx +++ b/packages/ui-components/src/sections/SideBar/Sidebar.test.tsx @@ -43,7 +43,6 @@ const packageMeta = { // const detailContextValue = { // packageName: 'foo', // readMe: 'test', -// enableLoading: () => {}, // isLoading: false, // hasNotBeenFound: false, // packageMeta: , diff --git a/packages/ui-components/src/test/test-react-testing-library.tsx b/packages/ui-components/src/test/test-react-testing-library.tsx index 7b5470c92..5b85552cd 100644 --- a/packages/ui-components/src/test/test-react-testing-library.tsx +++ b/packages/ui-components/src/test/test-react-testing-library.tsx @@ -6,6 +6,7 @@ import { Provider } from 'react-redux'; import { ThemeProvider } from '../Theme'; import AppConfigurationProvider from '../providers/AppConfigurationProvider'; +import PersistenceSettingProvider from '../providers/PersistenceSettingProvider'; import { Store } from '../store/store'; import i18nConfig from './i18n-config'; @@ -13,13 +14,15 @@ const renderWithStore = (ui: React.ReactElement<any>, store: Store) => render(ui, { wrapper: ({ children }) => ( <Provider store={store}> - <AppConfigurationProvider> - <StyledEngineProvider injectFirst={true}> - <ThemeProvider> - <I18nextProvider i18n={i18nConfig}>{children}</I18nextProvider> - </ThemeProvider> - </StyledEngineProvider> - </AppConfigurationProvider> + <PersistenceSettingProvider> + <AppConfigurationProvider> + <StyledEngineProvider injectFirst={true}> + <ThemeProvider> + <I18nextProvider i18n={i18nConfig}>{children}</I18nextProvider> + </ThemeProvider> + </StyledEngineProvider> + </AppConfigurationProvider> + </PersistenceSettingProvider> </Provider> ), }); @@ -27,11 +30,13 @@ const renderWithStore = (ui: React.ReactElement<any>, store: Store) => const customRender = (node: React.ReactElement, ...options: any) => { return render( <AppConfigurationProvider> - <StyledEngineProvider injectFirst={true}> - <ThemeProvider> - <I18nextProvider i18n={i18nConfig}>{node}</I18nextProvider> - </ThemeProvider> - </StyledEngineProvider> + <PersistenceSettingProvider> + <StyledEngineProvider injectFirst={true}> + <ThemeProvider> + <I18nextProvider i18n={i18nConfig}>{node}</I18nextProvider> + </ThemeProvider> + </StyledEngineProvider> + </PersistenceSettingProvider> </AppConfigurationProvider>, ...options ); diff --git a/packages/ui-components/src/utils/url.test.ts b/packages/ui-components/src/utils/url.test.ts index f6fe8fa75..bf2f6ff09 100644 --- a/packages/ui-components/src/utils/url.test.ts +++ b/packages/ui-components/src/utils/url.test.ts @@ -16,6 +16,10 @@ describe('utils', () => { test('isEmail() - should return false if invalid', () => { expect(isEmail('')).toBeFalsy(); }); + + test('git repo is valid', () => { + expect(isURL('git://github.com/verdaccio/ui.git')).toBeTruthy(); + }); }); describe('extractFileName', () => { diff --git a/packages/ui-components/src/utils/url.ts b/packages/ui-components/src/utils/url.ts index 7be90b1a2..7a80c6acb 100644 --- a/packages/ui-components/src/utils/url.ts +++ b/packages/ui-components/src/utils/url.ts @@ -3,7 +3,7 @@ import isURLValidator from 'validator/lib/isURL'; export function isURL(url: string): boolean { return isURLValidator(url || '', { - protocols: ['http', 'https', 'git+https'], + protocols: ['http', 'https', 'git+https', 'git'], require_protocol: true, require_tld: false, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 201968f6b..dd90565e4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -484,7 +484,7 @@ importers: specifier: workspace:2.0.0-6-next.8 version: link:../../packages/tools/helpers cypress: - specifier: 11.2.0 + specifier: ^11.2.0 version: 11.2.0 debug: specifier: 4.3.4 @@ -3840,7 +3840,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.7 - '@babel/helper-plugin-utils': 7.21.5 + '@babel/helper-plugin-utils': 7.20.2 /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.21.4): resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} @@ -3848,7 +3848,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.21.5 + '@babel/helper-plugin-utils': 7.20.2 dev: true /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.21.8): @@ -5811,7 +5811,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.20.7 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.21.5 '@babel/helper-validator-option': 7.21.0 '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.20.7) '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.20.7) @@ -5825,7 +5825,7 @@ packages: '@babel/core': ^7.0.0-0 dependencies: '@babel/core': 7.21.4 - '@babel/helper-plugin-utils': 7.20.2 + '@babel/helper-plugin-utils': 7.21.5 '@babel/helper-validator-option': 7.21.0 '@babel/plugin-transform-react-display-name': 7.18.6(@babel/core@7.21.4) '@babel/plugin-transform-react-jsx': 7.21.0(@babel/core@7.21.4) @@ -15034,7 +15034,7 @@ packages: pretty-bytes: 5.6.0 proxy-from-env: 1.0.0 request-progress: 3.0.0 - semver: 7.4.0 + semver: 7.5.0 supports-color: 8.1.1 tmp: 0.2.1 untildify: 4.0.0 @@ -24785,7 +24785,7 @@ packages: peerDependencies: react: '>=15' dependencies: - '@babel/runtime': 7.21.5 + '@babel/runtime': 7.21.0 history: 4.10.1 hoist-non-react-statics: 3.3.2 loose-envify: 1.4.0 @@ -25720,14 +25720,6 @@ packages: hasBin: true dev: true - /semver@7.4.0: - resolution: {integrity: sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==} - engines: {node: '>=10'} - hasBin: true - dependencies: - lru-cache: 6.0.0 - dev: true - /semver@7.5.0: resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==} engines: {node: '>=10'}