mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-03-25 02:32:52 -05:00
chore: align ui latest from repo
This commit is contained in:
parent
64f0921477
commit
7c0a0c106d
139 changed files with 2183 additions and 708 deletions
|
@ -33,6 +33,7 @@ module.exports = Object.assign({}, config, {
|
|||
// note: this section has to be on sync with webpack configuration
|
||||
'verdaccio-ui/components/(.*)': '<rootDir>/src/components/$1',
|
||||
'verdaccio-ui/utils/(.*)': '<rootDir>/src/utils/$1',
|
||||
'verdaccio-ui/providers/(.*)': '<rootDir>/src/providers/$1',
|
||||
'verdaccio-ui/design-tokens/(.*)': '<rootDir>/src/design-tokens/$1',
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,12 +5,20 @@
|
|||
import { GlobalWithFetchMock } from 'jest-fetch-mock';
|
||||
import 'mutationobserver-shim';
|
||||
|
||||
// @ts-ignore : Property '__APP_VERSION__' does not exist on type 'Global'.
|
||||
global.__APP_VERSION__ = '1.0.0';
|
||||
// @ts-ignore : Property '__VERDACCIO_BASENAME_UI_OPTIONS' does not exist on type 'Global'.
|
||||
global.__VERDACCIO_BASENAME_UI_OPTIONS = { base: 'http://localhost' };
|
||||
// @ts-ignore : Property 'VERDACCIO_API_URL' does not exist on type 'Global'.
|
||||
global.VERDACCIO_API_URL = 'https://verdaccio.tld';
|
||||
global.__VERDACCIO_BASENAME_UI_OPTIONS = {
|
||||
base: 'http://localhost',
|
||||
protocol: 'http',
|
||||
host: 'localhost',
|
||||
primaryColor: '#4b5e40',
|
||||
url_prefix: '',
|
||||
darkMode: false,
|
||||
language: 'en-US',
|
||||
uri: 'http://localhost:4873',
|
||||
title: 'Verdaccio Dev UI',
|
||||
scope: '',
|
||||
version: 'v1.0.0',
|
||||
};
|
||||
|
||||
const customGlobal: GlobalWithFetchMock = global as GlobalWithFetchMock;
|
||||
customGlobal.fetch = require('jest-fetch-mock');
|
||||
|
@ -19,8 +27,7 @@ customGlobal.fetchMock = customGlobal.fetch;
|
|||
// mocking few DOM methods
|
||||
// @ts-ignore : Property 'document' does not exist on type 'Global'.
|
||||
if (global.document) {
|
||||
// @ts-ignore : Type 'Mock<{ selectNodeContents: () => void; }, []>'
|
||||
// is not assignable to type '() => Range'.
|
||||
// @ts-ignore : Type 'Mock<{ selectNodeContents: () => void; }, []>' is not assignable to type '() => Range'.
|
||||
document.createRange = jest.fn((): void => ({
|
||||
selectNodeContents: (): void => {},
|
||||
}));
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
"babel-plugin-emotion": "10.0.33",
|
||||
"bundlesize": "0.18.0",
|
||||
"css-loader": "4.3.0",
|
||||
"css-loader": "5.2.1",
|
||||
"dayjs": "1.9.7",
|
||||
"emotion": "10.0.27",
|
||||
"emotion-theming": "10.0.27",
|
||||
|
|
|
@ -30,7 +30,7 @@ jest.mock('verdaccio-ui/utils/storage', () => {
|
|||
return new LocalStorageMock();
|
||||
});
|
||||
|
||||
jest.mock('verdaccio-ui/utils/api', () => ({
|
||||
jest.mock('verdaccio-ui/providers/API/api', () => ({
|
||||
// eslint-disable-next-line jest/no-mocks-import
|
||||
request: require('../../jest/unit/components/__mocks__/api').default.request,
|
||||
}));
|
||||
|
@ -70,7 +70,7 @@ describe('<App />', () => {
|
|||
expect(queryByTestId('greetings-label')).toBeFalsy();
|
||||
}
|
||||
}
|
||||
}, 20000);
|
||||
});
|
||||
|
||||
test('isUserAlreadyLoggedIn: token already available in storage', async () => {
|
||||
storage.setItem('username', 'verdaccio');
|
||||
|
@ -93,5 +93,5 @@ describe('<App />', () => {
|
|||
expect(queryAllByText('verdaccio')).toBeTruthy();
|
||||
}
|
||||
}
|
||||
}, 20000);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -35,7 +35,7 @@ const StyledBoxContent = styled(Box)<{ theme?: Theme }>(({ theme }) => ({
|
|||
/* eslint-disable react/jsx-no-bind */
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
const App: React.FC = () => {
|
||||
const [user, setUser] = useState<undefined | { username: string }>();
|
||||
const [user, setUser] = useState<undefined | { username: string | null }>();
|
||||
/**
|
||||
* Logout user
|
||||
* Required by: <Header />
|
||||
|
|
|
@ -6,7 +6,7 @@ export interface AppProps {
|
|||
}
|
||||
|
||||
export interface User {
|
||||
username: string;
|
||||
username: string | null;
|
||||
}
|
||||
|
||||
export interface AppContextProps extends AppProps {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
import { useConfig } from 'verdaccio-ui/providers/config';
|
||||
|
||||
import AppContext, { AppProps, User } from './AppContext';
|
||||
|
||||
interface Props {
|
||||
|
@ -8,8 +10,9 @@ interface Props {
|
|||
|
||||
/* eslint-disable react-hooks/exhaustive-deps */
|
||||
const AppContextProvider: React.FC<Props> = ({ children, user }) => {
|
||||
const { configOptions } = useConfig();
|
||||
const [state, setState] = useState<AppProps>({
|
||||
scope: window?.__VERDACCIO_BASENAME_UI_OPTIONS?.scope ?? '',
|
||||
scope: configOptions.scope ?? '',
|
||||
user,
|
||||
});
|
||||
|
||||
|
|
|
@ -24,8 +24,7 @@ enum Route {
|
|||
}
|
||||
|
||||
export const history = createBrowserHistory({
|
||||
// basename is deprecated and already removed in a major released
|
||||
basename: window?.__VERDACCIO_BASENAME_UI_OPTIONS?.basename,
|
||||
basename: window?.__VERDACCIO_BASENAME_UI_OPTIONS?.url_prefix,
|
||||
});
|
||||
|
||||
const AppRoute: React.FC = () => {
|
||||
|
@ -38,7 +37,7 @@ const AppRoute: React.FC = () => {
|
|||
|
||||
const { user } = appContext;
|
||||
|
||||
const isUserLoggedIn = user && user.username;
|
||||
const isUserLoggedIn = user?.username;
|
||||
|
||||
return (
|
||||
<Router history={history}>
|
||||
|
|
|
@ -6,19 +6,15 @@ import Footer from './Footer';
|
|||
|
||||
describe('<Footer /> component', () => {
|
||||
beforeAll(() => {
|
||||
window.__VERDACCIO_BASENAME_UI_OPTIONS = {
|
||||
version: 'v.1.0.0',
|
||||
};
|
||||
window.__VERDACCIO_BASENAME_UI_OPTIONS.version = 'v.1.0.0';
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
// @ts-ignore
|
||||
delete window.__VERDACCIO_BASENAME_UI_OPTIONS;
|
||||
delete window.__VERDACCIO_BASENAME_UI_OPTIONS.version;
|
||||
});
|
||||
|
||||
test('should load the initial state of Footer component', () => {
|
||||
render(<Footer />);
|
||||
// FIXME: this match does not work
|
||||
// expect(screen.getByText('Powered by')).toBeInTheDocument();
|
||||
const { container } = render(<Footer />);
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -15,6 +15,7 @@ import {
|
|||
} from 'verdaccio-ui/components/Icons';
|
||||
import Logo from 'verdaccio-ui/components/Logo';
|
||||
import { Theme } from 'verdaccio-ui/design-tokens/theme';
|
||||
import { useConfig } from 'verdaccio-ui/providers/config';
|
||||
import { goToVerdaccioWebsite } from 'verdaccio-ui/utils/windows';
|
||||
|
||||
import { Wrapper, Left, Right, Love, Inner } from './styles';
|
||||
|
@ -22,6 +23,7 @@ import { Wrapper, Left, Right, Love, Inner } from './styles';
|
|||
/* eslint-disable react/jsx-key */
|
||||
const Footer = () => {
|
||||
const { t } = useTranslation();
|
||||
const { configOptions } = useConfig();
|
||||
return (
|
||||
<Wrapper>
|
||||
<Inner>
|
||||
|
@ -42,9 +44,13 @@ const Footer = () => {
|
|||
</ToolTip>
|
||||
</Left>
|
||||
<Right>
|
||||
{t('footer.powered-by')}
|
||||
<Logo onClick={goToVerdaccioWebsite} size="x-small" />
|
||||
{`/ ${window?.__VERDACCIO_BASENAME_UI_OPTIONS?.version}`}
|
||||
{configOptions?.version && (
|
||||
<>
|
||||
{t('footer.powered-by')}
|
||||
<Logo onClick={goToVerdaccioWebsite} size="x-small" />
|
||||
{`/ ${configOptions.version}`}
|
||||
</>
|
||||
)}
|
||||
</Right>
|
||||
</Inner>
|
||||
</Wrapper>
|
||||
|
|
|
@ -0,0 +1,566 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<Footer /> component should load the initial state of Footer component 1`] = `
|
||||
.emotion-0 {
|
||||
background: #f9f9f9;
|
||||
border-top: 1px solid #e3e3e3;
|
||||
color: #999999;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.emotion-2 {
|
||||
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: end;
|
||||
-webkit-justify-content: flex-end;
|
||||
-ms-flex-pack: end;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (min-width:768px) {
|
||||
.emotion-2 {
|
||||
min-width: 400px;
|
||||
max-width: 800px;
|
||||
margin: auto;
|
||||
-webkit-box-pack: justify;
|
||||
-webkit-justify-content: space-between;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width:1024px) {
|
||||
.emotion-2 {
|
||||
max-width: 1240px;
|
||||
}
|
||||
}
|
||||
|
||||
.emotion-4 {
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (min-width:768px) {
|
||||
.emotion-4 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.emotion-6 {
|
||||
color: #e25555;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.emotion-8 {
|
||||
position: relative;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.emotion-8:hover .emotion-14 {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.emotion-11 {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin: 0px 8px;
|
||||
}
|
||||
|
||||
.emotion-13 {
|
||||
display: inline-grid;
|
||||
grid-template-columns: repeat(8,max-content);
|
||||
grid-gap: 0px 8px;
|
||||
position: absolute;
|
||||
background: #d3dddd;
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
height: 20px;
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
visibility: hidden;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
.emotion-13:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 29%;
|
||||
left: -4px;
|
||||
margin-left: -5px;
|
||||
border: 5px solid;
|
||||
border-color: #d3dddd transparent transparent transparent;
|
||||
-webkit-transform: rotate(90deg);
|
||||
-ms-transform: rotate(90deg);
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.emotion-15 {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.emotion-31 {
|
||||
-webkit-align-items: center;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
display: none;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media (min-width:768px) {
|
||||
.emotion-31 {
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.emotion-33 {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
background-position: center;
|
||||
background-size: contain;
|
||||
background-image: url([object Object]);
|
||||
background-repeat: no-repeat;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
<div
|
||||
class="emotion-0 emotion-1"
|
||||
>
|
||||
<div
|
||||
class="emotion-2 emotion-3"
|
||||
>
|
||||
<div
|
||||
class="emotion-4 emotion-5"
|
||||
>
|
||||
Made with
|
||||
<span
|
||||
class="emotion-6 emotion-7"
|
||||
>
|
||||
♥
|
||||
</span>
|
||||
on
|
||||
<span
|
||||
class="emotion-8 emotion-9"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root emotion-10 emotion-11 emotion-12"
|
||||
focusable="false"
|
||||
viewBox="0 0 45 45"
|
||||
>
|
||||
<defs>
|
||||
<clippath
|
||||
id="prefix__a"
|
||||
>
|
||||
<path
|
||||
d="M0 36h36V0H0v36z"
|
||||
/>
|
||||
</clippath>
|
||||
<clippath
|
||||
id="prefix__b"
|
||||
>
|
||||
<path
|
||||
d="M18 36C8.059 36 0 27.941 0 18S8.059 0 18 0s18 8.059 18 18-8.059 18-18 18z"
|
||||
/>
|
||||
</clippath>
|
||||
</defs>
|
||||
<g
|
||||
clip-path="url(#prefix__a)"
|
||||
transform="matrix(1.25 0 0 -1.25 0 45)"
|
||||
>
|
||||
<path
|
||||
d="M36 18c0-9.941-8.059-18-18-18S0 8.059 0 18s8.059 18 18 18 18-8.059 18-18"
|
||||
fill="#88c9f9"
|
||||
/>
|
||||
</g>
|
||||
<g
|
||||
clip-path="url(#prefix__b)"
|
||||
transform="matrix(1.25 0 0 -1.25 0 45)"
|
||||
>
|
||||
<path
|
||||
d="M3.627 28.952c-.45 2.93 2.195 4.156 3.607 4.47 1.412.314 2.776.62 2.933-.006.156-.628.311-1.46 1.173-1.148.862.314 3.043.56 4.063 1.342 1.02.783 2.244.787 3.264.473 1.02-.313 3.877-.227 3.25-1.167-.627-.94-1.825-.827-2.45-1.924-.628-1.099.171-1.826 1.033-1.826.865 0 1.71-.135 2.26.727.548.863-.383 2.463.324 2.357.706-.106 1.477-.866 2.03-2.043.547-1.176 1.408-.47 1.723-1.176.313-.705 2.04-2.039 1.177-1.804-.864.236-1.726.392-1.96-.47-.237-.863.388-1.726-.237-1.647-.627.08-.86-.089-1.725-.004-.862.083-1.333.631-2.039-.545-.705-1.175-1.254-1.96-1.567-2.509-.315-.549-.785-.86-.55-1.96.235-1.099-.628-.785-.628.156 0 .94-.548 1.098-1.253.942-.706-.157-1.803-.313-1.724-1.098.077-.784-.315-1.725.313-2.352.627-.629 1.33.076 1.723-.158.393-.237 1.525-.023 1.133-.416-.393-.39-1.76-.88-.976-1.509a4.831 4.831 0 011.893-.907c.313-.08.062.774 1.083 1.166 1.017.392 2.608 1.29 3 .584.391-.705.338-.595 1.75-.75 1.41-.156 1.79-.585 2.417-1.917.626-1.333.446-1.192 1.462-1.58 1.021-.394 1.678-.223.737-1.087-.94-.86-1.65-.814-2.199-1.833-.55-1.017-.153-1.73-1.25-2.75A20.755 20.755 0 0024 4c-.618-.37-2.162-2.07-3.083-2.667-.834-.54-1.083 0-1.083 0s.256 1.667.964 2.372c.704.705 1.105 3.344.87 4.128-.235.783-1.36 1.02-1.75 1.333-.393.312-1.418 1.548-1.418 2.334 0 .784 1.71 2.81 1.71 2.81.218-1.089-1.039.328-1.627.523-.47.157-1.542 1.656-2.459 1.814-.916.16-1.363.7-2.068 1.25-.706.55-2.43 1.332-2.353 2.195.08.862-1.725 1.568-2.038 1.568-.314 0-1.019 0-1.647 1.098-.627 1.098-1.725 2.196-1.41 2.98.312.783.391 1.726.233 2.588-.156.862-1.332 1.176-1.567.941-.235-.236-1.489-1.335-1.647-.315"
|
||||
fill="#5c913b"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
<span
|
||||
class="emotion-13 emotion-14"
|
||||
>
|
||||
<svg
|
||||
class="MuiSvgIcon-root emotion-15 emotion-12"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 45 45"
|
||||
>
|
||||
<defs>
|
||||
<clippath
|
||||
id="prefix__a"
|
||||
>
|
||||
<path
|
||||
d="M0 36h36V0H0v36z"
|
||||
/>
|
||||
</clippath>
|
||||
</defs>
|
||||
<g
|
||||
clip-path="url(#prefix__a)"
|
||||
transform="matrix(1.25 0 0 -1.25 0 45)"
|
||||
>
|
||||
<path
|
||||
d="M36 9a4 4 0 00-4-4H4a4 4 0 00-4 4v18a4 4 0 004 4h28a4 4 0 004-4V9z"
|
||||
fill="#c60a1d"
|
||||
/>
|
||||
<path
|
||||
d="M36 12H0v12h36V12z"
|
||||
fill="#ffc400"
|
||||
/>
|
||||
<path
|
||||
d="M9 19v-3a3 3 0 116 0v3H9z"
|
||||
fill="#ea596e"
|
||||
/>
|
||||
<path
|
||||
d="M12 17h3v3h-3v-3z"
|
||||
fill="#f4a2b2"
|
||||
/>
|
||||
<path
|
||||
d="M12 17H9v3h3v-3z"
|
||||
fill="#dd2e44"
|
||||
/>
|
||||
<path
|
||||
d="M15 21.5c0-.829-1.343-1.5-3-1.5s-3 .671-3 1.5 1.343 1.5 3 1.5 3-.671 3-1.5"
|
||||
fill="#ea596e"
|
||||
/>
|
||||
<path
|
||||
d="M15 22.25c0 .414-1.343.75-3 .75s-3-.336-3-.75 1.343-.75 3-.75 3 .336 3 .75"
|
||||
fill="#ffac33"
|
||||
/>
|
||||
<path
|
||||
d="M7 13h1v7H7v-7zm10 0h-1v7h1v-7z"
|
||||
fill="#99aab5"
|
||||
/>
|
||||
<path
|
||||
d="M9 13H6v1h3v-1zm9 0h-3v1h3v-1zM8 20H7v1h1v-1zm9 0h-1v1h1v-1z"
|
||||
fill="#66757f"
|
||||
/>
|
||||
</g>
|
||||
<title>
|
||||
Spain
|
||||
</title>
|
||||
</svg>
|
||||
<svg
|
||||
class="MuiSvgIcon-root emotion-15 emotion-12"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M512 384c0 31.418-25.473 56.889-56.889 56.889H56.89C25.472 440.889 0 415.417 0 384V128c0-31.418 25.472-56.889 56.889-56.889H455.11C486.53 71.111 512 96.584 512 128v256z"
|
||||
fill="#265fb5"
|
||||
/>
|
||||
<path
|
||||
d="M512 327.111H0V184.89h512v142.22z"
|
||||
fill="#eee"
|
||||
/>
|
||||
<path
|
||||
d="M320.811 256c0 35.797-29.014 64.811-64.811 64.811-35.783 0-64.811-29.014-64.811-64.811s29.027-64.811 64.811-64.811c35.797 0 64.811 29.013 64.811 64.811"
|
||||
fill="#a9bf4c"
|
||||
/>
|
||||
<path
|
||||
d="M312.889 256c0 31.418-25.473 56.889-56.889 56.889S199.111 287.416 199.111 256s25.473-56.889 56.889-56.889 56.889 25.471 56.889 56.889"
|
||||
fill="#eee"
|
||||
/>
|
||||
<path
|
||||
d="M209.891 286.649l45.909-79.517 45.909 79.517H209.89z"
|
||||
fill="#265fb5"
|
||||
/>
|
||||
<path
|
||||
d="M215.04 283.591l40.818-70.685 40.803 70.685H215.04z"
|
||||
fill="#55acee"
|
||||
/>
|
||||
<path
|
||||
d="M215.04 283.591l9.841-17.052 61.483-.783 10.297 17.835H215.04z"
|
||||
fill="#bbddf5"
|
||||
/>
|
||||
<path
|
||||
d="M222.891 272.441l15.331-12.445 6.67 7.553 5.774-6.215 4.893 4.892 4.665-5.12 5.561 5.12 5.106-5.334 4.665 5.334 4.451-4.892 7.325 9.102 1.338 2.674s-7.78 1.55-18.446.669c-10.667-.896-16.882 1.55-25.33 2.66-8.448 1.109-23.553-1.109-23.553-1.109l1.55-2.889z"
|
||||
fill="#5c913b"
|
||||
/>
|
||||
<path
|
||||
d="M237.995 262.67l10.226 11.107-5.12.442-5.774-8.434.668-3.115zm12.231.883l8.22 7.338-3.782.654-3.996-5.546-.442-2.446zm10.439-.442l7.111 6.67L266 270.89l-5.774-6.229.44-1.55zm9.33 0l-2.888 2.66 1.338 1.565 1.55-4.225zm8.889.228l-3.328 4.224 1.109 1.99 2.446-4.664-.227-1.55z"
|
||||
fill="#e2f09f"
|
||||
/>
|
||||
<path
|
||||
d="M256.426 233.671l.939 13.227 7.566-10.867-5.675 11.805 11.805-5.66-10.851 7.553 13.213.939-13.213.952 10.851 7.553-11.805-5.66 5.675 11.805-7.566-10.867-.939 13.226-.939-13.226-7.566 10.867 5.675-11.805-11.805 5.66 10.851-7.553-13.212-.953 13.212-.938-10.85-7.553 11.804 5.66-5.675-11.805 7.566 10.866.94-13.226z"
|
||||
fill="#bbddf5"
|
||||
/>
|
||||
<path
|
||||
d="M256 244.665l-2.66 2.66s.654 4.011.441 4.679C253.554 252.658 256 256 256 256l3.327-3.996-.88-5.334-2.447-2.005z"
|
||||
fill="#dd2e44"
|
||||
/>
|
||||
<path
|
||||
d="M257.28 240.071c10.894 0 17.109 5.334 17.109 5.334l-3.996-7.111s-6.443-4.893-13.995-4.893c-7.567 0-16 6.001-16 6.001l-3.783 7.553s9.771-6.884 20.665-6.884"
|
||||
fill="#269"
|
||||
/>
|
||||
<path
|
||||
d="M257.28 240.071c10.894 0 17.109 5.334 17.109 5.334l-2.888-5.106s-7.78-4.665-15.331-4.665-16.896 5.987-16.896 5.987l-2.66 5.334c.001 0 9.772-6.884 20.666-6.884"
|
||||
fill="#ffcc4d"
|
||||
/>
|
||||
<path
|
||||
d="M257.28 240.071c10.894 0 17.109 5.334 17.109 5.334l-2.005-3.327s-5.988-4.224-16.214-3.783c-7.553 0-18.005 5.561-18.005 5.561l-1.55 3.1c0-.001 9.771-6.885 20.665-6.885"
|
||||
fill="#dd2e44"
|
||||
/>
|
||||
<path
|
||||
d="M264.291 322.873h-14.165V309.29h14.165v13.582zm-16.426-118.898h-10.183l-5.106-12.459 10.198-1.137 5.091 13.596zm23.21 5.66l-6.784-2.261 3.385-16.426 13.028 1.137-9.629 17.55zm-61.141 69.646l-19.811 4.523-.57-13.583 19.812-2.83.569 11.89zm5.106 14.152l-16.426 6.812-3.954-10.766 15.289-5.106 5.091 9.06zm109.269-12.444l-18.105-5.091v-9.63l20.366-2.83-2.261 17.55zm-9.003 18.674l-16.981-7.937 5.646-10.182 18.703 5.66-7.368 12.459z"
|
||||
fill="#eee"
|
||||
/>
|
||||
<title>
|
||||
Nicaragua
|
||||
</title>
|
||||
</svg>
|
||||
<svg
|
||||
class="MuiSvgIcon-root emotion-15 emotion-12"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M0 384c0 31.418 25.473 56.889 56.889 56.889H455.11c31.42 0 56.89-25.473 56.89-56.889v-56.889H0V384z"
|
||||
fill="#138808"
|
||||
/>
|
||||
<path
|
||||
d="M0 327.111h512V184.89H0v142.22z"
|
||||
fill="#eee"
|
||||
/>
|
||||
<path
|
||||
d="M512 184.889V128c0-31.417-25.473-56.889-56.889-56.889H56.89C25.472 71.111 0 96.583 0 128v56.889h512z"
|
||||
fill="#f93"
|
||||
/>
|
||||
<path
|
||||
d="M312.889 256c0-31.431-25.473-56.902-56.903-56.902-31.417 0-56.888 25.472-56.888 56.902 0 31.418 25.472 56.889 56.888 56.889 31.432 0 56.903-25.472 56.903-56.889"
|
||||
fill="navy"
|
||||
/>
|
||||
<path
|
||||
d="M298.666 256c0-23.566-19.115-42.681-42.681-42.681S213.319 232.434 213.319 256s19.1 42.666 42.666 42.666 42.681-19.1 42.681-42.666"
|
||||
fill="#eee"
|
||||
/>
|
||||
<path
|
||||
d="M256 213.334l2.076 32.199 14.251-28.943-10.396 30.535 24.235-21.305-21.291 24.249 30.535-10.396-28.942 14.25L298.666 256l-32.198 2.076 28.942 14.237-30.535-10.383 21.291 24.235-24.235-21.29 10.396 30.535-14.25-28.942L256 298.666l-2.076-32.198-14.252 28.942 10.397-30.535-24.249 21.291 21.305-24.235-30.535 10.383 28.942-14.236L213.334 256l32.199-2.076-28.943-14.251 30.535 10.396-21.305-24.249 24.249 21.305-10.396-30.535 14.25 28.943 2.077-32.2z"
|
||||
fill="navy"
|
||||
opacity="0.6"
|
||||
/>
|
||||
<path
|
||||
d="M241.778 256c0-7.851 6.37-14.223 14.222-14.223s14.223 6.372 14.223 14.223-6.372 14.223-14.223 14.223-14.223-6.372-14.223-14.223"
|
||||
fill="navy"
|
||||
/>
|
||||
<title>
|
||||
India
|
||||
</title>
|
||||
</svg>
|
||||
<svg
|
||||
class="MuiSvgIcon-root emotion-15 emotion-12"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 45 45"
|
||||
>
|
||||
<defs>
|
||||
<clippath
|
||||
id="prefix__a"
|
||||
>
|
||||
<path
|
||||
d="M0 36h36V0H0v36z"
|
||||
/>
|
||||
</clippath>
|
||||
</defs>
|
||||
<g
|
||||
clip-path="url(#prefix__a)"
|
||||
transform="matrix(1.25 0 0 -1.25 0 45)"
|
||||
>
|
||||
<path
|
||||
d="M36 9a4 4 0 00-4-4H4a4 4 0 00-4 4v18a4 4 0 004 4h28a4 4 0 004-4V9z"
|
||||
fill="#009b3a"
|
||||
/>
|
||||
<path
|
||||
d="M32.727 18L18 6.876 3.27 18 18 29.125 32.727 18z"
|
||||
fill="#fedf01"
|
||||
/>
|
||||
<path
|
||||
d="M24.434 18.076a6.458 6.458 0 11-12.917 0 6.458 6.458 0 0112.917 0"
|
||||
fill="#002776"
|
||||
/>
|
||||
<path
|
||||
d="M12.277 21.113a6.406 6.406 0 01-.672-2.023c3.994.29 9.417-1.892 11.744-4.596.402.604.7 1.28.882 2.004-2.871 2.809-7.916 4.63-11.954 4.615"
|
||||
fill="#cbe9d4"
|
||||
/>
|
||||
<path
|
||||
d="M13 16.767h-1v1h1v-1zm1-2h-1v1h1v-1z"
|
||||
fill="#88c9f9"
|
||||
/>
|
||||
<path
|
||||
d="M16 16.767h-1v1h1v-1zm2-1h-1v1h1v-1zm4-2h-1v1h1v-1zm-3-1h-1v1h1v-1zm3 6h-1v1h1v-1z"
|
||||
fill="#55acee"
|
||||
/>
|
||||
<path
|
||||
d="M20 14.767h-1v1h1v-1z"
|
||||
fill="#3b88c3"
|
||||
/>
|
||||
</g>
|
||||
<title>
|
||||
Brazil
|
||||
</title>
|
||||
</svg>
|
||||
<svg
|
||||
class="MuiSvgIcon-root emotion-15 emotion-12"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 45 45"
|
||||
>
|
||||
<defs>
|
||||
<clippath
|
||||
id="prefix__a"
|
||||
>
|
||||
<path
|
||||
d="M0 36h36V0H0v36z"
|
||||
/>
|
||||
</clippath>
|
||||
</defs>
|
||||
<g
|
||||
clip-path="url(#prefix__a)"
|
||||
transform="matrix(1.25 0 0 -1.25 0 45)"
|
||||
>
|
||||
<path
|
||||
d="M36 9a4 4 0 00-4-4H4a4 4 0 00-4 4v18a4 4 0 004 4h28a4 4 0 004-4V9z"
|
||||
fill="#de2910"
|
||||
/>
|
||||
<path
|
||||
d="M7 25.049l.929-2.67 2.826-.06-2.253-1.706.819-2.707L7 19.52l-2.321-1.615.819 2.707-2.253 1.707 2.826.059.929 2.67zm6 3.423l.34-.688.759-.11-.549-.536.129-.756-.679.357-.679-.357.13.756-.55.536.76.11.339.688zm2-4l.34-.688.759-.11-.549-.536.129-.756-.679.357-.679-.357.13.756-.55.536.76.11.339.688zm0-4l.34-.688.759-.11-.549-.536.129-.756-.679.357-.679-.357.13.756-.55.536.76.11.339.688zm-2-3.999l.34-.69.759-.11-.549-.534.129-.757-.679.357-.679-.357.13.757-.55.535.76.11.339.689z"
|
||||
fill="#ffde02"
|
||||
/>
|
||||
</g>
|
||||
<title>
|
||||
China
|
||||
</title>
|
||||
</svg>
|
||||
<svg
|
||||
class="MuiSvgIcon-root emotion-15 emotion-12"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M473.655 88.276H38.345C17.167 88.276 0 105.443 0 126.621v73.471h512v-73.471c0-21.178-17.167-38.345-38.345-38.345zM0 385.379c0 21.177 17.167 38.345 38.345 38.345h435.31c21.177 0 38.345-17.167 38.345-38.345v-73.471H0v73.471z"
|
||||
fill="#ff4b55"
|
||||
/>
|
||||
<path
|
||||
d="M0 200.09h512V311.9H0z"
|
||||
fill="#f5f5f5"
|
||||
/>
|
||||
<title>
|
||||
Austria
|
||||
</title>
|
||||
</svg>
|
||||
<svg
|
||||
class="MuiSvgIcon-root emotion-15 emotion-12"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
>
|
||||
<path
|
||||
d="M473.655 88.276H38.345C17.167 88.276 0 105.443 0 126.621v73.471h512v-73.471c0-21.178-17.167-38.345-38.345-38.345z"
|
||||
fill="#464655"
|
||||
/>
|
||||
<path
|
||||
d="M0 385.379c0 21.177 17.167 38.345 38.345 38.345h435.31c21.177 0 38.345-17.167 38.345-38.345v-73.471H0v73.471z"
|
||||
fill="#ffe15a"
|
||||
/>
|
||||
<path
|
||||
d="M0 200.09h512V311.9H0z"
|
||||
fill="#ff4b55"
|
||||
/>
|
||||
<title>
|
||||
Germany
|
||||
</title>
|
||||
</svg>
|
||||
<svg
|
||||
class="MuiSvgIcon-root emotion-15 emotion-12"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="-60 -40 240 160"
|
||||
>
|
||||
<rect
|
||||
fill="#fe0000"
|
||||
height="100%"
|
||||
width="100%"
|
||||
x="-60"
|
||||
y="-40"
|
||||
/>
|
||||
<rect
|
||||
fill="#000095"
|
||||
height="50%"
|
||||
width="50%"
|
||||
x="-60"
|
||||
y="-40"
|
||||
/>
|
||||
<path
|
||||
d="M8 0L0 30-8 0l8-30M0 8l30-8L0-8l-30 8"
|
||||
fill="#fff"
|
||||
id="prefix__a"
|
||||
/>
|
||||
<use
|
||||
transform="rotate(30)"
|
||||
xlink:href="#prefix__a"
|
||||
/>
|
||||
<use
|
||||
transform="rotate(60)"
|
||||
xlink:href="#prefix__a"
|
||||
/>
|
||||
<circle
|
||||
fill="#000095"
|
||||
r="17"
|
||||
/>
|
||||
<circle
|
||||
fill="#fff"
|
||||
r="15"
|
||||
/>
|
||||
<title>
|
||||
Taiwan
|
||||
</title>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="emotion-31 emotion-32"
|
||||
>
|
||||
Powered by
|
||||
<div
|
||||
class="emotion-33 emotion-34"
|
||||
/>
|
||||
/ v.1.0.0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
|
@ -23,7 +23,7 @@ const props = {
|
|||
/* eslint-disable react/jsx-no-bind*/
|
||||
describe('<Header /> component with logged in state', () => {
|
||||
test('should load the component in logged out state', () => {
|
||||
const { queryByTestId, getByText } = render(
|
||||
const { container, queryByTestId, getByText } = render(
|
||||
<Router>
|
||||
<AppContextProvider>
|
||||
<Header />
|
||||
|
@ -31,12 +31,13 @@ describe('<Header /> component with logged in state', () => {
|
|||
</Router>
|
||||
);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
expect(queryByTestId('header--menu-accountcircle')).toBeNull();
|
||||
expect(getByText('Login')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should load the component in logged in state', () => {
|
||||
const { getByTestId, queryByText } = render(
|
||||
const { container, getByTestId, queryByText } = render(
|
||||
<Router>
|
||||
<AppContextProvider user={props.user}>
|
||||
<Header />
|
||||
|
@ -44,6 +45,7 @@ describe('<Header /> component with logged in state', () => {
|
|||
</Router>
|
||||
);
|
||||
|
||||
expect(container.firstChild).toMatchSnapshot();
|
||||
expect(getByTestId('header--menu-accountcircle')).toBeTruthy();
|
||||
expect(queryByText('Login')).toBeNull();
|
||||
});
|
||||
|
|
|
@ -2,8 +2,8 @@ import React, { useState, useContext } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Button from 'verdaccio-ui/components/Button';
|
||||
import { useConfig } from 'verdaccio-ui/providers/config';
|
||||
import storage from 'verdaccio-ui/utils/storage';
|
||||
import { getRegistryURL } from 'verdaccio-ui/utils/url';
|
||||
|
||||
import AppContext from '../../App/AppContext';
|
||||
|
||||
|
@ -31,6 +31,7 @@ const Header: React.FC<Props> = ({ withoutSearch }) => {
|
|||
}
|
||||
|
||||
const { user, scope, setUser } = appContext;
|
||||
const { configOptions } = useConfig();
|
||||
|
||||
/**
|
||||
* Logouts user
|
||||
|
@ -52,14 +53,14 @@ const Header: React.FC<Props> = ({ withoutSearch }) => {
|
|||
onOpenRegistryInfoDialog={() => setOpenInfoDialog(true)}
|
||||
onToggleLogin={() => setShowLoginModal(!showLoginModal)}
|
||||
onToggleMobileNav={() => setShowMobileNavBar(!showMobileNavBar)}
|
||||
username={user && user.username}
|
||||
username={user?.username}
|
||||
withoutSearch={withoutSearch}
|
||||
/>
|
||||
</InnerNavBar>
|
||||
<HeaderInfoDialog
|
||||
isOpen={isInfoDialogOpen}
|
||||
onCloseDialog={() => setOpenInfoDialog(false)}
|
||||
registryUrl={getRegistryURL()}
|
||||
registryUrl={configOptions.base}
|
||||
scope={scope}
|
||||
/>
|
||||
</NavBar>
|
||||
|
|
|
@ -14,7 +14,7 @@ const HeaderGreetings: React.FC<Props> = ({ username }) => {
|
|||
return (
|
||||
<>
|
||||
<Greetings>{t('header.greetings')}</Greetings>
|
||||
<Label capitalize={true} data-testid="greetings-label" text={username} weight="bold" />
|
||||
<Label data-testid="greetings-label" text={username} weight="bold" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ import { RightSide } from './styles';
|
|||
|
||||
interface Props {
|
||||
withoutSearch?: boolean;
|
||||
username?: string;
|
||||
username?: string | null;
|
||||
onToggleLogin: () => void;
|
||||
onOpenRegistryInfoDialog: () => void;
|
||||
onToggleMobileNav: () => void;
|
||||
|
|
|
@ -25,7 +25,7 @@ import MenuItem from 'verdaccio-ui/components/MenuItem';
|
|||
import { Theme } from 'verdaccio-ui/design-tokens/theme';
|
||||
import ThemeContext from 'verdaccio-ui/design-tokens/ThemeContext';
|
||||
|
||||
import { Language } from '../../../../i18n/config';
|
||||
import { Language } from '../../../i18n/config';
|
||||
|
||||
const lngDetails: Record<Language, { translation: TFunctionKeys; icon: React.ReactElement }> = {
|
||||
'fr-FR': {
|
||||
|
@ -166,7 +166,7 @@ const StyledLanguageIcon = styled(LanguageIcon)<{ theme?: Theme }>(({ theme }) =
|
|||
const Wrapper = styled('div')<{ theme?: Theme }>(({ theme }) => ({
|
||||
width: 220,
|
||||
display: 'none',
|
||||
[`@media screen and (min-width: ${theme && theme.breakPoints.medium}px)`]: {
|
||||
[`@media screen and (min-width: ${theme?.breakPoints.medium}px)`]: {
|
||||
display: 'inline-block',
|
||||
},
|
||||
}));
|
|
@ -1 +0,0 @@
|
|||
export { default } from './LanguageSwitch';
|
|
@ -1,7 +1,13 @@
|
|||
import React from 'react';
|
||||
|
||||
import api from 'verdaccio-ui/utils/api';
|
||||
import { render, waitFor, fireEvent } from 'verdaccio-ui/utils/test-react-testing-library';
|
||||
import api from 'verdaccio-ui/providers/API/api';
|
||||
import {
|
||||
render,
|
||||
waitFor,
|
||||
fireEvent,
|
||||
cleanup,
|
||||
act,
|
||||
} from 'verdaccio-ui/utils/test-react-testing-library';
|
||||
|
||||
import AppContext, { AppContextProps } from '../../AppContext';
|
||||
|
||||
|
@ -16,6 +22,7 @@ describe('<LoginDialog /> component', () => {
|
|||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
jest.resetAllMocks();
|
||||
cleanup();
|
||||
});
|
||||
|
||||
test('should render the component in default state', () => {
|
||||
|
@ -61,12 +68,13 @@ describe('<LoginDialog /> component', () => {
|
|||
const loginDialogButton = await waitFor(() => getByTestId('close-login-dialog-button'));
|
||||
expect(loginDialogButton).toBeTruthy();
|
||||
|
||||
fireEvent.click(loginDialogButton, { open: false });
|
||||
act(() => {
|
||||
fireEvent.click(loginDialogButton, { open: false });
|
||||
});
|
||||
expect(props.onClose).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// TODO
|
||||
test.skip('setCredentials - should set username and password in state', async () => {
|
||||
test('setCredentials - should set username and password in state', async () => {
|
||||
const props = {
|
||||
open: true,
|
||||
onClose: jest.fn(),
|
||||
|
@ -79,7 +87,7 @@ describe('<LoginDialog /> component', () => {
|
|||
})
|
||||
);
|
||||
|
||||
const { getByPlaceholderText, getByText } = render(
|
||||
const { getByPlaceholderText, getByTestId } = render(
|
||||
<AppContext.Provider value={appContextValue}>
|
||||
<LoginDialog onClose={props.onClose} open={props.open} />
|
||||
</AppContext.Provider>
|
||||
|
@ -87,17 +95,23 @@ describe('<LoginDialog /> component', () => {
|
|||
|
||||
// TODO: the input's value is not being updated in the DOM
|
||||
const userNameInput = getByPlaceholderText('Your username');
|
||||
|
||||
fireEvent.focus(userNameInput);
|
||||
|
||||
fireEvent.change(userNameInput, { target: { value: 'xyz' } });
|
||||
|
||||
// TODO: the input's value is not being updated in the DOM
|
||||
const passwordInput = getByPlaceholderText('Your strong password');
|
||||
|
||||
fireEvent.focus(passwordInput);
|
||||
fireEvent.change(passwordInput, { target: { value: '1234' } });
|
||||
|
||||
// TODO: submitting form does not work
|
||||
const signInButton = getByText('Sign in');
|
||||
fireEvent.click(signInButton);
|
||||
const signInButton = getByTestId('login-dialog-form-login-button');
|
||||
expect(signInButton).not.toBeDisabled();
|
||||
act(() => {
|
||||
fireEvent.click(signInButton);
|
||||
});
|
||||
});
|
||||
|
||||
test.todo('validateCredentials: should validate credentials');
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import i18next from 'i18next';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import React, { useState, useContext, useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import Dialog from 'verdaccio-ui/components/Dialog';
|
||||
import DialogContent from 'verdaccio-ui/components/DialogContent';
|
||||
import { makeLogin, LoginError } from 'verdaccio-ui/utils/login';
|
||||
import { useAPI, LoginBody } from 'verdaccio-ui/providers/API/APIProvider';
|
||||
import { LoginError } from 'verdaccio-ui/utils/login';
|
||||
import storage from 'verdaccio-ui/utils/storage';
|
||||
|
||||
import AppContext from '../../../App/AppContext';
|
||||
|
@ -20,6 +23,34 @@ interface Props {
|
|||
const LoginDialog: React.FC<Props> = ({ onClose, open = false }) => {
|
||||
const { t } = useTranslation();
|
||||
const appContext = useContext(AppContext);
|
||||
const { doLogin } = useAPI();
|
||||
|
||||
const makeLogin = useCallback(
|
||||
async (username?: string, password?: string): Promise<LoginBody> => {
|
||||
// checks isEmpty
|
||||
if (isEmpty(username) || isEmpty(password)) {
|
||||
const error = {
|
||||
type: 'error',
|
||||
description: i18next.t('form-validation.username-or-password-cant-be-empty'),
|
||||
};
|
||||
return { error };
|
||||
}
|
||||
|
||||
try {
|
||||
const response: LoginBody = await doLogin(username as string, password as string);
|
||||
|
||||
return response;
|
||||
} catch (e) {
|
||||
console.error('login error', e.message);
|
||||
const error = {
|
||||
type: 'error',
|
||||
description: i18next.t('form-validation.unable-to-sign-in'),
|
||||
};
|
||||
return { error };
|
||||
}
|
||||
},
|
||||
[doLogin]
|
||||
);
|
||||
|
||||
if (!appContext) {
|
||||
throw Error(t('app-context-not-correct-used'));
|
||||
|
@ -42,7 +73,7 @@ const LoginDialog: React.FC<Props> = ({ onClose, open = false }) => {
|
|||
onClose();
|
||||
}
|
||||
},
|
||||
[appContext, onClose]
|
||||
[appContext, onClose, makeLogin]
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -79,7 +79,7 @@ const RegistryInfoContent: React.FC<Props> = (props) => {
|
|||
/* eslint react/prop-types:0 */
|
||||
const TabContainer: React.FC = ({ children }): JSX.Element => {
|
||||
return (
|
||||
<CommandContainer>
|
||||
<CommandContainer data-testid={'tab-content'}>
|
||||
<Typography>{children}</Typography>
|
||||
</CommandContainer>
|
||||
);
|
||||
|
|
|
@ -2,9 +2,17 @@ import React from 'react';
|
|||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import api from 'verdaccio-ui/utils/api';
|
||||
|
||||
import api from 'verdaccio-ui/providers/API/api';
|
||||
import { render, fireEvent, waitFor } from 'verdaccio-ui/utils/test-react-testing-library';
|
||||
|
||||
jest.mock('lodash/debounce', () =>
|
||||
jest.fn((fn) => {
|
||||
fn.cancel = jest.fn();
|
||||
return fn;
|
||||
})
|
||||
);
|
||||
|
||||
import Search from './Search';
|
||||
|
||||
/* eslint-disable verdaccio/jsx-spread */
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { RouteComponentProps, withRouter } from 'react-router';
|
||||
|
||||
import AutoComplete from 'verdaccio-ui/components/AutoComplete';
|
||||
import { callSearch } from 'verdaccio-ui/utils/calls';
|
||||
import { useAPI } from 'verdaccio-ui/providers/API/APIProvider';
|
||||
|
||||
import SearchAdornment from './SearchAdornment';
|
||||
|
||||
|
@ -23,6 +23,7 @@ const Search: React.FC<RouteComponentProps> = ({ history }) => {
|
|||
const [loading, setLoading] = useState(false);
|
||||
const mountedRef = useRef(true);
|
||||
const [requestList, setRequestList] = useState<{ abort: () => void }[]>([]);
|
||||
const { callSearch } = useAPI();
|
||||
|
||||
/**
|
||||
* Cancel all the requests which are in pending state.
|
||||
|
@ -138,7 +139,7 @@ const Search: React.FC<RouteComponentProps> = ({ history }) => {
|
|||
}
|
||||
}
|
||||
},
|
||||
[requestList, setRequestList, setSuggestions, setLoaded, setError, setLoading]
|
||||
[requestList, setRequestList, setSuggestions, setLoaded, setError, setLoading, callSearch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,17 +2,17 @@ import styled from '@emotion/styled';
|
|||
import BugReportIcon from '@material-ui/icons/BugReport';
|
||||
import DownloadIcon from '@material-ui/icons/CloudDownload';
|
||||
import HomeIcon from '@material-ui/icons/Home';
|
||||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Theme } from 'verdaccio-ui/design-tokens/theme';
|
||||
import { useAPI } from 'verdaccio-ui/providers/API/APIProvider';
|
||||
import { extractFileName, downloadFile } from 'verdaccio-ui/utils/url';
|
||||
|
||||
import FloatingActionButton from '../FloatingActionButton';
|
||||
import Link from '../Link';
|
||||
import Tooltip from '../Tooltip';
|
||||
|
||||
import downloadTarball from './download-tarball';
|
||||
|
||||
export const Fab = styled(FloatingActionButton)<{ theme?: Theme }>(({ theme }) => ({
|
||||
backgroundColor:
|
||||
theme?.palette.type === 'light' ? theme?.palette.primary.main : theme?.palette.cyanBlue,
|
||||
|
@ -34,6 +34,14 @@ export interface ActionBarActionProps {
|
|||
/* eslint-disable react/jsx-no-bind */
|
||||
const ActionBarAction: React.FC<ActionBarActionProps> = ({ type, link }) => {
|
||||
const { t } = useTranslation();
|
||||
const { getResource } = useAPI();
|
||||
|
||||
const handleDownload = useCallback(async () => {
|
||||
const fileStream = await getResource(link);
|
||||
const fileName = extractFileName(link);
|
||||
downloadFile(fileStream, fileName);
|
||||
}, [getResource, link]);
|
||||
|
||||
switch (type) {
|
||||
case 'VISIT_HOMEPAGE':
|
||||
return (
|
||||
|
@ -58,7 +66,7 @@ const ActionBarAction: React.FC<ActionBarActionProps> = ({ type, link }) => {
|
|||
case 'DOWNLOAD_TARBALL':
|
||||
return (
|
||||
<Tooltip title={t('action-bar-action.download-tarball')}>
|
||||
<Fab data-testid="download-tarball-btn" onClick={downloadTarball(link)} size="small">
|
||||
<Fab data-testid="download-tarball-btn" onClick={handleDownload} size="small">
|
||||
<DownloadIcon />
|
||||
</Fab>
|
||||
</Tooltip>
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import api from 'verdaccio-ui/utils/api';
|
||||
import { extractFileName, downloadFile } from 'verdaccio-ui/utils/url';
|
||||
|
||||
function downloadTarball(link: string) {
|
||||
return async function downloadHandler(): Promise<void> {
|
||||
const fileStream: Blob = await api.request(link, 'GET', {
|
||||
headers: {
|
||||
['accept']:
|
||||
'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
|
||||
},
|
||||
credentials: 'include',
|
||||
});
|
||||
const fileName = extractFileName(link);
|
||||
downloadFile(fileStream, fileName);
|
||||
};
|
||||
}
|
||||
|
||||
export default downloadTarball;
|
|
@ -1,2 +1 @@
|
|||
export { default } from './ActionBar';
|
||||
export { default as downloadTarball } from './download-tarball';
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export { default } from './AppBar';
|
|
@ -139,6 +139,7 @@ const AutoComplete = memo(
|
|||
onBlur,
|
||||
};
|
||||
|
||||
// this format avoid arrow function eslint rule
|
||||
const renderSuggestionsContainer: RenderSuggestionsContainer = function ({
|
||||
containerProps,
|
||||
children,
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Avatar';
|
|
@ -1,6 +1,8 @@
|
|||
import { default as MaterialUIBox, BoxProps } from '@material-ui/core/Box';
|
||||
import React from 'react';
|
||||
|
||||
const Box: React.FC<BoxProps> = (props) => <MaterialUIBox {...props} />;
|
||||
function Box(props: BoxProps) {
|
||||
return <MaterialUIBox {...props} />;
|
||||
}
|
||||
|
||||
export default Box;
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Box';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Button';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Card';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './CardActions';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './CardContent';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Chip';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './CircularProgress';
|
45
packages/plugins/ui-theme/src/components/CopyToClipBoard.tsx
Normal file
45
packages/plugins/ui-theme/src/components/CopyToClipBoard.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import styled from '@emotion/styled';
|
||||
import FileCopy from '@material-ui/icons/FileCopy';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { copyToClipBoardUtility } from 'verdaccio-ui/utils/cli-utils';
|
||||
|
||||
import IconButton from './IconButton';
|
||||
import Tooltip from './Tooltip';
|
||||
|
||||
interface Props {
|
||||
text: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
function CopyToClipBoard({ text, children, ...props }: Props) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Wrapper {...props}>
|
||||
<Content>{children ?? text}</Content>
|
||||
<Tooltip disableFocusListener={true} title={t('copy-to-clipboard')}>
|
||||
<IconButton onClick={copyToClipBoardUtility(text)} data-testid="copy-icon">
|
||||
<FileCopy />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default CopyToClipBoard;
|
||||
|
||||
const Wrapper = styled('div')({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
});
|
||||
|
||||
const Content = styled('span')({
|
||||
display: 'inline-block',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
textOverflow: 'ellipsis',
|
||||
height: '21px',
|
||||
fontSize: '1rem',
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
import { copyToClipBoardUtility } from 'verdaccio-ui/utils/cli-utils';
|
||||
import { render, cleanup, fireEvent } from 'verdaccio-ui/utils/test-react-testing-library';
|
||||
|
||||
import CopyToClipBoard from './CopyToClipBoard';
|
||||
|
||||
jest.mock('verdaccio-ui/utils/cli-utils');
|
||||
|
||||
describe('<CopyToClipBoard /> component', () => {
|
||||
let wrapper: any;
|
||||
const copyText = 'copy text';
|
||||
|
||||
beforeEach(() => {
|
||||
cleanup();
|
||||
wrapper = render(<CopyToClipBoard text={copyText} />);
|
||||
});
|
||||
|
||||
test('should load the component in default state', () => {
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should call the copyToClipBoardUtility for copy to clipboard utility', () => {
|
||||
fireEvent.click(wrapper.getByTestId('copy-icon'));
|
||||
expect(copyToClipBoardUtility).toHaveBeenCalledWith(copyText);
|
||||
});
|
||||
});
|
|
@ -1,38 +0,0 @@
|
|||
import FileCopy from '@material-ui/icons/FileCopy';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { copyToClipBoardUtility } from 'verdaccio-ui/utils/cli-utils';
|
||||
|
||||
import Tooltip from '../Tooltip';
|
||||
|
||||
import { ClipBoardCopy, ClipBoardCopyText, CopyIcon } from './styles';
|
||||
|
||||
interface Props {
|
||||
text: string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const renderText = (text: string, children: React.ReactNode): JSX.Element => {
|
||||
if (children) {
|
||||
return <ClipBoardCopyText>{children}</ClipBoardCopyText>;
|
||||
}
|
||||
|
||||
return <ClipBoardCopyText>{text}</ClipBoardCopyText>;
|
||||
};
|
||||
|
||||
const CopyToClipBoard: React.FC<Props> = ({ text, children }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<ClipBoardCopy>
|
||||
{renderText(text, children)}
|
||||
<Tooltip disableFocusListener={true} title={t('copy-to-clipboard')}>
|
||||
<CopyIcon onClick={copyToClipBoardUtility(text)} data-testid="copy-icon">
|
||||
<FileCopy />
|
||||
</CopyIcon>
|
||||
</Tooltip>
|
||||
</ClipBoardCopy>
|
||||
);
|
||||
};
|
||||
|
||||
export default CopyToClipBoard;
|
|
@ -1,180 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<CopyToClipBoard /> component should load the component in default state 1`] = `
|
||||
Object {
|
||||
"asFragment": [Function],
|
||||
"baseElement": .emotion-0 {
|
||||
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-2 {
|
||||
display: inline-block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
height: 21px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
class="emotion-0 emotion-1"
|
||||
>
|
||||
<span
|
||||
class="emotion-2 emotion-3"
|
||||
>
|
||||
copy text
|
||||
</span>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root emotion-4 emotion-5"
|
||||
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>
|
||||
</div>
|
||||
</body>,
|
||||
"container": .emotion-0 {
|
||||
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-2 {
|
||||
display: inline-block;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
height: 21px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
<div>
|
||||
<div
|
||||
class="emotion-0 emotion-1"
|
||||
>
|
||||
<span
|
||||
class="emotion-2 emotion-3"
|
||||
>
|
||||
copy text
|
||||
</span>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiIconButton-root emotion-4 emotion-5"
|
||||
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>
|
||||
</div>,
|
||||
"debug": [Function],
|
||||
"findAllByAltText": [Function],
|
||||
"findAllByDisplayValue": [Function],
|
||||
"findAllByLabelText": [Function],
|
||||
"findAllByPlaceholderText": [Function],
|
||||
"findAllByRole": [Function],
|
||||
"findAllByTestId": [Function],
|
||||
"findAllByText": [Function],
|
||||
"findAllByTitle": [Function],
|
||||
"findByAltText": [Function],
|
||||
"findByDisplayValue": [Function],
|
||||
"findByLabelText": [Function],
|
||||
"findByPlaceholderText": [Function],
|
||||
"findByRole": [Function],
|
||||
"findByTestId": [Function],
|
||||
"findByText": [Function],
|
||||
"findByTitle": [Function],
|
||||
"getAllByAltText": [Function],
|
||||
"getAllByDisplayValue": [Function],
|
||||
"getAllByLabelText": [Function],
|
||||
"getAllByPlaceholderText": [Function],
|
||||
"getAllByRole": [Function],
|
||||
"getAllByTestId": [Function],
|
||||
"getAllByText": [Function],
|
||||
"getAllByTitle": [Function],
|
||||
"getByAltText": [Function],
|
||||
"getByDisplayValue": [Function],
|
||||
"getByLabelText": [Function],
|
||||
"getByPlaceholderText": [Function],
|
||||
"getByRole": [Function],
|
||||
"getByTestId": [Function],
|
||||
"getByText": [Function],
|
||||
"getByTitle": [Function],
|
||||
"queryAllByAltText": [Function],
|
||||
"queryAllByDisplayValue": [Function],
|
||||
"queryAllByLabelText": [Function],
|
||||
"queryAllByPlaceholderText": [Function],
|
||||
"queryAllByRole": [Function],
|
||||
"queryAllByTestId": [Function],
|
||||
"queryAllByText": [Function],
|
||||
"queryAllByTitle": [Function],
|
||||
"queryByAltText": [Function],
|
||||
"queryByDisplayValue": [Function],
|
||||
"queryByLabelText": [Function],
|
||||
"queryByPlaceholderText": [Function],
|
||||
"queryByRole": [Function],
|
||||
"queryByTestId": [Function],
|
||||
"queryByText": [Function],
|
||||
"queryByTitle": [Function],
|
||||
"rerender": [Function],
|
||||
"unmount": [Function],
|
||||
}
|
||||
`;
|
|
@ -1 +0,0 @@
|
|||
export { default } from './CopyToClipBoard';
|
|
@ -1,20 +0,0 @@
|
|||
import styled from '@emotion/styled';
|
||||
|
||||
import IconButton from '../IconButton';
|
||||
|
||||
export const ClipBoardCopy = styled('div')({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
});
|
||||
|
||||
export const ClipBoardCopyText = styled('span')({
|
||||
display: 'inline-block',
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
whiteSpace: 'nowrap',
|
||||
height: '21px',
|
||||
fontSize: '1rem',
|
||||
});
|
||||
|
||||
export const CopyIcon = styled(IconButton)({});
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Dialog';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './DialogActions';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './DialogContent';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './DialogTitle';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Divider';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './FloatingActionButton';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './FormControl';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './FormHelperText';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Grid';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Heading';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './IconButton';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Input';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './InputLabel';
|
|
@ -1,7 +1,7 @@
|
|||
import React, { MouseEvent } from 'react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
|
||||
import Text, { TextProps } from '../Text';
|
||||
import Text, { TextProps } from './Text';
|
||||
|
||||
interface Props extends Pick<TextProps, 'variant'> {
|
||||
external?: boolean;
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Link';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './List';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './ListItem';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './ListItemText';
|
|
@ -2,6 +2,7 @@ import styled from '@emotion/styled';
|
|||
import React from 'react';
|
||||
|
||||
import { Theme } from 'verdaccio-ui/design-tokens/theme';
|
||||
import { useConfig } from 'verdaccio-ui/providers/config';
|
||||
|
||||
import blackAndWithLogo from './img/logo-black-and-white.svg';
|
||||
import defaultLogo from './img/logo.svg';
|
||||
|
@ -17,8 +18,6 @@ const logos = {
|
|||
dark: blackAndWithLogo,
|
||||
};
|
||||
|
||||
const logo = window?.__VERDACCIO_BASENAME_UI_OPTIONS?.logoURI;
|
||||
|
||||
interface Props {
|
||||
size?: keyof typeof sizes;
|
||||
onClick?: () => void;
|
||||
|
@ -26,10 +25,11 @@ interface Props {
|
|||
}
|
||||
|
||||
const Logo: React.FC<Props> = ({ size, onClick, className }) => {
|
||||
if (logo) {
|
||||
const { configOptions } = useConfig();
|
||||
if (configOptions?.logo) {
|
||||
return (
|
||||
<ImageLogo onClick={onClick} className={className}>
|
||||
<img alt="logo" height="40px" src={logo} />
|
||||
<img alt="logo" height="40px" src={configOptions.logo} />
|
||||
</ImageLogo>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Menu';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './MenuItem';
|
|
@ -1,5 +1,7 @@
|
|||
import styled from '@emotion/styled';
|
||||
|
||||
import { PRIMARY_COLOR } from 'verdaccio-ui/utils/colors';
|
||||
|
||||
import { default as MuiCard } from '../Card';
|
||||
import { default as Typography } from '../Heading';
|
||||
import List from '../List';
|
||||
|
@ -25,12 +27,12 @@ export const EmptyPackage = styled('img')({
|
|||
});
|
||||
|
||||
export const Heading = styled(Typography)({
|
||||
color: '#4b5e40',
|
||||
color: PRIMARY_COLOR,
|
||||
});
|
||||
|
||||
export const StyledList = styled(List)({
|
||||
padding: 0,
|
||||
color: '#4b5e40',
|
||||
color: PRIMARY_COLOR,
|
||||
});
|
||||
|
||||
export const Card = styled(MuiCard)({
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Paper';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './SnackbarContent';
|
|
@ -1,10 +0,0 @@
|
|||
import { default as MaterialUISvgIcon, SvgIconProps } from '@material-ui/core/SvgIcon';
|
||||
import React, { forwardRef } from 'react';
|
||||
|
||||
type SvgIconRef = SVGSVGElement;
|
||||
|
||||
const SvgIcon = forwardRef<SvgIconRef, SvgIconProps>(function SvgIcon(props, ref) {
|
||||
return <MaterialUISvgIcon {...props} ref={ref} />;
|
||||
});
|
||||
|
||||
export default SvgIcon;
|
|
@ -1 +0,0 @@
|
|||
export { default } from './SvgIcon';
|
|
@ -1 +0,0 @@
|
|||
export { default } from './Tab';
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue