@verdaccio/ui-components 3.0.0-next-7.3 → 3.0.0-next-7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/build/AppTest/AppRoute.js +34 -0
- package/build/AppTest/pages/Front/Home.js +9 -0
- package/build/{components/SideBarTittle → AppTest/pages/Front}/index.js +2 -2
- package/build/AppTest/pages/Version/Version.js +34 -0
- package/build/AppTest/pages/Version/index.js +14 -0
- package/build/components/Dependencies/Dependencies.js +16 -51
- package/build/components/Dependencies/DependencyBlock.js +87 -0
- package/build/components/Dependencies/utits.js +10 -0
- package/build/components/Forbidden/Forbidden.js +75 -0
- package/build/components/Forbidden/index.js +14 -0
- package/build/components/NotFound/NotFound.js +16 -14
- package/build/components/SideBarTitle/SideBarTitle.js +94 -0
- package/build/components/SideBarTitle/index.js +14 -0
- package/build/index.js +10 -2
- package/build/sections/Header/HeaderSettingsDialog.js +2 -5
- package/build/sections/Header/RegistryInfoContent/RegistryInfoContent.js +5 -10
- package/build/sections/SideBar/Sidebar.js +12 -13
- package/build/src/AppTest/App.stories.d.ts +8 -0
- package/build/src/AppTest/AppRoute.d.ts +3 -0
- package/build/src/AppTest/pages/Front/Home.d.ts +2 -0
- package/build/src/AppTest/pages/Front/index.d.ts +1 -0
- package/build/src/AppTest/pages/Version/Version.d.ts +3 -0
- package/build/src/AppTest/pages/Version/index.d.ts +1 -0
- package/build/src/components/Dependencies/Dependencies.d.ts +1 -0
- package/build/src/components/Dependencies/DependencyBlock.d.ts +16 -0
- package/build/src/components/Dependencies/DependencyBlock.test.d.ts +1 -0
- package/build/src/components/Dependencies/utits.d.ts +3 -0
- package/build/src/components/Forbidden/Forbidden.d.ts +3 -0
- package/build/src/components/Forbidden/Forbidden.test.d.ts +1 -0
- package/build/src/components/Forbidden/index.d.ts +1 -0
- package/build/src/components/SideBarTitle/index.d.ts +1 -0
- package/build/src/index.d.ts +2 -1
- package/build/src/sections/Detail/Detail.stories.d.ts +1 -0
- package/build/src/sections/Home/Home.test.d.ts +1 -0
- package/build/src/store/models/manifest.d.ts +11 -0
- package/build/store/api.js +12 -6
- package/build/store/models/manifest.js +20 -2
- package/package.json +1 -2
- package/src/AppTest/App.stories.tsx +32 -0
- package/src/AppTest/AppRoute.tsx +42 -0
- package/src/AppTest/pages/Front/Home.ts +3 -0
- package/src/AppTest/pages/Front/index.ts +1 -0
- package/src/AppTest/pages/Version/Version.tsx +28 -0
- package/src/AppTest/pages/Version/index.ts +1 -0
- package/src/components/Dependencies/Dependencies.test.tsx +1 -11
- package/src/components/Dependencies/Dependencies.tsx +8 -50
- package/src/components/Dependencies/DependencyBlock.test.tsx +33 -0
- package/src/components/Dependencies/DependencyBlock.tsx +69 -0
- package/src/components/Dependencies/utits.ts +3 -0
- package/src/components/Forbidden/Forbidden.test.tsx +38 -0
- package/src/components/Forbidden/Forbidden.tsx +56 -0
- package/src/components/Forbidden/index.ts +1 -0
- package/src/components/Loading/Loading.test.tsx +4 -3
- package/src/components/NotFound/NotFound.tsx +6 -5
- package/src/components/NotFound/Notfound.test.tsx +6 -3
- package/src/components/SideBarTitle/index.ts +1 -0
- package/src/index.ts +2 -1
- package/src/sections/Detail/Detail.stories.tsx +10 -0
- package/src/sections/Header/HeaderSettingsDialog.tsx +1 -2
- package/src/sections/Header/RegistryInfoContent/RegistryInfoContent.tsx +2 -7
- package/src/sections/Home/Home.test.tsx +29 -0
- package/src/sections/Home/Home.tsx +0 -1
- package/src/sections/SideBar/Sidebar.test.tsx +42 -85
- package/src/sections/SideBar/Sidebar.tsx +6 -17
- package/src/store/api.test.ts +3 -2
- package/src/store/api.ts +13 -6
- package/src/store/models/manifest.ts +19 -1
- package/build/components/Dependencies/styles.js +0 -64
- package/build/components/SideBarTittle/SideBarTittle.js +0 -94
- package/build/src/components/Dependencies/styles.d.ts +0 -8
- package/build/src/components/SideBarTittle/index.d.ts +0 -1
- package/src/components/Dependencies/styles.ts +0 -26
- package/src/components/SideBarTittle/index.ts +0 -1
- /package/build/components/{SideBarTittle → SideBarTitle}/utils.js +0 -0
- /package/build/src/components/{SideBarTittle/SideBarTittle.d.ts → SideBarTitle/SideBarTitle.d.ts} +0 -0
- /package/build/src/components/{SideBarTittle/SideBarTittle.stories.d.ts → SideBarTitle/SideBarTitle.stories.d.ts} +0 -0
- /package/build/src/components/{SideBarTittle → SideBarTitle}/utils.d.ts +0 -0
- /package/src/components/{SideBarTittle/SideBarTittle.stories.tsx → SideBarTitle/SideBarTitle.stories.tsx} +0 -0
- /package/src/components/{SideBarTittle/SideBarTittle.tsx → SideBarTitle/SideBarTitle.tsx} +0 -0
- /package/src/components/{SideBarTittle → SideBarTitle}/utils.ts +0 -0
package/build/store/api.js
CHANGED
|
@@ -7,6 +7,8 @@ exports.default = void 0;
|
|
|
7
7
|
exports.handleResponseType = handleResponseType;
|
|
8
8
|
var _storage = _interopRequireDefault(require("./storage"));
|
|
9
9
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
10
|
+
class CustomError extends Error {}
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* Handles response according to content type
|
|
12
14
|
* @param {object} response
|
|
@@ -31,7 +33,9 @@ function handleResponseType(response) {
|
|
|
31
33
|
return Promise.all([response.ok, response.blob()]);
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
|
-
|
|
36
|
+
|
|
37
|
+
// error handling
|
|
38
|
+
return Promise.all([response.ok, response]);
|
|
35
39
|
}
|
|
36
40
|
const AuthHeader = 'Authorization';
|
|
37
41
|
class API {
|
|
@@ -52,12 +56,14 @@ class API {
|
|
|
52
56
|
signal: options.signal,
|
|
53
57
|
...options
|
|
54
58
|
}).then(handleResponseType).then(response => {
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
const [ok, data] = response;
|
|
60
|
+
if (ok === true) {
|
|
61
|
+
resolve(data);
|
|
57
62
|
} else {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
var _data$statusText, _data$status;
|
|
64
|
+
const error = new CustomError((_data$statusText = data === null || data === void 0 ? void 0 : data.statusText) !== null && _data$statusText !== void 0 ? _data$statusText : 'Unknown error');
|
|
65
|
+
error.code = (_data$status = data === null || data === void 0 ? void 0 : data.status) !== null && _data$status !== void 0 ? _data$status : 500;
|
|
66
|
+
reject(error);
|
|
61
67
|
}
|
|
62
68
|
}).catch(error => {
|
|
63
69
|
reject(error);
|
|
@@ -29,6 +29,18 @@ const manifest = exports.manifest = (0, _core.createModel)()({
|
|
|
29
29
|
return {
|
|
30
30
|
...state,
|
|
31
31
|
hasNotBeenFound: true,
|
|
32
|
+
forbidden: false,
|
|
33
|
+
manifest: undefined,
|
|
34
|
+
packageName: undefined,
|
|
35
|
+
packageVersion: undefined,
|
|
36
|
+
readme: undefined
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
forbidden(state) {
|
|
40
|
+
return {
|
|
41
|
+
...state,
|
|
42
|
+
forbidden: true,
|
|
43
|
+
hasNotBeenFound: false,
|
|
32
44
|
manifest: undefined,
|
|
33
45
|
packageName: undefined,
|
|
34
46
|
packageVersion: undefined,
|
|
@@ -46,6 +58,7 @@ const manifest = exports.manifest = (0, _core.createModel)()({
|
|
|
46
58
|
...state,
|
|
47
59
|
isError: true,
|
|
48
60
|
hasNotBeenFound: false,
|
|
61
|
+
forbidden: false,
|
|
49
62
|
manifest: undefined,
|
|
50
63
|
packageName: undefined,
|
|
51
64
|
packageVersion: undefined,
|
|
@@ -64,7 +77,8 @@ const manifest = exports.manifest = (0, _core.createModel)()({
|
|
|
64
77
|
packageName,
|
|
65
78
|
packageVersion,
|
|
66
79
|
readme,
|
|
67
|
-
hasNotBeenFound: false
|
|
80
|
+
hasNotBeenFound: false,
|
|
81
|
+
forbidden: false
|
|
68
82
|
};
|
|
69
83
|
}
|
|
70
84
|
},
|
|
@@ -90,7 +104,11 @@ const manifest = exports.manifest = (0, _core.createModel)()({
|
|
|
90
104
|
readme
|
|
91
105
|
});
|
|
92
106
|
} catch (error) {
|
|
93
|
-
|
|
107
|
+
if (error.code === 404) {
|
|
108
|
+
dispatch.manifest.notFound();
|
|
109
|
+
} else {
|
|
110
|
+
dispatch.manifest.forbidden();
|
|
111
|
+
}
|
|
94
112
|
}
|
|
95
113
|
}
|
|
96
114
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@verdaccio/ui-components",
|
|
3
|
-
"version": "3.0.0-next-7.
|
|
3
|
+
"version": "3.0.0-next-7.5",
|
|
4
4
|
"description": "theme ui component",
|
|
5
5
|
"author": "Juan Picado <juanpicado19@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,7 +42,6 @@
|
|
|
42
42
|
"react-router-dom": "5.3.4",
|
|
43
43
|
"react-virtualized": "9.22.5",
|
|
44
44
|
"redux": "4.2.1",
|
|
45
|
-
"remark-gfm": "3.0.1",
|
|
46
45
|
"validator": "13.11.0"
|
|
47
46
|
},
|
|
48
47
|
"devDependencies": {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { MemoryRouter } from 'react-router';
|
|
3
|
+
|
|
4
|
+
import AppRoute from './AppRoute';
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
title: 'App/Main',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const ApplicationStoryBook: any = () => (
|
|
11
|
+
<MemoryRouter initialEntries={[`/-/web/detail/storybook`]}>
|
|
12
|
+
<AppRoute />
|
|
13
|
+
</MemoryRouter>
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
export const ApplicationjQuery: any = () => (
|
|
17
|
+
<MemoryRouter initialEntries={[`/-/web/detail/jquery`]}>
|
|
18
|
+
<AppRoute />
|
|
19
|
+
</MemoryRouter>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
export const ApplicationForbidden: any = () => (
|
|
23
|
+
<MemoryRouter initialEntries={[`/-/web/detail/JSONStream`]}>
|
|
24
|
+
<AppRoute />
|
|
25
|
+
</MemoryRouter>
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export const ApplicationNotFound: any = () => (
|
|
29
|
+
<MemoryRouter initialEntries={[`/-/web/detail/kleur`]}>
|
|
30
|
+
<AppRoute />
|
|
31
|
+
</MemoryRouter>
|
|
32
|
+
);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Route as ReactRouterDomRoute, Switch } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import { NotFound, Route, VersionProvider, loadable } from '../index';
|
|
5
|
+
|
|
6
|
+
const VersionPage = loadable(() => import(/* webpackChunkName: "Version" */ './pages/Version'));
|
|
7
|
+
const Front = loadable(() => import(/* webpackChunkName: "Home" */ './pages/Front'));
|
|
8
|
+
|
|
9
|
+
const AppRoute: React.FC = () => {
|
|
10
|
+
return (
|
|
11
|
+
<Switch>
|
|
12
|
+
<ReactRouterDomRoute exact={true} path={Route.ROOT}>
|
|
13
|
+
<Front />
|
|
14
|
+
</ReactRouterDomRoute>
|
|
15
|
+
<ReactRouterDomRoute exact={true} path={Route.PACKAGE}>
|
|
16
|
+
<VersionProvider>
|
|
17
|
+
<VersionPage />
|
|
18
|
+
</VersionProvider>
|
|
19
|
+
</ReactRouterDomRoute>
|
|
20
|
+
<ReactRouterDomRoute exact={true} path={Route.PACKAGE_VERSION}>
|
|
21
|
+
<VersionProvider>
|
|
22
|
+
<VersionPage />
|
|
23
|
+
</VersionProvider>
|
|
24
|
+
</ReactRouterDomRoute>
|
|
25
|
+
<ReactRouterDomRoute exact={true} path={Route.SCOPE_PACKAGE_VERSION}>
|
|
26
|
+
<VersionProvider>
|
|
27
|
+
<VersionPage />
|
|
28
|
+
</VersionProvider>
|
|
29
|
+
</ReactRouterDomRoute>
|
|
30
|
+
<ReactRouterDomRoute exact={true} path={Route.SCOPE_PACKAGE}>
|
|
31
|
+
<VersionProvider>
|
|
32
|
+
<VersionPage />
|
|
33
|
+
</VersionProvider>
|
|
34
|
+
</ReactRouterDomRoute>
|
|
35
|
+
<ReactRouterDomRoute>
|
|
36
|
+
<NotFound />
|
|
37
|
+
</ReactRouterDomRoute>
|
|
38
|
+
</Switch>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default AppRoute;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Home';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useSelector } from 'react-redux';
|
|
3
|
+
|
|
4
|
+
import Forbidden from '../../../components/Forbidden';
|
|
5
|
+
import { Loading, NotFound, RootState, VersionLayout } from '../../../index';
|
|
6
|
+
|
|
7
|
+
const Version: React.FC = () => {
|
|
8
|
+
const manifestStore = useSelector((state: RootState) => state.manifest);
|
|
9
|
+
const isLoading = useSelector((state: RootState) => state?.loading?.models.manifest);
|
|
10
|
+
|
|
11
|
+
if (isLoading) {
|
|
12
|
+
return <Loading />;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// @ts-expect-error
|
|
16
|
+
if (manifestStore.forbidden) {
|
|
17
|
+
return <Forbidden />;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// @ts-expect-error
|
|
21
|
+
if (manifestStore.hasNotBeenFound) {
|
|
22
|
+
return <NotFound />;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return <VersionLayout />;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default Version;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Version';
|
|
@@ -6,7 +6,6 @@ import Dependencies from './Dependencies';
|
|
|
6
6
|
|
|
7
7
|
describe('<Dependencies /> component', () => {
|
|
8
8
|
test('Renders a message when there are no dependencies', () => {
|
|
9
|
-
// Given
|
|
10
9
|
const packageMeta = {
|
|
11
10
|
latest: {
|
|
12
11
|
name: 'verdaccio',
|
|
@@ -25,15 +24,12 @@ describe('<Dependencies /> component', () => {
|
|
|
25
24
|
_uplinks: {},
|
|
26
25
|
};
|
|
27
26
|
|
|
28
|
-
// When
|
|
29
27
|
const { getByText } = render(<Dependencies packageMeta={packageMeta} />);
|
|
30
28
|
|
|
31
|
-
// Then
|
|
32
29
|
expect(getByText('dependencies.has-no-dependencies')).toBeDefined();
|
|
33
30
|
});
|
|
34
31
|
|
|
35
|
-
test('
|
|
36
|
-
// Given
|
|
32
|
+
test('renders a link to each dependency', () => {
|
|
37
33
|
const packageMeta = {
|
|
38
34
|
latest: {
|
|
39
35
|
name: 'verdaccio',
|
|
@@ -59,18 +55,12 @@ describe('<Dependencies /> component', () => {
|
|
|
59
55
|
_uplinks: {},
|
|
60
56
|
};
|
|
61
57
|
|
|
62
|
-
// When
|
|
63
58
|
const { getByText } = render(
|
|
64
59
|
<HashRouter>
|
|
65
60
|
<Dependencies packageMeta={packageMeta} />
|
|
66
61
|
</HashRouter>
|
|
67
62
|
);
|
|
68
63
|
|
|
69
|
-
// Then
|
|
70
|
-
// FIXME: currently MaterialUI chips do not support the children
|
|
71
|
-
// prop, therefore it is impossible to use proper links for
|
|
72
|
-
// dependencies. Those are for now clickable spans
|
|
73
|
-
|
|
74
64
|
expect(getByText('dependencies (2)')).toBeDefined();
|
|
75
65
|
expect(getByText('devDependencies (1)')).toBeDefined();
|
|
76
66
|
expect(getByText('peerDependencies (1)')).toBeDefined();
|
|
@@ -1,63 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import styled from '@emotion/styled';
|
|
2
|
+
import Card from '@mui/material/Card';
|
|
2
3
|
import CardContent from '@mui/material/CardContent';
|
|
3
|
-
import { useTheme } from '@mui/styles';
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
|
-
import { useHistory } from 'react-router-dom';
|
|
7
6
|
|
|
8
|
-
import {
|
|
7
|
+
import { Theme } from '../../Theme';
|
|
9
8
|
import NoItems from '../NoItems';
|
|
10
|
-
import {
|
|
9
|
+
import { DependencyBlock } from './DependencyBlock';
|
|
10
|
+
import { hasKeys } from './utits';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const DependencyBlock: React.FC<DependencyBlockProps> = ({ title, dependencies }) => {
|
|
18
|
-
const history = useHistory();
|
|
19
|
-
const { t } = useTranslation();
|
|
20
|
-
const theme = useTheme();
|
|
21
|
-
|
|
22
|
-
const deps = Object.entries(dependencies);
|
|
23
|
-
|
|
24
|
-
function handleClick(name: string): void {
|
|
25
|
-
history.push(`/-/web/detail/${name}`);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return (
|
|
29
|
-
<Box data-testid={title} sx={{ margin: theme.spacing(2) }}>
|
|
30
|
-
<StyledText sx={{ marginBottom: theme.spacing(1) }} variant="subtitle1">
|
|
31
|
-
{`${title} (${deps.length})`}
|
|
32
|
-
</StyledText>
|
|
33
|
-
<Tags>
|
|
34
|
-
{deps.map(([name, version]) => (
|
|
35
|
-
<Tag
|
|
36
|
-
className={'dep-tag'}
|
|
37
|
-
clickable={true}
|
|
38
|
-
data-testid={name}
|
|
39
|
-
key={name}
|
|
40
|
-
label={t('dependencies.dependency-block', { package: name, version })}
|
|
41
|
-
// eslint-disable-next-line
|
|
42
|
-
onClick={() => handleClick(name)}
|
|
43
|
-
/>
|
|
44
|
-
))}
|
|
45
|
-
</Tags>
|
|
46
|
-
</Box>
|
|
47
|
-
);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
function hasKeys(object?: { [key: string]: any }): boolean {
|
|
51
|
-
return !!object && Object.keys(object).length > 0;
|
|
52
|
-
}
|
|
12
|
+
export const CardWrap = styled(Card)<{ theme?: Theme }>((props) => ({
|
|
13
|
+
marginBottom: props.theme.spacing(2),
|
|
14
|
+
}));
|
|
53
15
|
|
|
54
16
|
const Dependencies: React.FC<{ packageMeta: any }> = ({ packageMeta }) => {
|
|
55
17
|
const { t } = useTranslation();
|
|
56
18
|
|
|
57
|
-
if (!packageMeta) {
|
|
58
|
-
throw new Error(t('error.package-meta-is-required-at-detail-context'));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
19
|
const { latest } = packageMeta;
|
|
62
20
|
// FIXME: add dependencies to package meta type
|
|
63
21
|
// @ts-ignore
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import userEvent from '@testing-library/user-event';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { MemoryRouter, Route } from 'react-router';
|
|
4
|
+
|
|
5
|
+
import { render, screen } from '../../test/test-react-testing-library';
|
|
6
|
+
import { DependencyBlock } from './DependencyBlock';
|
|
7
|
+
|
|
8
|
+
describe('<DependencyBlock /> component', () => {
|
|
9
|
+
test('renders dependency block', () => {
|
|
10
|
+
render(<DependencyBlock dependencies={{ jquery: '1.0.0' }} title="foo" />);
|
|
11
|
+
|
|
12
|
+
expect(screen.getByText('foo (1)')).toBeInTheDocument();
|
|
13
|
+
expect(screen.getByText('dependencies.dependency-block')).toBeInTheDocument();
|
|
14
|
+
|
|
15
|
+
userEvent.click(screen.getByText('dependencies.dependency-block'));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test.todo('test the click event');
|
|
19
|
+
test.skip('handle change route handler', () => {
|
|
20
|
+
render(
|
|
21
|
+
<MemoryRouter
|
|
22
|
+
initialEntries={[`/-/web/detail/some-dep`, `/-/web/detail/jquery`]}
|
|
23
|
+
initialIndex={0}
|
|
24
|
+
>
|
|
25
|
+
<Route exact={true} path="/-/web/detail/:package">
|
|
26
|
+
<DependencyBlock dependencies={{ jquery: '1.0.0' }} title="foo" />
|
|
27
|
+
</Route>
|
|
28
|
+
</MemoryRouter>
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
userEvent.click(screen.getByTestId('jquery'));
|
|
32
|
+
});
|
|
33
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import styled from '@emotion/styled';
|
|
2
|
+
import Box from '@mui/material/Box';
|
|
3
|
+
import Chip from '@mui/material/Chip';
|
|
4
|
+
import Typography from '@mui/material/Typography';
|
|
5
|
+
import { useTheme } from '@mui/styles';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
import { useHistory } from 'react-router-dom';
|
|
9
|
+
|
|
10
|
+
import { Theme } from '../../Theme';
|
|
11
|
+
import { PackageDependencies } from '../../types/packageMeta';
|
|
12
|
+
|
|
13
|
+
interface DependencyBlockProps {
|
|
14
|
+
title: string;
|
|
15
|
+
dependencies: PackageDependencies;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const StyledText = styled(Typography)<{ theme?: Theme }>((props) => ({
|
|
19
|
+
fontWeight: props.theme && props.theme.fontWeight.bold,
|
|
20
|
+
textTransform: 'capitalize',
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
export const Tags = styled('div')<{ theme?: Theme }>((props) => ({
|
|
24
|
+
display: 'flex',
|
|
25
|
+
justifyContent: 'start',
|
|
26
|
+
flexWrap: 'wrap',
|
|
27
|
+
// force title to be on the same line as the title
|
|
28
|
+
// could be better to avoid margin on the first element
|
|
29
|
+
// but it is a bit tricky to do with flexbox
|
|
30
|
+
marginLeft: props.theme.spacing(-0.6),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
export const Tag = styled(Chip)<{ theme?: Theme }>((props) => ({
|
|
34
|
+
margin: props.theme.spacing(0.6),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
export const DependencyBlock: React.FC<DependencyBlockProps> = ({ title, dependencies }) => {
|
|
38
|
+
const history = useHistory();
|
|
39
|
+
const { t } = useTranslation();
|
|
40
|
+
const theme = useTheme();
|
|
41
|
+
const deps = Object.entries(dependencies);
|
|
42
|
+
|
|
43
|
+
function handleClick(name: string): void {
|
|
44
|
+
history.push(`/-/web/detail/${name}`);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<Box data-testid={title} sx={{ margin: theme.spacing(2) }}>
|
|
49
|
+
<StyledText sx={{ marginBottom: theme.spacing(1) }} variant="subtitle1">
|
|
50
|
+
{`${title} (${deps.length})`}
|
|
51
|
+
</StyledText>
|
|
52
|
+
<Tags>
|
|
53
|
+
{deps.map(([name, version]) => (
|
|
54
|
+
<Tag
|
|
55
|
+
className={'dep-tag'}
|
|
56
|
+
clickable={true}
|
|
57
|
+
data-testid={name}
|
|
58
|
+
key={name}
|
|
59
|
+
label={t('dependencies.dependency-block', { package: name, version })}
|
|
60
|
+
// eslint-disable-next-line
|
|
61
|
+
onClick={() => {
|
|
62
|
+
handleClick(name);
|
|
63
|
+
}}
|
|
64
|
+
/>
|
|
65
|
+
))}
|
|
66
|
+
</Tags>
|
|
67
|
+
</Box>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { MemoryRouter } from 'react-router';
|
|
3
|
+
|
|
4
|
+
import { fireEvent, render, screen } from '../../test/test-react-testing-library';
|
|
5
|
+
import Forbidden from './Forbidden';
|
|
6
|
+
|
|
7
|
+
const mockHistory = jest.fn();
|
|
8
|
+
|
|
9
|
+
jest.mock('react-router-dom', () => ({
|
|
10
|
+
useHistory: () => ({
|
|
11
|
+
push: mockHistory,
|
|
12
|
+
}),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
describe('<Forbidden /> component', () => {
|
|
16
|
+
test('should load the component in default state', () => {
|
|
17
|
+
render(
|
|
18
|
+
<MemoryRouter>
|
|
19
|
+
<Forbidden />
|
|
20
|
+
</MemoryRouter>
|
|
21
|
+
);
|
|
22
|
+
expect(screen.getByTestId('LockIcon')).toBeInTheDocument();
|
|
23
|
+
expect(screen.getByText('error.401.sorry-no-access')).toBeInTheDocument();
|
|
24
|
+
expect(screen.getByText('button.go-to-the-home-page')).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('go to Home Page button click', async () => {
|
|
28
|
+
const { getByTestId } = render(
|
|
29
|
+
<MemoryRouter>
|
|
30
|
+
<Forbidden />
|
|
31
|
+
</MemoryRouter>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const node = getByTestId('not-found-go-to-home-button');
|
|
35
|
+
fireEvent.click(node);
|
|
36
|
+
expect(mockHistory).toHaveBeenCalled();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/* eslint-disable react/forbid-component-props */
|
|
2
|
+
|
|
3
|
+
/* eslint-disable verdaccio/jsx-no-style */
|
|
4
|
+
import styled from '@emotion/styled';
|
|
5
|
+
import LockIcon from '@mui/icons-material/Lock';
|
|
6
|
+
import Box from '@mui/material/Box';
|
|
7
|
+
import Button from '@mui/material/Button';
|
|
8
|
+
import React, { useCallback } from 'react';
|
|
9
|
+
import { useTranslation } from 'react-i18next';
|
|
10
|
+
import { useHistory } from 'react-router-dom';
|
|
11
|
+
|
|
12
|
+
import { Theme } from '../..';
|
|
13
|
+
import Heading from '../Heading';
|
|
14
|
+
|
|
15
|
+
const Foebidden: React.FC = () => {
|
|
16
|
+
const history = useHistory();
|
|
17
|
+
|
|
18
|
+
const { t } = useTranslation();
|
|
19
|
+
|
|
20
|
+
const handleGoHome = useCallback(() => {
|
|
21
|
+
history.push('/');
|
|
22
|
+
}, [history]);
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<Box
|
|
26
|
+
alignItems="center"
|
|
27
|
+
data-testid="404"
|
|
28
|
+
display="flex"
|
|
29
|
+
flexDirection="column"
|
|
30
|
+
flexGrow={1}
|
|
31
|
+
justifyContent="center"
|
|
32
|
+
p={2}
|
|
33
|
+
>
|
|
34
|
+
<Container>
|
|
35
|
+
<LockIcon color="primary" style={{ fontSize: 236 }} />
|
|
36
|
+
</Container>
|
|
37
|
+
<StyledHeading className="not-found-text" variant="h4">
|
|
38
|
+
{t('error.401.sorry-no-access')}
|
|
39
|
+
</StyledHeading>
|
|
40
|
+
<Button data-testid="not-found-go-to-home-button" onClick={handleGoHome} variant="contained">
|
|
41
|
+
{t('button.go-to-the-home-page')}
|
|
42
|
+
</Button>
|
|
43
|
+
</Box>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default Foebidden;
|
|
48
|
+
|
|
49
|
+
const Container = styled('div')({
|
|
50
|
+
margin: '0 auto',
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const StyledHeading = styled(Heading)<{ theme?: Theme }>(({ theme }) => ({
|
|
54
|
+
color: theme?.palette.mode === 'light' ? theme?.palette.primary.main : theme?.palette.white,
|
|
55
|
+
marginBottom: 16,
|
|
56
|
+
}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Forbidden';
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
-
import { render } from '../../test/test-react-testing-library';
|
|
3
|
+
import { render, screen } from '../../test/test-react-testing-library';
|
|
4
4
|
import Loading from './Loading';
|
|
5
5
|
|
|
6
6
|
describe('<Loading /> component', () => {
|
|
7
7
|
test('should render the component in default state', () => {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
render(<Loading />);
|
|
9
|
+
screen.debug();
|
|
10
|
+
expect(screen.getByTestId('loading')).toBeInTheDocument();
|
|
10
11
|
});
|
|
11
12
|
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
/* eslint-disable verdaccio/jsx-no-style */
|
|
1
2
|
import styled from '@emotion/styled';
|
|
3
|
+
import FolderOffIcon from '@mui/icons-material/FolderOff';
|
|
2
4
|
import Box from '@mui/material/Box';
|
|
3
5
|
import Button from '@mui/material/Button';
|
|
4
6
|
import React, { useCallback } from 'react';
|
|
@@ -7,8 +9,6 @@ import { useHistory } from 'react-router-dom';
|
|
|
7
9
|
|
|
8
10
|
import { Theme } from '../../';
|
|
9
11
|
import Heading from '../Heading';
|
|
10
|
-
// @ts-ignore
|
|
11
|
-
import PackageImg from './img/package.svg';
|
|
12
12
|
|
|
13
13
|
const NotFound: React.FC = () => {
|
|
14
14
|
const history = useHistory();
|
|
@@ -28,7 +28,9 @@ const NotFound: React.FC = () => {
|
|
|
28
28
|
justifyContent="center"
|
|
29
29
|
p={2}
|
|
30
30
|
>
|
|
31
|
-
<
|
|
31
|
+
<Container>
|
|
32
|
+
<FolderOffIcon color="primary" style={{ fontSize: 236 }} />
|
|
33
|
+
</Container>
|
|
32
34
|
<StyledHeading className="not-found-text" variant="h4">
|
|
33
35
|
{t('error.404.sorry-we-could-not-find-it')}
|
|
34
36
|
</StyledHeading>
|
|
@@ -41,8 +43,7 @@ const NotFound: React.FC = () => {
|
|
|
41
43
|
|
|
42
44
|
export default NotFound;
|
|
43
45
|
|
|
44
|
-
const
|
|
45
|
-
width: '150px',
|
|
46
|
+
const Container = styled('div')({
|
|
46
47
|
margin: '0 auto',
|
|
47
48
|
});
|
|
48
49
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { MemoryRouter } from 'react-router';
|
|
3
3
|
|
|
4
|
-
import { fireEvent, render } from '../../test/test-react-testing-library';
|
|
4
|
+
import { fireEvent, render, screen } from '../../test/test-react-testing-library';
|
|
5
5
|
import NotFound from './NotFound';
|
|
6
6
|
|
|
7
7
|
const mockHistory = jest.fn();
|
|
@@ -14,12 +14,15 @@ jest.mock('react-router-dom', () => ({
|
|
|
14
14
|
|
|
15
15
|
describe('<NotFound /> component', () => {
|
|
16
16
|
test('should load the component in default state', () => {
|
|
17
|
-
|
|
17
|
+
render(
|
|
18
18
|
<MemoryRouter>
|
|
19
19
|
<NotFound />
|
|
20
20
|
</MemoryRouter>
|
|
21
21
|
);
|
|
22
|
-
|
|
22
|
+
|
|
23
|
+
expect(screen.getByTestId('FolderOffIcon')).toBeInTheDocument();
|
|
24
|
+
expect(screen.getByText('button.go-to-the-home-page')).toBeInTheDocument();
|
|
25
|
+
expect(screen.getByText('error.404.sorry-we-could-not-find-it')).toBeInTheDocument();
|
|
23
26
|
});
|
|
24
27
|
|
|
25
28
|
test('go to Home Page button click', async () => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './SideBarTitle';
|
package/src/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ export * as Icons from './components/Icons';
|
|
|
14
14
|
export { default as Install } from './components/Install';
|
|
15
15
|
export { default as RawViewer } from './components/RawViewer';
|
|
16
16
|
export { default as Readme } from './components/Readme';
|
|
17
|
-
export { default as SideBarTitle } from './components/
|
|
17
|
+
export { default as SideBarTitle } from './components/SideBarTitle';
|
|
18
18
|
export { default as UpLinks } from './components/UpLinks';
|
|
19
19
|
export { default as Versions } from './components/Versions';
|
|
20
20
|
export { default as TextField } from './components/TextField';
|
|
@@ -22,6 +22,7 @@ export { default as Label } from './components/Label';
|
|
|
22
22
|
export { default as Logo } from './components/Logo';
|
|
23
23
|
export { default as MenuItem } from './components/MenuItem';
|
|
24
24
|
export { default as NotFound } from './components/NotFound';
|
|
25
|
+
export { default as Forbidden } from './components/Forbidden';
|
|
25
26
|
export { default as LoginDialog } from './components/LoginDialog';
|
|
26
27
|
export { default as Search } from './components/Search';
|
|
27
28
|
export { default as HeaderInfoDialog } from './components/HeaderInfoDialog';
|
|
@@ -27,3 +27,13 @@ export const DetailJquery: any = () => (
|
|
|
27
27
|
</Route>
|
|
28
28
|
</MemoryRouter>
|
|
29
29
|
);
|
|
30
|
+
|
|
31
|
+
export const DetailForbidden: any = () => (
|
|
32
|
+
<MemoryRouter initialEntries={[`/-/web/detail/JSONStream`]}>
|
|
33
|
+
<Route exact={true} path="/-/web/detail/:package">
|
|
34
|
+
<VersionProvider>
|
|
35
|
+
<Detail />
|
|
36
|
+
</VersionProvider>
|
|
37
|
+
</Route>
|
|
38
|
+
</MemoryRouter>
|
|
39
|
+
);
|