0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-01-20 22:52:46 -05:00

feat: allow ui hide package managers on sidebar (#2226)

* feat: allow ui hide package managers on sidebar

* add test

* add changeset

* chore: remove snapshot

* chore: update config
This commit is contained in:
Juan Picado 2021-05-04 21:15:07 +02:00 committed by GitHub
parent 9ffa205076
commit aecbd226de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 137 additions and 333 deletions

View file

@ -0,0 +1,32 @@
---
'@verdaccio/types': minor
'@verdaccio/ui-theme': minor
'@verdaccio/web': minor
---
web: allow ui hide package managers on sidebar
If there is a package manager of preference over others, you can define the package managers to be displayed on the detail page and sidebar, just define in the `config.yaml` and web section the list of package managers to be displayed.
```
web:
title: Verdaccio
sort_packages: asc
primary_color: #cccccc
pkgManagers:
- pnpm
- yarn
# - npm
```
To disable all package managers, just define empty:
```
web:
title: Verdaccio
sort_packages: asc
primary_color: #cccccc
pkgManagers:
```
and the section would be hidden.

View file

@ -15,6 +15,50 @@ declare module '@verdaccio/types' {
url?: string;
}
type PackageManagers = 'pnpm' | 'yarn' | 'npm';
// FUTURE: WebConf and TemplateUIOptions should be merged .
type CommonWebConf = {
title?: string;
logo?: string;
favicon?: string;
gravatar?: boolean;
sort_packages?: string;
darkMode?: boolean;
url_prefix?: string;
language?: string;
scope?: string;
pkgManagers?: PackageManagers[];
};
/**
* Options are passed to the index.html
*/
export type TemplateUIOptions = {
uri?: string;
darkMode?: boolean;
protocol?: string;
host?: string;
base: string;
primaryColor?: string;
version?: string;
logoURI?: string;
} & CommonWebConf;
/**
* Options on config.yaml for web
*/
type WebConf = {
// FIXME: rename to primaryColor and move it to CommonWebConf
primary_color?: string;
enable?: boolean;
scriptsHead?: string[];
scriptsBodyAfter?: string[];
metaScripts?: string[];
bodyBefore?: string[];
bodyAfter?: string[];
} & CommonWebConf;
interface Dist {
integrity?: string;
shasum: string;
@ -276,23 +320,6 @@ declare module '@verdaccio/types' {
interface ListenAddress {
[key: string]: string;
}
interface WebConf {
enable?: boolean;
title?: string;
logo?: string;
favicon?: string;
gravatar?: boolean;
sort_packages?: string;
scriptsHead?: string[];
scriptsBodyAfter?: string[];
metaScripts?: string[];
bodyBefore?: string[];
bodyAfter?: string[];
darkMode?: boolean;
primary_color?: string;
}
interface HttpsConfKeyCert {
key: string;
cert: string;

View file

@ -15,6 +15,7 @@ global.__VERDACCIO_BASENAME_UI_OPTIONS = {
darkMode: false,
language: 'en-US',
uri: 'http://localhost:4873',
pkgManagers: ['pnpm', 'yarn', 'npm'],
title: 'Verdaccio Dev UI',
scope: '',
version: 'v1.0.0',

View file

@ -34,7 +34,7 @@ const ActionBar: React.FC = () => {
}
return (
<Box alignItems="center" display="flex" marginBottom="8px">
<Box alignItems="center" display="flex" marginBottom="14px">
{actions.map((action) => (
<ActionBarAction key={action.link} {...action} />
))}

View file

@ -1,6 +1,6 @@
import React from 'react';
import { render } from 'verdaccio-ui/utils/test-react-testing-library';
import { render, screen } from 'verdaccio-ui/utils/test-react-testing-library';
import { DetailContext } from '../../context';
import { DetailContextProps } from '../../version-config';
@ -22,11 +22,14 @@ const ComponentToBeRendered: React.FC = () => (
/* eslint-disable react/jsx-no-bind*/
describe('<Install />', () => {
test('renders correctly', () => {
const { container } = render(<ComponentToBeRendered />);
expect(container.firstChild).toMatchSnapshot();
render(<ComponentToBeRendered />);
expect(screen.getByText('pnpm install foo')).toBeInTheDocument();
expect(screen.getByText('yarn add foo')).toBeInTheDocument();
expect(screen.getByText('npm install foo')).toBeInTheDocument();
});
test('should have 3 children', () => {
window.__VERDACCIO_BASENAME_UI_OPTIONS.pkgManagers = ['yarn', 'pnpm', 'npm'];
const { getByTestId } = render(<ComponentToBeRendered />);
const installListItems = getByTestId('installList');
// installitems + subHeader = 4
@ -34,13 +37,17 @@ describe('<Install />', () => {
});
test('should have the element NPM', () => {
window.__VERDACCIO_BASENAME_UI_OPTIONS.pkgManagers = ['npm'];
const { getByTestId, queryByText } = render(<ComponentToBeRendered />);
expect(getByTestId('installListItem-npm')).toBeTruthy();
expect(queryByText(`npm install ${detailContextValue.packageName}`)).toBeTruthy();
expect(queryByText('Install using npm')).toBeTruthy();
expect(screen.queryByText('pnpm install foo')).not.toBeInTheDocument();
expect(screen.queryByText('yarn add foo')).not.toBeInTheDocument();
});
test('should have the element YARN', () => {
window.__VERDACCIO_BASENAME_UI_OPTIONS.pkgManagers = ['yarn'];
const { getByTestId, queryByText } = render(<ComponentToBeRendered />);
expect(getByTestId('installListItem-yarn')).toBeTruthy();
expect(queryByText(`yarn add ${detailContextValue.packageName}`)).toBeTruthy();
@ -48,6 +55,7 @@ describe('<Install />', () => {
});
test('should have the element PNPM', () => {
window.__VERDACCIO_BASENAME_UI_OPTIONS.pkgManagers = ['pnpm'];
const { getByTestId, queryByText } = render(<ComponentToBeRendered />);
expect(getByTestId('installListItem-pnpm')).toBeTruthy();
expect(queryByText(`pnpm install ${detailContextValue.packageName}`)).toBeTruthy();

View file

@ -5,18 +5,20 @@ import { useTranslation } from 'react-i18next';
import List from 'verdaccio-ui/components/List';
import Text from 'verdaccio-ui/components/Text';
import { Theme } from 'verdaccio-ui/design-tokens/theme';
import { useConfig } from 'verdaccio-ui/providers/config';
import { DetailContext } from '../..';
import InstallListItem, { DependencyManager } from './InstallListItem';
const StyledText = styled(Text)<{ theme?: Theme }>((props) => ({
fontWeight: props.theme && props.theme.fontWeight.bold,
fontWeight: props.theme?.fontWeight.bold,
textTransform: 'capitalize',
}));
const Install: React.FC = () => {
const { t } = useTranslation();
const { configOptions } = useConfig();
const detailContext = useContext(DetailContext);
const { packageMeta, packageName } = detailContext;
@ -25,15 +27,26 @@ const Install: React.FC = () => {
return null;
}
return (
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>}>
<InstallListItem dependencyManager={DependencyManager.NPM} packageName={packageName} />
<InstallListItem dependencyManager={DependencyManager.YARN} packageName={packageName} />
<InstallListItem dependencyManager={DependencyManager.PNPM} packageName={packageName} />
{hasNpm && (
<InstallListItem dependencyManager={DependencyManager.NPM} packageName={packageName} />
)}
{hasYarn && (
<InstallListItem dependencyManager={DependencyManager.YARN} packageName={packageName} />
)}
{hasPnpm && (
<InstallListItem dependencyManager={DependencyManager.PNPM} packageName={packageName} />
)}
</List>
);
) : null;
};
export default Install;

View file

@ -1,266 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Install /> renders correctly 1`] = `
.emotion-0 {
font-weight: 700;
text-transform: capitalize;
}
.emotion-2 {
padding: 0;
}
.emotion-2:hover {
background-color: transparent;
}
.emotion-4 {
border-radius: 0px;
padding: 0;
}
.emotion-4 img {
background-color: transparent;
}
.emotion-6 {
padding: 0 10px;
margin: 0;
}
.emotion-8 {
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: justify;
-webkit-justify-content: space-between;
-ms-flex-pack: justify;
justify-content: space-between;
}
.emotion-10 {
display: inline-block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
height: 21px;
font-size: 1rem;
}
<ul
class="MuiList-root MuiList-padding MuiList-subheader"
data-testid="installList"
>
<span
class="MuiTypography-root emotion-0 emotion-1 MuiTypography-subtitle1"
>
Installation
</span>
<div
aria-disabled="false"
class="MuiButtonBase-root MuiListItem-root emotion-2 emotion-3 MuiListItem-gutters MuiListItem-button"
data-testid="installListItem-npm"
role="button"
tabindex="0"
>
<div
class="MuiAvatar-root MuiAvatar-circle emotion-4 emotion-5"
>
<img
alt="npm"
class="MuiAvatar-img"
src="[object Object]"
/>
</div>
<div
class="MuiListItemText-root emotion-6 emotion-7 MuiListItemText-multiline"
>
<span
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
>
<div
class="emotion-8 emotion-9"
>
<span
class="emotion-10 emotion-11"
>
npm install foo
</span>
<button
class="MuiButtonBase-root MuiIconButton-root"
data-testid="copy-icon"
tabindex="0"
title="Copy to clipboard"
type="button"
>
<span
class="MuiIconButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z"
/>
</svg>
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
</div>
</span>
<p
class="MuiTypography-root MuiListItemText-secondary MuiTypography-body2 MuiTypography-colorTextSecondary MuiTypography-displayBlock"
>
Install using npm
</p>
</div>
<span
class="MuiTouchRipple-root"
/>
</div>
<div
aria-disabled="false"
class="MuiButtonBase-root MuiListItem-root emotion-2 emotion-3 MuiListItem-gutters MuiListItem-button"
data-testid="installListItem-yarn"
role="button"
tabindex="0"
>
<div
class="MuiAvatar-root MuiAvatar-circle emotion-4 emotion-5"
>
<img
alt="yarn"
class="MuiAvatar-img"
src="[object Object]"
/>
</div>
<div
class="MuiListItemText-root emotion-6 emotion-7 MuiListItemText-multiline"
>
<span
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
>
<div
class="emotion-8 emotion-9"
>
<span
class="emotion-10 emotion-11"
>
yarn add foo
</span>
<button
class="MuiButtonBase-root MuiIconButton-root"
data-testid="copy-icon"
tabindex="0"
title="Copy to clipboard"
type="button"
>
<span
class="MuiIconButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z"
/>
</svg>
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
</div>
</span>
<p
class="MuiTypography-root MuiListItemText-secondary MuiTypography-body2 MuiTypography-colorTextSecondary MuiTypography-displayBlock"
>
Install using yarn
</p>
</div>
<span
class="MuiTouchRipple-root"
/>
</div>
<div
aria-disabled="false"
class="MuiButtonBase-root MuiListItem-root emotion-2 emotion-3 MuiListItem-gutters MuiListItem-button"
data-testid="installListItem-pnpm"
role="button"
tabindex="0"
>
<div
class="MuiAvatar-root MuiAvatar-circle emotion-4 emotion-5"
>
<img
alt="pnpm"
class="MuiAvatar-img"
src="[object Object]"
/>
</div>
<div
class="MuiListItemText-root emotion-6 emotion-7 MuiListItemText-multiline"
>
<span
class="MuiTypography-root MuiListItemText-primary MuiTypography-body1 MuiTypography-displayBlock"
>
<div
class="emotion-8 emotion-9"
>
<span
class="emotion-10 emotion-11"
>
pnpm install foo
</span>
<button
class="MuiButtonBase-root MuiIconButton-root"
data-testid="copy-icon"
tabindex="0"
title="Copy to clipboard"
type="button"
>
<span
class="MuiIconButton-label"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z"
/>
</svg>
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
</div>
</span>
<p
class="MuiTypography-root MuiListItemText-secondary MuiTypography-body2 MuiTypography-colorTextSecondary MuiTypography-displayBlock"
>
Install using pnpm
</p>
</div>
<span
class="MuiTouchRipple-root"
/>
</div>
</ul>
`;

View file

@ -1,26 +1,12 @@
import { TemplateUIOptions } from '@verdaccio/types';
import isEmpty from 'lodash/isEmpty';
import isNil from 'lodash/isNil';
import React, { createContext, FunctionComponent, useContext, useMemo, useState } from 'react';
import { PRIMARY_COLOR } from 'verdaccio-ui/utils/colors';
export type VerdaccioOptions = {
url_prefix: string;
base: string;
scope: string;
title: string;
primaryColor: string;
darkMode: boolean;
uri?: string;
language?: string;
version?: string;
protocol?: string;
host?: string;
logo?: string;
};
type ConfigProviderProps = {
configOptions: VerdaccioOptions;
configOptions: TemplateUIOptions;
setConfigOptions: Function;
};
@ -28,6 +14,7 @@ const defaultValues: ConfigProviderProps = {
configOptions: {
primaryColor: PRIMARY_COLOR,
darkMode: false,
pkgManagers: ['yarn', 'pnpm', 'npm'],
scope: '',
base: '',
url_prefix: '',

View file

@ -2,6 +2,9 @@ web:
title: Verdaccio Local Dev
sort_packages: asc
primary_color: #CCC
pkgManagers:
- npm
- yarn
plugins: ../

View file

@ -8,10 +8,11 @@ import config from './webpack.dev.config.babel';
const compiler = webpack(config);
const spinner = ora('Compiler is running...').start();
const port = 4873;
compiler.hooks.done.tap('Verdaccio Dev Server', () => {
if (!global.rebuild) {
spinner.stop();
console.log('Dev Server Listening at http://localhost:4873/');
console.log(`Dev Server Listening at http://localhost:${port}/`);
global.rebuild = true;
}
});
@ -40,7 +41,7 @@ new WebpackDevServer(compiler, {
target: 'http://localhost:8000',
},
],
}).listen(4873, 'localhost', function (err) {
}).listen(port, 'localhost', function (err) {
if (err) {
return console.log(err);
}

View file

@ -1,5 +1,8 @@
import fs from 'fs';
import FriendlyErrorsPlugin from 'friendly-errors-webpack-plugin';
import HTMLWebpackPlugin from 'html-webpack-plugin';
import yalm from 'js-yaml';
import StyleLintPlugin from 'stylelint-webpack-plugin';
import webpack from 'webpack';
@ -8,6 +11,7 @@ import env from '../config/env';
import getPackageJson from './getPackageJson';
import baseConfig from './webpack.config';
const configJsonFormat = yalm.safeLoad(fs.readFileSync('./tools/_verdaccio.config.yaml', 'utf8'));
export default {
...baseConfig,
mode: 'development',
@ -35,8 +39,7 @@ export default {
}),
new HTMLWebpackPlugin({
__UI_OPTIONS: JSON.stringify({
title: 'Verdaccio Dev UI',
scope: '',
...configJsonFormat.web,
filename: 'index.html',
verdaccioURL: '//localhost:4873',
base: new URL('/', 'http://localhost:4873'),

View file

@ -1,13 +1,20 @@
// FIXME: this should comes from @verdaccio/types
type PackageManagers = 'pnpm' | 'yarn' | 'npm';
export interface VerdaccioOptions {
// @deprecated
url_prefix: string;
// @deprecated
base: string;
basePath: string;
basename: string;
scope: string;
title: string;
primaryColor: string;
darkMode: boolean;
uri?: string;
language?: string;
darkMode?: boolean;
version?: string;
protocol?: string;
host?: string;
logo?: string;
pkgManagers?: PackageManagers[];
}
declare global {

View file

@ -29,6 +29,7 @@ export default function renderHTML(config, manifest, manifestFiles, req, res) {
const scope = config?.web?.scope ?? '';
// FIXME: logo URI is incomplete
let logoURI = config?.web?.logo ?? '';
const pkgManagers = config?.web?.pkgManagers ?? ['yarn', 'pnpm', 'npm'];
const version = pkgJSON.version;
const primaryColor = validatePrimaryColor(config?.web?.primary_color) ?? '#4b5e40';
const { scriptsBodyAfter, metaScripts, scriptsbodyBefore } = Object.assign(
@ -48,6 +49,7 @@ export default function renderHTML(config, manifest, manifestFiles, req, res) {
primaryColor,
version,
logoURI,
pkgManagers,
title,
scope,
language,

View file

@ -1,23 +1,9 @@
import buildDebug from 'debug';
import { TemplateUIOptions } from '@verdaccio/types';
import { getManifestValue, Manifest } from './utils/manifest';
const debug = buildDebug('verdaccio:web:render:template');
export type TemplateUIOptions = {
title?: string;
uri?: string;
darkMode?: boolean;
protocol?: string;
host?: string;
url_prefix?: string;
base: string;
primaryColor?: string;
version?: string;
logoURI?: string;
scope?: string;
language?: string;
};
export type Template = {
manifest: Manifest;
options: TemplateUIOptions;