forked from sombochea/verdaccio-ui
Compare commits
51 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6aad5370f | ||
|
|
d481f54948 | ||
|
|
e6e9cfb2b4 | ||
|
|
6570e3fba1 | ||
|
|
d4f2720994 | ||
|
|
1eca1f4079 | ||
|
|
164cea6c10 | ||
|
|
dad44c46c0 | ||
|
|
222ffed022 | ||
|
|
ee1c3f08eb | ||
|
|
1531cb6226 | ||
|
|
e514ec95a6 | ||
|
|
6b322ad553 | ||
|
|
3fd0154da3 | ||
|
|
6bd38b8120 | ||
|
|
6e2bface93 | ||
|
|
6eeae630ef | ||
|
|
eaea5f2501 | ||
|
|
c1e4e739c8 | ||
|
|
4d31aff4a4 | ||
|
|
d9a9fc4b96 | ||
|
|
e4ecc4a2f9 | ||
|
|
bae9638b23 | ||
|
|
544b999f81 | ||
|
|
d554049699 | ||
|
|
787dda4a01 | ||
|
|
797c2381e4 | ||
|
|
3888a268e4 | ||
|
|
884d76d4a9 | ||
|
|
7b55ce5ea2 | ||
|
|
1ec62de0bf | ||
|
|
3fc5c38a8e | ||
|
|
53e1e63b12 | ||
|
|
ef2b50a329 | ||
|
|
61dc9b0783 | ||
|
|
0e3391ca3d | ||
|
|
277b44ab94 | ||
|
|
71276e15ef | ||
|
|
bf093cc27b | ||
|
|
3a9f66c023 | ||
|
|
eef2913dd5 | ||
|
|
2af2dfe91b | ||
|
|
d4e8dff40f | ||
|
|
cc22574100 | ||
|
|
580b47bea1 | ||
|
|
ea24e1b50e | ||
|
|
a74dc87628 | ||
|
|
cecb54c490 | ||
|
|
b8c68314e9 | ||
|
|
6bb37d6656 | ||
|
|
ac1a4fa46d |
@@ -136,7 +136,7 @@ jobs:
|
|||||||
command: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
|
command: echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc
|
||||||
- run:
|
- run:
|
||||||
name: Publish
|
name: Publish
|
||||||
command: yarn publish
|
command: npm publish
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
version: 2
|
version: 2
|
||||||
|
|||||||
11
.github/workflows/security.yml
vendored
Normal file
11
.github/workflows/security.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
name: Security Flow
|
||||||
|
on: push
|
||||||
|
jobs:
|
||||||
|
security:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Run Snyk to check for vulnerabilities
|
||||||
|
uses: snyk/actions/node@0.1.0
|
||||||
|
env:
|
||||||
|
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||||
0
.sonarcloud.properties
Normal file
0
.sonarcloud.properties
Normal file
44
CHANGELOG.md
44
CHANGELOG.md
@@ -2,6 +2,50 @@
|
|||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.3.13](https://github.com/verdaccio/ui/compare/v0.3.12...v0.3.13) (2020-02-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* do not capitalize heading - closes [#428](https://github.com/verdaccio/ui/issues/428) ([#431](https://github.com/verdaccio/ui/issues/431)) ([d481f54](https://github.com/verdaccio/ui/commit/d481f549484361c1d1bc011e0858e8f99b8a2528))
|
||||||
|
* package list refresh based on logged-in user ([#415](https://github.com/verdaccio/ui/issues/415)) ([222ffed](https://github.com/verdaccio/ui/commit/222ffed0226f5aaa62f2d5b91bb08717b2aa24ef)), closes [#414](https://github.com/verdaccio/ui/issues/414) [#414](https://github.com/verdaccio/ui/issues/414)
|
||||||
|
* reload packages on log in ([#421](https://github.com/verdaccio/ui/issues/421)) ([1eca1f4](https://github.com/verdaccio/ui/commit/1eca1f40797790e87d9592204ca061527d09c4ae))
|
||||||
|
* typo ([#423](https://github.com/verdaccio/ui/issues/423)) ([164cea6](https://github.com/verdaccio/ui/commit/164cea6c10804c1d2097c2a582eb3e1e51814d4a))
|
||||||
|
* update dependencies ([#420](https://github.com/verdaccio/ui/issues/420)) ([ee1c3f0](https://github.com/verdaccio/ui/commit/ee1c3f08eb16da2313d8841cfab18358d7f4ea10))
|
||||||
|
|
||||||
|
### [0.3.12](https://github.com/verdaccio/ui/compare/v0.3.11...v0.3.12) (2020-01-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* generate correct registry URL ([#413](https://github.com/verdaccio/ui/issues/413)) ([6b322ad](https://github.com/verdaccio/ui/commit/6b322ad553e9fb3ee65b2968dcfe856ba42a0bfb)), closes [#300](https://github.com/verdaccio/ui/issues/300) [#311](https://github.com/verdaccio/ui/issues/311)
|
||||||
|
|
||||||
|
### [0.3.11](https://github.com/verdaccio/ui/compare/v0.3.10...v0.3.11) (2020-01-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* remove prevent default and use react context ([#411](https://github.com/verdaccio/ui/issues/411)) ([6bd38b8](https://github.com/verdaccio/ui/commit/6bd38b812032857bb19af8978d48f6f8969af6cf))
|
||||||
|
* removed unused style file ([#406](https://github.com/verdaccio/ui/issues/406)) ([6eeae63](https://github.com/verdaccio/ui/commit/6eeae630ef441a871d06b888b6a21178e36e0db7))
|
||||||
|
|
||||||
|
### [0.3.10](https://github.com/verdaccio/ui/compare/v0.3.9...v0.3.10) (2019-12-30)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* added "Fund this package" button ([#375](https://github.com/verdaccio/ui/issues/375)) ([bf093cc](https://github.com/verdaccio/ui/commit/bf093cc27b8625cdc50dbfc9b8dd7e37f4e24da9))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* add missing trailing slash to publicPath - closes [#395](https://github.com/verdaccio/ui/issues/395) ([#396](https://github.com/verdaccio/ui/issues/396)) ([bae9638](https://github.com/verdaccio/ui/commit/bae9638b23b70eff78b78b8ca52ff40162333354))
|
||||||
|
* engine warning on console for ui ([#403](https://github.com/verdaccio/ui/issues/403)) ([d554049](https://github.com/verdaccio/ui/commit/d554049699494e946f4caf345177839b4f0cba8b))
|
||||||
|
* remove background from styled Avatar components - closes [#371](https://github.com/verdaccio/ui/issues/371) ([#398](https://github.com/verdaccio/ui/issues/398)) ([787dda4](https://github.com/verdaccio/ui/commit/787dda4a016a1fcd1142bd4b705e2c71e232d13e))
|
||||||
|
* remove double padding and add missing background color - closes [#373](https://github.com/verdaccio/ui/issues/373) ([#399](https://github.com/verdaccio/ui/issues/399)) ([797c238](https://github.com/verdaccio/ui/commit/797c2381e453d4f40e1703402f192eb7675d6fbe))
|
||||||
|
* remove whitespace from logo image - closes [#374](https://github.com/verdaccio/ui/issues/374) ([#400](https://github.com/verdaccio/ui/issues/400)) ([544b999](https://github.com/verdaccio/ui/commit/544b999f81e39557e0fc002d21b24c512cfebc54))
|
||||||
|
|
||||||
|
### [0.3.9](https://github.com/verdaccio/ui/compare/v0.3.8...v0.3.9) (2019-12-14)
|
||||||
|
|
||||||
### [0.3.8](https://github.com/verdaccio/ui/compare/v0.3.7...v0.3.8) (2019-12-14)
|
### [0.3.8](https://github.com/verdaccio/ui/compare/v0.3.7...v0.3.8) (2019-12-14)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
[](https://stackshare.io/verdaccio)
|
[](https://stackshare.io/verdaccio)
|
||||||
[](http://chat.verdaccio.org/)
|
[](http://chat.verdaccio.org/)
|
||||||
[](https://www.npmjs.com/package/@verdaccio/ui-theme)
|
[](https://www.npmjs.com/package/@verdaccio/ui-theme)
|
||||||

|
[](./LICENSE)
|
||||||
[](https://crowdin.com/project/verdaccio)
|
[](https://crowdin.com/project/verdaccio)
|
||||||
[](https://codecov.io/gh/verdaccio/ui)
|
[](https://codecov.io/gh/verdaccio/ui)
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
We use `>=yarn@1.13.0`, keep on mind we use lock file.
|
We use `>=yarn@1.13.0`, keep in mind that we use lockfiles and use at least Node `v10.13.0` to be able to build the project.
|
||||||
|
|
||||||
For development run the following command, it will execute `webpack` and `verdaccio` to
|
For development run the following command, it will execute `webpack` and `verdaccio` to
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
require.requireActual('babel/polyfill');
|
jest.requireActual('babel/polyfill');
|
||||||
|
|||||||
@@ -8,14 +8,13 @@ import Adapter from 'enzyme-adapter-react-16';
|
|||||||
import { GlobalWithFetchMock } from 'jest-fetch-mock';
|
import { GlobalWithFetchMock } from 'jest-fetch-mock';
|
||||||
import 'mutationobserver-shim';
|
import 'mutationobserver-shim';
|
||||||
|
|
||||||
// @ts-ignore : Only a void function can be called with the 'new' keyword
|
|
||||||
configure({ adapter: new Adapter() });
|
configure({ adapter: new Adapter() });
|
||||||
|
|
||||||
// @ts-ignore : Property '__APP_VERSION__' does not exist on type 'Global'.
|
// @ts-ignore : Property '__APP_VERSION__' does not exist on type 'Global'.
|
||||||
global.__APP_VERSION__ = '1.0.0';
|
global.__APP_VERSION__ = '1.0.0';
|
||||||
// @ts-ignore : Property '__VERDACCIO_BASENAME_UI_OPTIONS' does not exist on type 'Global'.
|
// @ts-ignore : Property '__VERDACCIO_BASENAME_UI_OPTIONS' does not exist on type 'Global'.
|
||||||
global.__VERDACCIO_BASENAME_UI_OPTIONS = {};
|
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_API_URL = 'https://verdaccio.tld';
|
||||||
|
|
||||||
const customGlobal: GlobalWithFetchMock = global as GlobalWithFetchMock;
|
const customGlobal: GlobalWithFetchMock = global as GlobalWithFetchMock;
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ export const packageMeta = {
|
|||||||
jest: { snapshotSerializers: ['jest-serializer-enzyme'] },
|
jest: { snapshotSerializers: ['jest-serializer-enzyme'] },
|
||||||
engines: { node: '>=4.6.1', npm: '>=2.15.9' },
|
engines: { node: '>=4.6.1', npm: '>=2.15.9' },
|
||||||
preferGlobal: true,
|
preferGlobal: true,
|
||||||
publishConfig: { registry: 'http://localhost:4873/' },
|
publishConfig: { registry: 'https://registry.verdaccio.org' },
|
||||||
license: 'WTFPL',
|
license: 'WTFPL',
|
||||||
contributors: [
|
contributors: [
|
||||||
{
|
{
|
||||||
@@ -578,7 +578,7 @@ export const packageMeta = {
|
|||||||
_npmUser: {},
|
_npmUser: {},
|
||||||
dist: {
|
dist: {
|
||||||
shasum: '958c919180e7f2ed6775f48d4ec64bd8de2a14df',
|
shasum: '958c919180e7f2ed6775f48d4ec64bd8de2a14df',
|
||||||
tarball: 'http://localhost:4873/verdaccio/-/verdaccio-2.7.1.tgz',
|
tarball: 'https://registry.verdaccio.org/verdaccio/-/verdaccio-2.7.1.tgz',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
81
package.json
81
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@verdaccio/ui-theme",
|
"name": "@verdaccio/ui-theme",
|
||||||
"version": "0.3.8",
|
"version": "0.3.13",
|
||||||
"description": "Verdaccio User Interface",
|
"description": "Verdaccio User Interface",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Verdaccio Core Team",
|
"name": "Verdaccio Core Team",
|
||||||
@@ -13,55 +13,55 @@
|
|||||||
"homepage": "https://verdaccio.org",
|
"homepage": "https://verdaccio.org",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.7.4",
|
"@babel/plugin-proposal-nullish-coalescing-operator": "7.8.0",
|
||||||
"@babel/plugin-proposal-optional-chaining": "7.7.5",
|
"@babel/plugin-proposal-optional-chaining": "7.8.0",
|
||||||
"@commitlint/cli": "8.2.0",
|
"@commitlint/cli": "8.3.4",
|
||||||
"@commitlint/config-conventional": "8.2.0",
|
"@commitlint/config-conventional": "8.3.4",
|
||||||
"@emotion/core": "10.0.22",
|
"@emotion/core": "10.0.22",
|
||||||
"@emotion/styled": "10.0.23",
|
"@emotion/styled": "10.0.23",
|
||||||
"@material-ui/core": "4.7.2",
|
"@material-ui/core": "4.8.0",
|
||||||
"@material-ui/icons": "4.5.1",
|
"@material-ui/icons": "4.5.1",
|
||||||
"@octokit/rest": "16.35.0",
|
"@octokit/rest": "16.35.2",
|
||||||
"@testing-library/jest-dom": "4.2.4",
|
"@testing-library/jest-dom": "4.2.4",
|
||||||
"@testing-library/react": "9.3.3",
|
"@testing-library/react": "9.4.0",
|
||||||
"@types/autosuggest-highlight": "3.1.0",
|
"@types/autosuggest-highlight": "3.1.0",
|
||||||
"@types/enzyme": "3.10.4",
|
"@types/enzyme": "3.10.4",
|
||||||
"@types/jest": "24.0.23",
|
"@types/jest": "24.0.24",
|
||||||
"@types/js-base64": "2.3.1",
|
"@types/js-base64": "2.3.1",
|
||||||
"@types/lodash": "4.14.149",
|
"@types/lodash": "4.14.149",
|
||||||
"@types/node": "12.12.17",
|
"@types/node": "13.1.6",
|
||||||
"@types/react": "16.9.16",
|
"@types/react": "16.9.17",
|
||||||
"@types/react-autosuggest": "9.3.13",
|
"@types/react-autosuggest": "9.3.13",
|
||||||
"@types/react-dom": "16.9.4",
|
"@types/react-dom": "16.9.4",
|
||||||
"@types/react-router-dom": "5.1.3",
|
"@types/react-router-dom": "5.1.3",
|
||||||
"@types/request": "2.48.3",
|
"@types/request": "2.48.4",
|
||||||
"@types/validator": "12.0.1",
|
"@types/validator": "12.0.1",
|
||||||
"@types/webpack-env": "1.14.1",
|
"@types/webpack-env": "1.15.0",
|
||||||
"@typescript-eslint/parser": "2.11.0",
|
"@typescript-eslint/parser": "2.18.0",
|
||||||
"@verdaccio/babel-preset": "8.4.2",
|
"@verdaccio/babel-preset": "9.0.0",
|
||||||
"@verdaccio/commons-api": "8.4.2",
|
"@verdaccio/commons-api": "9.0.0",
|
||||||
"@verdaccio/eslint-config": "8.4.2",
|
"@verdaccio/eslint-config": "8.4.2",
|
||||||
"@verdaccio/types": "8.4.2",
|
"@verdaccio/types": "9.0.0",
|
||||||
"autosuggest-highlight": "3.1.1",
|
"autosuggest-highlight": "3.1.1",
|
||||||
"babel-loader": "8.0.6",
|
"babel-loader": "8.0.6",
|
||||||
"bundlesize": "0.18.0",
|
"bundlesize": "0.18.0",
|
||||||
"codeceptjs": "2.3.6",
|
"codeceptjs": "2.4.0",
|
||||||
"codecov": "3.6.1",
|
"codecov": "3.6.1",
|
||||||
"concurrently": "5.0.1",
|
"concurrently": "5.0.2",
|
||||||
"cross-env": "6.0.3",
|
"cross-env": "6.0.3",
|
||||||
"css-loader": "3.3.0",
|
"css-loader": "3.4.2",
|
||||||
"dayjs": "1.8.17",
|
"dayjs": "1.8.19",
|
||||||
"detect-secrets": "1.0.5",
|
"detect-secrets": "1.0.5",
|
||||||
"emotion": "10.0.23",
|
"emotion": "10.0.27",
|
||||||
"emotion-theming": "10.0.19",
|
"emotion-theming": "10.0.27",
|
||||||
"enzyme": "3.10.0",
|
"enzyme": "3.11.0",
|
||||||
"enzyme-adapter-react-16": "1.15.1",
|
"enzyme-adapter-react-16": "1.15.2",
|
||||||
"enzyme-to-json": "3.4.3",
|
"enzyme-to-json": "3.4.3",
|
||||||
"eslint": "6.7.2",
|
"eslint": "6.7.2",
|
||||||
"eslint-plugin-codeceptjs": "1.2.0",
|
"eslint-plugin-codeceptjs": "1.2.0",
|
||||||
"eslint-plugin-import": "2.19.1",
|
"eslint-plugin-import": "2.19.1",
|
||||||
"eslint-plugin-jsx-a11y": "6.2.3",
|
"eslint-plugin-jsx-a11y": "6.2.3",
|
||||||
"eslint-plugin-prettier": "3.1.1",
|
"eslint-plugin-prettier": "3.1.2",
|
||||||
"eslint-plugin-react": "7.17.0",
|
"eslint-plugin-react": "7.17.0",
|
||||||
"eslint-plugin-react-hooks": "2.3.0",
|
"eslint-plugin-react-hooks": "2.3.0",
|
||||||
"eslint-plugin-verdaccio": "8.4.2",
|
"eslint-plugin-verdaccio": "8.4.2",
|
||||||
@@ -74,20 +74,20 @@
|
|||||||
"identity-obj-proxy": "3.0.0",
|
"identity-obj-proxy": "3.0.0",
|
||||||
"in-publish": "2.0.0",
|
"in-publish": "2.0.0",
|
||||||
"jest": "24.9.0",
|
"jest": "24.9.0",
|
||||||
"jest-emotion": "10.0.26",
|
"jest-emotion": "10.0.27",
|
||||||
"jest-environment-jsdom": "24.9.0",
|
"jest-environment-jsdom": "24.9.0",
|
||||||
"jest-environment-jsdom-global": "1.2.0",
|
"jest-environment-jsdom-global": "1.2.0",
|
||||||
"jest-environment-node": "24.9.0",
|
"jest-environment-node": "24.9.0",
|
||||||
"jest-fetch-mock": "2.1.2",
|
"jest-fetch-mock": "3.0.1",
|
||||||
"js-base64": "2.5.1",
|
"js-base64": "2.5.1",
|
||||||
"js-yaml": "3.13.1",
|
"js-yaml": "3.13.1",
|
||||||
"lint-staged": "9.5.0",
|
"lint-staged": "9.5.0",
|
||||||
"localstorage-memory": "1.0.3",
|
"localstorage-memory": "1.0.3",
|
||||||
"lockfile-lint": "3.0.3",
|
"lockfile-lint": "3.0.5",
|
||||||
"lodash": "^4.17.15",
|
"lodash": "^4.17.15",
|
||||||
"mini-css-extract-plugin": "0.8.0",
|
"mini-css-extract-plugin": "0.9.0",
|
||||||
"mutationobserver-shim": "0.3.3",
|
"mutationobserver-shim": "0.3.3",
|
||||||
"node-mocks-http": "1.8.0",
|
"node-mocks-http": "1.8.1",
|
||||||
"normalize.css": "8.0.1",
|
"normalize.css": "8.0.1",
|
||||||
"optimize-css-assets-webpack-plugin": "5.0.3",
|
"optimize-css-assets-webpack-plugin": "5.0.3",
|
||||||
"ora": "4.0.3",
|
"ora": "4.0.3",
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
"react": "16.12.0",
|
"react": "16.12.0",
|
||||||
"react-autosuggest": "9.4.3",
|
"react-autosuggest": "9.4.3",
|
||||||
"react-dom": "16.12.0",
|
"react-dom": "16.12.0",
|
||||||
"react-hook-form": "3.28.15",
|
"react-hook-form": "3.29.4",
|
||||||
"react-hot-loader": "4.12.18",
|
"react-hot-loader": "4.12.18",
|
||||||
"react-router-dom": "5.1.2",
|
"react-router-dom": "5.1.2",
|
||||||
"request": "2.88.0",
|
"request": "2.88.0",
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
"rimraf": "3.0.0",
|
"rimraf": "3.0.0",
|
||||||
"source-map-loader": "0.2.4",
|
"source-map-loader": "0.2.4",
|
||||||
"standard-version": "7.0.1",
|
"standard-version": "7.0.1",
|
||||||
"style-loader": "1.0.1",
|
"style-loader": "1.1.2",
|
||||||
"stylelint": "12.0.0",
|
"stylelint": "12.0.0",
|
||||||
"stylelint-config-recommended": "3.0.0",
|
"stylelint-config-recommended": "3.0.0",
|
||||||
"stylelint-config-styled-components": "0.1.1",
|
"stylelint-config-styled-components": "0.1.1",
|
||||||
@@ -113,19 +113,19 @@
|
|||||||
"stylelint-webpack-plugin": "1.1.2",
|
"stylelint-webpack-plugin": "1.1.2",
|
||||||
"supertest": "4.0.2",
|
"supertest": "4.0.2",
|
||||||
"typeface-roboto": "0.0.75",
|
"typeface-roboto": "0.0.75",
|
||||||
"typescript": "3.7.3",
|
"typescript": "3.7.4",
|
||||||
"uglifyjs-webpack-plugin": "2.2.0",
|
"uglifyjs-webpack-plugin": "2.2.0",
|
||||||
"url-loader": "3.0.0",
|
"url-loader": "3.0.0",
|
||||||
"validator": "12.1.0",
|
"validator": "12.1.0",
|
||||||
"verdaccio": "4.3.5",
|
"verdaccio": "4.4.2",
|
||||||
"verdaccio-auth-memory": "8.4.2",
|
"verdaccio-auth-memory": "9.0.0",
|
||||||
"verdaccio-memory": "8.4.2",
|
"verdaccio-memory": "9.0.0",
|
||||||
"wait-on": "3.3.0",
|
"wait-on": "3.3.0",
|
||||||
"webpack": "4.41.2",
|
"webpack": "4.41.5",
|
||||||
"webpack-bundle-analyzer": "3.6.0",
|
"webpack-bundle-analyzer": "3.6.0",
|
||||||
"webpack-bundle-size-analyzer": "3.1.0",
|
"webpack-bundle-size-analyzer": "3.1.0",
|
||||||
"webpack-cli": "3.3.10",
|
"webpack-cli": "3.3.10",
|
||||||
"webpack-dev-server": "3.9.0",
|
"webpack-dev-server": "3.10.1",
|
||||||
"webpack-merge": "4.2.2",
|
"webpack-merge": "4.2.2",
|
||||||
"whatwg-fetch": "3.0.0",
|
"whatwg-fetch": "3.0.0",
|
||||||
"xss": "1.0.6"
|
"xss": "1.0.6"
|
||||||
@@ -171,6 +171,7 @@
|
|||||||
"test:acceptance:server": "concurrently --kill-others \"npm run verdaccio:server\" \"npm run test:acceptance\"",
|
"test:acceptance:server": "concurrently --kill-others \"npm run verdaccio:server\" \"npm run test:acceptance\"",
|
||||||
"test:e2e": "cross-env BABEL_ENV=test jest --config ./test/jest.config.e2e.js",
|
"test:e2e": "cross-env BABEL_ENV=test jest --config ./test/jest.config.e2e.js",
|
||||||
"test": "cross-env NODE_ENV=test BABEL_ENV=test TZ=UTC jest --config ./jest/jest.config.js --maxWorkers 2 --passWithNoTests",
|
"test": "cross-env NODE_ENV=test BABEL_ENV=test TZ=UTC jest --config ./jest/jest.config.js --maxWorkers 2 --passWithNoTests",
|
||||||
|
"test:update-snapshot": "npm run test -- -u",
|
||||||
"test:size": "bundlesize",
|
"test:size": "bundlesize",
|
||||||
"lint": "npm run lint:js && npm run lint:css && npm run lint:lockfile",
|
"lint": "npm run lint:js && npm run lint:css && npm run lint:lockfile",
|
||||||
"lint:js": "npm run type-check && eslint . --ext .js,.ts,.tsx",
|
"lint:js": "npm run type-check && eslint . --ext .js,.ts,.tsx",
|
||||||
@@ -186,7 +187,7 @@
|
|||||||
"dev": "concurrently --kill-others \"npm run dev:web\" \"npm run verdaccio:server\""
|
"dev": "concurrently --kill-others \"npm run dev:web\" \"npm run verdaccio:server\""
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 10.13.0",
|
"node": ">= 8",
|
||||||
"npm": ">=5"
|
"npm": ">=5"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
|
|||||||
@@ -5,19 +5,20 @@ import { Router } from 'react-router-dom';
|
|||||||
|
|
||||||
import storage from '../utils/storage';
|
import storage from '../utils/storage';
|
||||||
import { isTokenExpire } from '../utils/login';
|
import { isTokenExpire } from '../utils/login';
|
||||||
import API from '../utils/api';
|
|
||||||
import Header from '../components/Header';
|
import Header from '../components/Header';
|
||||||
import Footer from '../components/Footer';
|
import Footer from '../components/Footer';
|
||||||
import Box from '../muiComponents/Box';
|
import Box from '../muiComponents/Box';
|
||||||
import Loading from '../components/Loading';
|
|
||||||
import StyleBaseline from '../design-tokens/StyleBaseline';
|
import StyleBaseline from '../design-tokens/StyleBaseline';
|
||||||
import { Theme } from '../design-tokens/theme';
|
import { Theme } from '../design-tokens/theme';
|
||||||
|
|
||||||
import AppContextProvider from './AppContextProvider';
|
import AppContextProvider from './AppContextProvider';
|
||||||
import AppRoute, { history } from './AppRoute';
|
import AppRoute, { history } from './AppRoute';
|
||||||
|
|
||||||
|
const StyledBox = styled(Box)<{ theme?: Theme }>(({ theme }) => ({
|
||||||
|
backgroundColor: theme && theme.palette.white,
|
||||||
|
}));
|
||||||
|
|
||||||
const StyledBoxContent = styled(Box)<{ theme?: Theme }>(({ theme }) => ({
|
const StyledBoxContent = styled(Box)<{ theme?: Theme }>(({ theme }) => ({
|
||||||
padding: 15,
|
|
||||||
[`@media screen and (min-width: ${theme && theme.breakPoints.container}px)`]: {
|
[`@media screen and (min-width: ${theme && theme.breakPoints.container}px)`]: {
|
||||||
maxWidth: theme && theme.breakPoints.container,
|
maxWidth: theme && theme.breakPoints.container,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@@ -30,11 +31,9 @@ const StyledBoxContent = styled(Box)<{ theme?: Theme }>(({ theme }) => ({
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
const App: React.FC = () => {
|
const App: React.FC = () => {
|
||||||
const [user, setUser] = useState();
|
const [user, setUser] = useState();
|
||||||
const [packages, setPackages] = useState([]);
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Logouts user
|
* Logout user
|
||||||
* Required by: <Header />
|
* Required by: <Header />
|
||||||
*/
|
*/
|
||||||
const logout = () => {
|
const logout = () => {
|
||||||
@@ -56,47 +55,27 @@ const App: React.FC = () => {
|
|||||||
setUser({ username });
|
setUser({ username });
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadOnHandler = async () => {
|
|
||||||
try {
|
|
||||||
const packages = await API.request('packages', 'GET');
|
|
||||||
// FIXME add correct type for package
|
|
||||||
setPackages(packages as never[]);
|
|
||||||
} catch (error) {
|
|
||||||
// FIXME: add dialog
|
|
||||||
console.error({
|
|
||||||
title: 'Warning',
|
|
||||||
message: `Unable to load package list: ${error.message}`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
setIsLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
checkUserAlreadyLoggedIn();
|
checkUserAlreadyLoggedIn();
|
||||||
loadOnHandler();
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyleBaseline />
|
<StyleBaseline />
|
||||||
<Box display="flex" flexDirection="column" height="100%">
|
<StyledBox display="flex" flexDirection="column" height="100%">
|
||||||
{isLoading ? (
|
<>
|
||||||
<Loading />
|
<Router history={history}>
|
||||||
) : (
|
<AppContextProvider user={user}>
|
||||||
<>
|
<Header />
|
||||||
<Router history={history}>
|
<StyledBoxContent flexGrow={1}>
|
||||||
<AppContextProvider packages={packages} user={user}>
|
{/* eslint-disable-next-line react/jsx-max-depth */}
|
||||||
<Header />
|
<AppRoute />
|
||||||
<StyledBoxContent flexGrow={1}>
|
</StyledBoxContent>
|
||||||
<AppRoute />
|
</AppContextProvider>
|
||||||
</StyledBoxContent>
|
</Router>
|
||||||
</AppContextProvider>
|
<Footer />
|
||||||
</Router>
|
</>
|
||||||
<Footer />
|
</StyledBox>
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Box>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { createContext } from 'react';
|
|||||||
export interface AppProps {
|
export interface AppProps {
|
||||||
user?: User;
|
user?: User;
|
||||||
scope: string;
|
scope: string;
|
||||||
packages: any[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
|
|||||||
@@ -3,15 +3,13 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import AppContext, { AppProps, User } from './AppContext';
|
import AppContext, { AppProps, User } from './AppContext';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
packages: any[];
|
|
||||||
user?: User;
|
user?: User;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
const AppContextProvider: React.FC<Props> = ({ children, packages, user }) => {
|
const AppContextProvider: React.FC<Props> = ({ children, user }) => {
|
||||||
const [state, setState] = useState<AppProps>({
|
const [state, setState] = useState<AppProps>({
|
||||||
scope: window.VERDACCIO_SCOPE || '',
|
scope: window.VERDACCIO_SCOPE || '',
|
||||||
packages,
|
|
||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -22,13 +20,6 @@ const AppContextProvider: React.FC<Props> = ({ children, packages, user }) => {
|
|||||||
});
|
});
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setState({
|
|
||||||
...state,
|
|
||||||
packages,
|
|
||||||
});
|
|
||||||
}, [packages]);
|
|
||||||
|
|
||||||
const setUser = (user?: User) => {
|
const setUser = (user?: User) => {
|
||||||
setState({
|
setState({
|
||||||
...state,
|
...state,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ const AppRoute: React.FC = () => {
|
|||||||
throw Error('The app Context was not correct used');
|
throw Error('The app Context was not correct used');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { user, packages } = appContext;
|
const { user } = appContext;
|
||||||
|
|
||||||
const isUserLoggedIn = user && user.username;
|
const isUserLoggedIn = user && user.username;
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ const AppRoute: React.FC = () => {
|
|||||||
<Suspense fallback={<Loading />}>
|
<Suspense fallback={<Loading />}>
|
||||||
<Switch>
|
<Switch>
|
||||||
<ReactRouterDomRoute exact={true} path={Route.ROOT}>
|
<ReactRouterDomRoute exact={true} path={Route.ROOT}>
|
||||||
<HomePage isUserLoggedIn={!!isUserLoggedIn} packages={packages || []} />
|
<HomePage isUserLoggedIn={!!isUserLoggedIn} />
|
||||||
</ReactRouterDomRoute>
|
</ReactRouterDomRoute>
|
||||||
<ReactRouterDomRoute exact={true} path={Route.PACKAGE}>
|
<ReactRouterDomRoute exact={true} path={Route.PACKAGE}>
|
||||||
<VersionContextProvider>
|
<VersionContextProvider>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +0,0 @@
|
|||||||
import { css } from '@emotion/core';
|
|
||||||
|
|
||||||
import { theme } from '../design-tokens/theme';
|
|
||||||
|
|
||||||
export const alertError = css({
|
|
||||||
backgroundColor: `${theme.palette.red} !important`,
|
|
||||||
minWidth: 'inherit !important',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const alertErrorMsg = css({
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const alertIcon = css({
|
|
||||||
opacity: 0.9,
|
|
||||||
marginRight: '8px',
|
|
||||||
});
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
import React, { FC } from 'react';
|
|
||||||
|
|
||||||
import { isEmail } from '../../utils/url';
|
|
||||||
import Tooltip from '../../muiComponents/Tooltip';
|
|
||||||
import Avatar from '../../muiComponents/Avatar';
|
|
||||||
|
|
||||||
export interface AvatarDeveloper {
|
|
||||||
name: string;
|
|
||||||
packageName: string;
|
|
||||||
version: string;
|
|
||||||
avatar: string;
|
|
||||||
email: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AvatarTooltip: FC<AvatarDeveloper> = ({ name, packageName, version, avatar, email }) => {
|
|
||||||
const avatarComponent = <Avatar aria-label={name} src={avatar} />;
|
|
||||||
function renderLinkForMail(
|
|
||||||
email: string,
|
|
||||||
avatarComponent: JSX.Element,
|
|
||||||
packageName: string,
|
|
||||||
version: string
|
|
||||||
): JSX.Element {
|
|
||||||
if (!email || isEmail(email) === false) {
|
|
||||||
return avatarComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<a href={`mailto:${email}?subject=${packageName}@${version}`} target={'_top'}>
|
|
||||||
{avatarComponent}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <Tooltip title={name}>{renderLinkForMail(email, avatarComponent, packageName, version)}</Tooltip>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { AvatarTooltip };
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import { AvatarTooltip } from './AvatarTooltip';
|
|
||||||
|
|
||||||
export { AvatarTooltip };
|
|
||||||
export default AvatarTooltip;
|
|
||||||
@@ -1,80 +1,52 @@
|
|||||||
import React, { ReactElement } from 'react';
|
import React, { useContext } from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import ActionBar from '../ActionBar';
|
|
||||||
import Author from '../Author';
|
|
||||||
import Developers from '../Developers';
|
|
||||||
import Dist from '../Dist/Dist';
|
|
||||||
import Engine from '../Engines/Engines';
|
|
||||||
import Install from '../Install';
|
|
||||||
import Repository from '../Repository/Repository';
|
|
||||||
import { DetailContext } from '../../pages/Version';
|
import { DetailContext } from '../../pages/Version';
|
||||||
import List from '../../muiComponents/List';
|
import Paper from '../../muiComponents/Paper';
|
||||||
import Card from '../../muiComponents/Card';
|
import ActionBar from '../ActionBar';
|
||||||
import CardContent from '../../muiComponents/CardContent';
|
import Repository from '../Repository';
|
||||||
|
import Engines from '../Engines';
|
||||||
|
import Dist from '../Dist';
|
||||||
|
import Install from '../Install';
|
||||||
|
import Author from '../Author';
|
||||||
|
import Developers, { DeveloperType } from '../Developers';
|
||||||
|
import { Theme } from '../../design-tokens/theme';
|
||||||
|
|
||||||
import { TitleListItem, TitleListItemText, PackageDescription, PackageVersion } from './styles';
|
import DetailSidebarTitle from './DetailSidebarTitle';
|
||||||
|
import DetailSidebarFundButton from './DetailSidebarFundButton';
|
||||||
|
|
||||||
const renderLatestDescription = (description, version, isLatest = true): JSX.Element => {
|
const StyledPaper = styled(Paper)<{ theme?: Theme }>(({ theme }) => ({
|
||||||
return (
|
padding: theme.spacing(3, 2),
|
||||||
<>
|
}));
|
||||||
<PackageDescription>{description}</PackageDescription>
|
|
||||||
{version ? (
|
|
||||||
<PackageVersion>
|
|
||||||
<small>{`${isLatest ? 'Latest v' : 'v'}${version}`}</small>
|
|
||||||
</PackageVersion>
|
|
||||||
) : null}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderCopyCLI = (): JSX.Element => <Install />;
|
const DetailSidebar: React.FC = () => {
|
||||||
const renderMaintainers = (): JSX.Element => <Developers type="maintainers" />;
|
const detailContext = useContext(DetailContext);
|
||||||
const renderContributors = (): JSX.Element => <Developers type="contributors" />;
|
|
||||||
const renderRepository = (): JSX.Element => <Repository />;
|
const { packageMeta, packageName, packageVersion } = detailContext;
|
||||||
const renderAuthor = (): JSX.Element => <Author />;
|
|
||||||
const renderEngine = (): JSX.Element => <Engine />;
|
if (!packageMeta || !packageName) {
|
||||||
const renderDist = (): JSX.Element => <Dist />;
|
return null;
|
||||||
const renderActionBar = (): JSX.Element => <ActionBar />;
|
}
|
||||||
const renderTitle = (packageName, packageVersion, packageMeta): JSX.Element => {
|
|
||||||
const version = packageVersion ? packageVersion : packageMeta.latest.version;
|
|
||||||
const isLatest = typeof packageVersion === 'undefined';
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<List className="detail-info">
|
<StyledPaper className={'sidebar-info'}>
|
||||||
<TitleListItem alignItems="flex-start" button={true}>
|
<DetailSidebarTitle
|
||||||
<TitleListItemText
|
description={packageMeta.latest?.description}
|
||||||
primary={<b>{packageName}</b>}
|
isLatest={typeof packageVersion === 'undefined'}
|
||||||
secondary={renderLatestDescription(packageMeta.latest.description, version, isLatest)}
|
packageName={packageName}
|
||||||
/>
|
version={packageVersion || packageMeta.latest.version}
|
||||||
</TitleListItem>
|
/>
|
||||||
</List>
|
<ActionBar />
|
||||||
|
<Install />
|
||||||
|
<DetailSidebarFundButton />
|
||||||
|
<Repository />
|
||||||
|
<Engines />
|
||||||
|
<Dist />
|
||||||
|
<Author />
|
||||||
|
<Developers type={DeveloperType.MAINTAINERS} />
|
||||||
|
<Developers type={DeveloperType.CONTRIBUTORS} />
|
||||||
|
</StyledPaper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderSideBar(packageName, packageVersion, packageMeta): ReactElement<HTMLElement> {
|
|
||||||
return (
|
|
||||||
<div className={'sidebar-info'}>
|
|
||||||
<Card>
|
|
||||||
<CardContent>
|
|
||||||
{renderTitle(packageName, packageVersion, packageMeta)}
|
|
||||||
{renderActionBar()}
|
|
||||||
{renderCopyCLI()}
|
|
||||||
{renderRepository()}
|
|
||||||
{renderEngine()}
|
|
||||||
{renderDist()}
|
|
||||||
{renderAuthor()}
|
|
||||||
{renderMaintainers()}
|
|
||||||
{renderContributors()}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const DetailSidebar = (): JSX.Element => {
|
|
||||||
const { packageName, packageMeta, packageVersion } = React.useContext(DetailContext);
|
|
||||||
|
|
||||||
return renderSideBar(packageName, packageVersion, packageMeta);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default DetailSidebar;
|
export default DetailSidebar;
|
||||||
|
|||||||
103
src/components/DetailSidebar/DetailSidebarFundButton.test.tsx
Normal file
103
src/components/DetailSidebar/DetailSidebarFundButton.test.tsx
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
import { render } from '../../utils/test-react-testing-library';
|
||||||
|
import { DetailContext, DetailContextProps } from '../../pages/Version';
|
||||||
|
|
||||||
|
import DetailSidebarFundButton from './DetailSidebarFundButton';
|
||||||
|
|
||||||
|
const ComponentToBeRendered: React.FC<{ contextValue: DetailContextProps }> = ({ contextValue }) => (
|
||||||
|
<DetailContext.Provider value={contextValue}>
|
||||||
|
<DetailSidebarFundButton />
|
||||||
|
</DetailContext.Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
const detailContextValue: DetailContextProps = {
|
||||||
|
packageName: 'foo',
|
||||||
|
readMe: 'test',
|
||||||
|
enableLoading: () => {},
|
||||||
|
isLoading: false,
|
||||||
|
hasNotBeenFound: false,
|
||||||
|
packageMeta: {
|
||||||
|
_uplinks: {},
|
||||||
|
latest: {
|
||||||
|
name: '@verdaccio/local-storage',
|
||||||
|
version: '8.0.1-next.1',
|
||||||
|
dist: { fileCount: 0, unpackedSize: 0, tarball: 'http://localhost:8080/bootstrap/-/bootstrap-4.3.1.tgz' },
|
||||||
|
homepage: 'https://verdaccio.org',
|
||||||
|
bugs: {
|
||||||
|
url: 'https://github.com/verdaccio/monorepo/issues',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('test DetailSidebarFundButton', () => {
|
||||||
|
test('should not display the button if fund is missing', () => {
|
||||||
|
const wrapper = render(<ComponentToBeRendered contextValue={detailContextValue} />);
|
||||||
|
|
||||||
|
expect(wrapper.queryByText('Fund')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not display the button if url is missing', () => {
|
||||||
|
const value = _.merge(detailContextValue, {
|
||||||
|
packageMeta: {
|
||||||
|
latest: {
|
||||||
|
funding: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = render(<ComponentToBeRendered contextValue={value} />);
|
||||||
|
|
||||||
|
expect(wrapper.queryByText('Fund')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not display the button if url is not a string', () => {
|
||||||
|
const value = _.merge(detailContextValue, {
|
||||||
|
packageMeta: {
|
||||||
|
latest: {
|
||||||
|
funding: {
|
||||||
|
url: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = render(<ComponentToBeRendered contextValue={value} />);
|
||||||
|
|
||||||
|
expect(wrapper.queryByText('Fund')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should not display the button if url is not an url', () => {
|
||||||
|
const value = _.merge(detailContextValue, {
|
||||||
|
packageMeta: {
|
||||||
|
latest: {
|
||||||
|
funding: {
|
||||||
|
url: 'somethign different as url',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = render(<ComponentToBeRendered contextValue={value} />);
|
||||||
|
|
||||||
|
expect(wrapper.queryByText('Fund')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should display the button if url is a valid url', () => {
|
||||||
|
const value = _.merge(detailContextValue, {
|
||||||
|
packageMeta: {
|
||||||
|
latest: {
|
||||||
|
funding: {
|
||||||
|
url: 'https://opencollective.com/verdaccio',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapper = render(<ComponentToBeRendered contextValue={value} />);
|
||||||
|
|
||||||
|
expect(wrapper.getByText('Fund')).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
48
src/components/DetailSidebar/DetailSidebarFundButton.tsx
Normal file
48
src/components/DetailSidebar/DetailSidebarFundButton.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import React, { useContext } from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
import Favorite from '@material-ui/icons/Favorite';
|
||||||
|
|
||||||
|
import Button from '../../muiComponents/Button';
|
||||||
|
import Link from '../Link';
|
||||||
|
import { isURL } from '../../utils/url';
|
||||||
|
import { Theme } from '../../design-tokens/theme';
|
||||||
|
import { DetailContext } from '../../pages/Version';
|
||||||
|
|
||||||
|
const StyledLink = styled(Link)<{ theme?: Theme }>(({ theme }) => ({
|
||||||
|
marginTop: theme && theme.spacing(1),
|
||||||
|
marginBottom: theme && theme.spacing(1),
|
||||||
|
textDecoration: 'none',
|
||||||
|
display: 'block',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledFavoriteIcon = styled(Favorite)<{ theme?: Theme }>(({ theme }) => ({
|
||||||
|
color: theme && theme.palette.orange,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledFundStrong = styled('strong')({
|
||||||
|
marginRight: 3,
|
||||||
|
});
|
||||||
|
|
||||||
|
/* eslint-disable react/jsx-no-bind */
|
||||||
|
const DetailSidebarFundButton: React.FC = () => {
|
||||||
|
const detailContext = useContext(DetailContext);
|
||||||
|
|
||||||
|
const { packageMeta } = detailContext;
|
||||||
|
|
||||||
|
const fundingUrl = packageMeta?.latest?.funding?.url as string;
|
||||||
|
|
||||||
|
if (!isURL(fundingUrl)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledLink external={true} to={fundingUrl}>
|
||||||
|
<Button color="primary" fullWidth={true} startIcon={<StyledFavoriteIcon />} variant="outlined">
|
||||||
|
<StyledFundStrong>{'Fund'}</StyledFundStrong>
|
||||||
|
{'this package'}
|
||||||
|
</Button>
|
||||||
|
</StyledLink>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DetailSidebarFundButton;
|
||||||
32
src/components/DetailSidebar/DetailSidebarTitle.tsx
Normal file
32
src/components/DetailSidebar/DetailSidebarTitle.tsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import Box from '../../muiComponents/Box';
|
||||||
|
import Heading from '../../muiComponents/Heading';
|
||||||
|
import { Theme } from '../../design-tokens/theme';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
packageName: string;
|
||||||
|
description?: string;
|
||||||
|
version: string;
|
||||||
|
isLatest: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const StyledHeading = styled(Heading)({
|
||||||
|
fontSize: '1rem',
|
||||||
|
fontWeight: 700,
|
||||||
|
});
|
||||||
|
|
||||||
|
const StyledBoxVersion = styled(Box)<{ theme?: Theme }>(({ theme }) => ({
|
||||||
|
color: theme && theme.palette.text.secondary,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const DetailSidebarTitle: React.FC<Props> = ({ description, packageName, version, isLatest }) => (
|
||||||
|
<Box className={'detail-info'} display="flex" flexDirection="column" marginBottom="8px">
|
||||||
|
<StyledHeading>{packageName}</StyledHeading>
|
||||||
|
{description && <div>{description}</div>}
|
||||||
|
<StyledBoxVersion>{`${isLatest ? 'Latest v' : 'v'}${version}`}</StyledBoxVersion>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default DetailSidebarTitle;
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
|
|
||||||
import ListItem from '../../muiComponents/ListItem';
|
|
||||||
import ListItemText from '../../muiComponents/ListItemText';
|
|
||||||
|
|
||||||
export const TitleListItem = styled(ListItem)({
|
|
||||||
paddingLeft: 0,
|
|
||||||
paddingRight: 0,
|
|
||||||
paddingBottom: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const TitleListItemText = styled(ListItemText)({
|
|
||||||
paddingLeft: 0,
|
|
||||||
paddingRight: 0,
|
|
||||||
paddingTop: '8px',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const PackageDescription = styled('span')({
|
|
||||||
display: 'block',
|
|
||||||
});
|
|
||||||
|
|
||||||
export const PackageVersion = styled('span')({
|
|
||||||
display: 'block',
|
|
||||||
});
|
|
||||||
@@ -3,8 +3,7 @@ import React from 'react';
|
|||||||
import { mount } from '../../utils/test-enzyme';
|
import { mount } from '../../utils/test-enzyme';
|
||||||
import { DetailContextProvider } from '../../pages/Version';
|
import { DetailContextProvider } from '../../pages/Version';
|
||||||
|
|
||||||
import Developers, { DevelopersType } from './Developers';
|
import Developers, { DeveloperType, Fab } from './Developers';
|
||||||
import { Fab } from './styles';
|
|
||||||
|
|
||||||
describe('test Developers', () => {
|
describe('test Developers', () => {
|
||||||
const packageMeta = {
|
const packageMeta = {
|
||||||
@@ -35,14 +34,13 @@ describe('test Developers', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
test('should render the component with no items', () => {
|
test('should render the component with no items', () => {
|
||||||
const type: DevelopersType = 'maintainers';
|
|
||||||
const packageMeta = {
|
const packageMeta = {
|
||||||
latest: {},
|
latest: {},
|
||||||
};
|
};
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<DetailContextProvider value={{ packageMeta }}>
|
<DetailContextProvider value={{ packageMeta }}>
|
||||||
<Developers type={type} />
|
<Developers type={DeveloperType.MAINTAINERS} />
|
||||||
</DetailContextProvider>
|
</DetailContextProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -50,11 +48,10 @@ describe('test Developers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should render the component for maintainers with items', () => {
|
test('should render the component for maintainers with items', () => {
|
||||||
const type: DevelopersType = 'maintainers';
|
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<DetailContextProvider value={{ packageMeta }}>
|
<DetailContextProvider value={{ packageMeta }}>
|
||||||
<Developers type={type} />
|
<Developers type={DeveloperType.MAINTAINERS} />
|
||||||
</DetailContextProvider>
|
</DetailContextProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -62,11 +59,10 @@ describe('test Developers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should render the component for contributors with items', () => {
|
test('should render the component for contributors with items', () => {
|
||||||
const type: DevelopersType = 'contributors';
|
|
||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<DetailContextProvider value={{ packageMeta }}>
|
<DetailContextProvider value={{ packageMeta }}>
|
||||||
<Developers type={type} />
|
<Developers type={DeveloperType.CONTRIBUTORS} />
|
||||||
</DetailContextProvider>
|
</DetailContextProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -74,7 +70,6 @@ describe('test Developers', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('should test onClick the component avatar', () => {
|
test('should test onClick the component avatar', () => {
|
||||||
const type: DevelopersType = 'contributors';
|
|
||||||
const packageMeta = {
|
const packageMeta = {
|
||||||
latest: {
|
latest: {
|
||||||
packageName: 'foo',
|
packageName: 'foo',
|
||||||
@@ -95,7 +90,7 @@ describe('test Developers', () => {
|
|||||||
const wrapper = mount(
|
const wrapper = mount(
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
<DetailContextProvider value={{ packageMeta }}>
|
<DetailContextProvider value={{ packageMeta }}>
|
||||||
<Developers type={type} visibleMax={1} />
|
<Developers type={DeveloperType.CONTRIBUTORS} visibleMax={1} />
|
||||||
</DetailContextProvider>
|
</DetailContextProvider>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,60 +1,89 @@
|
|||||||
import React, { FC, Fragment } from 'react';
|
import React, { useState, useCallback, useContext, useEffect, useMemo } from 'react';
|
||||||
import Add from '@material-ui/icons/Add';
|
import Add from '@material-ui/icons/Add';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import { DetailContext } from '../../pages/Version';
|
import { DetailContext } from '../../pages/Version';
|
||||||
import { AvatarTooltip } from '../AvatarTooltip';
|
import Tooltip from '../../muiComponents/Tooltip';
|
||||||
|
import Avatar from '../../muiComponents/Avatar';
|
||||||
|
import Box from '../../muiComponents/Box';
|
||||||
|
import Text from '../../muiComponents/Text';
|
||||||
|
import FloatingActionButton from '../../muiComponents/FloatingActionButton';
|
||||||
|
import { Theme } from '../../design-tokens/theme';
|
||||||
|
|
||||||
import { Details, StyledText, Content, Fab } from './styles';
|
import getUniqueDeveloperValues from './get-unique-developer-values';
|
||||||
|
|
||||||
export type DevelopersType = 'contributors' | 'maintainers';
|
export const Fab = styled(FloatingActionButton)<{ theme?: Theme }>(props => ({
|
||||||
|
backgroundColor: props.theme && props.theme.palette.primary.main,
|
||||||
|
color: props.theme && props.theme.palette.white,
|
||||||
|
}));
|
||||||
|
|
||||||
|
export enum DeveloperType {
|
||||||
|
CONTRIBUTORS = 'contributors',
|
||||||
|
MAINTAINERS = 'maintainers',
|
||||||
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
type: DevelopersType;
|
type: DeveloperType;
|
||||||
visibleMax?: number;
|
visibleMax?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const StyledText = styled(Text)<{ theme?: Theme }>(({ theme }) => ({
|
||||||
|
fontWeight: theme && theme.fontWeight.bold,
|
||||||
|
marginBottom: '10px',
|
||||||
|
textTransform: 'capitalize',
|
||||||
|
}));
|
||||||
|
|
||||||
|
const StyledBox = styled(Box)({
|
||||||
|
'> *': {
|
||||||
|
margin: 5,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
export const VISIBLE_MAX = 6;
|
export const VISIBLE_MAX = 6;
|
||||||
|
|
||||||
const Developers: FC<Props> = ({ type, visibleMax }) => {
|
const Developers: React.FC<Props> = ({ type, visibleMax = VISIBLE_MAX }) => {
|
||||||
const [visibleDevs, setVisibleDevs] = React.useState<number>(visibleMax || VISIBLE_MAX);
|
const detailContext = useContext(DetailContext);
|
||||||
const { packageMeta } = React.useContext(DetailContext);
|
|
||||||
|
|
||||||
const handleLoadMore = (): void => {
|
if (!detailContext) {
|
||||||
setVisibleDevs(visibleDevs + VISIBLE_MAX);
|
throw Error("The app's detail Context was not correct used");
|
||||||
};
|
|
||||||
|
|
||||||
const renderDeveloperDetails = ({ name, avatar, email }, packageMeta): JSX.Element => {
|
|
||||||
const { name: packageName, version } = packageMeta.latest;
|
|
||||||
|
|
||||||
return <AvatarTooltip avatar={avatar} email={email} name={name} packageName={packageName} version={version} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderDevelopers = (developers, packageMeta): JSX.Element => {
|
|
||||||
const listVisibleDevelopers = developers.slice(0, visibleDevs);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
<StyledText variant={'subtitle1'}>{type}</StyledText>
|
|
||||||
<Content>
|
|
||||||
{listVisibleDevelopers.map(developer => (
|
|
||||||
<Details key={developer.email}>{renderDeveloperDetails(developer, packageMeta)}</Details>
|
|
||||||
))}
|
|
||||||
{visibleDevs < developers.length && (
|
|
||||||
<Fab onClick={handleLoadMore} size="small">
|
|
||||||
<Add />
|
|
||||||
</Fab>
|
|
||||||
)}
|
|
||||||
</Content>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const developerList = packageMeta && packageMeta.latest[type];
|
|
||||||
if (!developerList || developerList.length === 0) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return renderDevelopers(developerList, packageMeta);
|
const developers = useMemo(() => getUniqueDeveloperValues(detailContext.packageMeta?.latest[type]), [
|
||||||
|
detailContext.packageMeta,
|
||||||
|
type,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [visibleDevelopersMax, setVisibleDevelopersMax] = useState(visibleMax);
|
||||||
|
const [visibleDevelopers, setVisibleDevelopers] = useState(developers);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!developers) return;
|
||||||
|
setVisibleDevelopers(developers.slice(0, visibleDevelopersMax));
|
||||||
|
}, [developers, visibleDevelopersMax]);
|
||||||
|
|
||||||
|
const handleSetVisibleDevelopersMax = useCallback(() => {
|
||||||
|
setVisibleDevelopersMax(visibleDevelopersMax + VISIBLE_MAX);
|
||||||
|
}, [visibleDevelopersMax]);
|
||||||
|
|
||||||
|
if (!visibleDevelopers || !developers) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StyledText variant={'subtitle1'}>{type}</StyledText>
|
||||||
|
<StyledBox display="flex" flexWrap="wrap" margin="10px 0 10px 0">
|
||||||
|
{visibleDevelopers.map(visibleDeveloper => (
|
||||||
|
<Tooltip key={visibleDeveloper.email} title={visibleDeveloper.name}>
|
||||||
|
<Avatar alt={visibleDeveloper.name} src={visibleDeveloper.avatar} />
|
||||||
|
</Tooltip>
|
||||||
|
))}
|
||||||
|
{visibleDevelopersMax < developers.length && (
|
||||||
|
<Fab onClick={handleSetVisibleDevelopersMax} size="small">
|
||||||
|
<Add />
|
||||||
|
</Fab>
|
||||||
|
)}
|
||||||
|
</StyledBox>
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Developers;
|
export default Developers;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
12
src/components/Developers/get-unique-developer-values.ts
Normal file
12
src/components/Developers/get-unique-developer-values.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { Developer } from '../../../types/packageMeta';
|
||||||
|
|
||||||
|
function getUniqueDeveloperValues(developers?: Array<Developer>): undefined | Array<Developer> {
|
||||||
|
if (!developers) return;
|
||||||
|
return developers.reduce(
|
||||||
|
(accumulator: Array<Developer>, current: Developer) =>
|
||||||
|
accumulator.some(developer => developer.email === current.email) ? accumulator : [...accumulator, current],
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default getUniqueDeveloperValues;
|
||||||
@@ -1 +1 @@
|
|||||||
export { default } from './Developers';
|
export { default, DeveloperType } from './Developers';
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ describe('<Header /> component with logged in state', () => {
|
|||||||
test('should load the component in logged out state', () => {
|
test('should load the component in logged out state', () => {
|
||||||
const { container, queryByTestId, getByText } = render(
|
const { container, queryByTestId, getByText } = render(
|
||||||
<Router>
|
<Router>
|
||||||
<AppContextProvider packages={props.packages}>
|
<AppContextProvider>
|
||||||
<Header />
|
<Header />
|
||||||
</AppContextProvider>
|
</AppContextProvider>
|
||||||
</Router>
|
</Router>
|
||||||
@@ -32,7 +32,7 @@ describe('<Header /> component with logged in state', () => {
|
|||||||
test('should load the component in logged in state', () => {
|
test('should load the component in logged in state', () => {
|
||||||
const { container, getByTestId, queryByText } = render(
|
const { container, getByTestId, queryByText } = render(
|
||||||
<Router>
|
<Router>
|
||||||
<AppContextProvider packages={props.packages} user={props.user}>
|
<AppContextProvider user={props.user}>
|
||||||
<Header />
|
<Header />
|
||||||
</AppContextProvider>
|
</AppContextProvider>
|
||||||
</Router>
|
</Router>
|
||||||
@@ -46,7 +46,7 @@ describe('<Header /> component with logged in state', () => {
|
|||||||
test('should open login dialog', async () => {
|
test('should open login dialog', async () => {
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<Router>
|
<Router>
|
||||||
<AppContextProvider packages={props.packages}>
|
<AppContextProvider>
|
||||||
<Header />
|
<Header />
|
||||||
</AppContextProvider>
|
</AppContextProvider>
|
||||||
</Router>
|
</Router>
|
||||||
@@ -61,7 +61,7 @@ describe('<Header /> component with logged in state', () => {
|
|||||||
test('should logout the user', async () => {
|
test('should logout the user', async () => {
|
||||||
const { getByText, getByTestId } = render(
|
const { getByText, getByTestId } = render(
|
||||||
<Router>
|
<Router>
|
||||||
<AppContextProvider packages={props.packages} user={props.user}>
|
<AppContextProvider user={props.user}>
|
||||||
<Header />
|
<Header />
|
||||||
</AppContextProvider>
|
</AppContextProvider>
|
||||||
</Router>
|
</Router>
|
||||||
@@ -79,7 +79,7 @@ describe('<Header /> component with logged in state', () => {
|
|||||||
test("The question icon should open a new tab of verdaccio's website - installation doc", () => {
|
test("The question icon should open a new tab of verdaccio's website - installation doc", () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
<Router>
|
<Router>
|
||||||
<AppContextProvider packages={props.packages} user={props.user}>
|
<AppContextProvider user={props.user}>
|
||||||
<Header />
|
<Header />
|
||||||
</AppContextProvider>
|
</AppContextProvider>
|
||||||
</Router>
|
</Router>
|
||||||
@@ -92,7 +92,7 @@ describe('<Header /> component with logged in state', () => {
|
|||||||
test('should open the registrationInfo modal when clicking on the info icon', async () => {
|
test('should open the registrationInfo modal when clicking on the info icon', async () => {
|
||||||
const { getByTestId } = render(
|
const { getByTestId } = render(
|
||||||
<Router>
|
<Router>
|
||||||
<AppContextProvider packages={props.packages} user={props.user}>
|
<AppContextProvider user={props.user}>
|
||||||
<Header />
|
<Header />
|
||||||
</AppContextProvider>
|
</AppContextProvider>
|
||||||
</Router>
|
</Router>
|
||||||
@@ -109,7 +109,7 @@ describe('<Header /> component with logged in state', () => {
|
|||||||
test('should close the registrationInfo modal when clicking on the button close', async () => {
|
test('should close the registrationInfo modal when clicking on the button close', async () => {
|
||||||
const { getByTestId, getByText, queryByTestId } = render(
|
const { getByTestId, getByText, queryByTestId } = render(
|
||||||
<Router>
|
<Router>
|
||||||
<AppContextProvider packages={props.packages} user={props.user}>
|
<AppContextProvider user={props.user}>
|
||||||
<Header />
|
<Header />
|
||||||
</AppContextProvider>
|
</AppContextProvider>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
import Logo from '../Logo';
|
import Logo from '../Logo';
|
||||||
|
|
||||||
@@ -8,7 +9,14 @@ interface Props {
|
|||||||
|
|
||||||
const HeaderLogo: React.FC<Props> = ({ logo }) => {
|
const HeaderLogo: React.FC<Props> = ({ logo }) => {
|
||||||
if (logo) {
|
if (logo) {
|
||||||
return <img alt="logo" height="40px" src={logo} />;
|
const Wrapper = styled('div')({
|
||||||
|
fontSize: 0,
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<img alt="logo" height="40px" src={logo} />
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Logo />;
|
return <Logo />;
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ const InstallListItemText = styled(ListItemText)({
|
|||||||
const PackageMangerAvatar = styled(Avatar)({
|
const PackageMangerAvatar = styled(Avatar)({
|
||||||
borderRadius: '0px',
|
borderRadius: '0px',
|
||||||
padding: '0',
|
padding: '0',
|
||||||
|
img: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export enum DependencyManager {
|
export enum DependencyManager {
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ exports[`<Install /> renders correctly 1`] = `
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.emotion-2 img {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
.emotion-10 {
|
.emotion-10 {
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import LoginDialog from './LoginDialog';
|
|||||||
|
|
||||||
const appContextValue: AppContextProps = {
|
const appContextValue: AppContextProps = {
|
||||||
scope: '',
|
scope: '',
|
||||||
packages: [],
|
|
||||||
setUser: jest.fn(),
|
setUser: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,14 @@ const RepositoryListItemText = styled(ListItemText)({
|
|||||||
margin: 0,
|
margin: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const RepositoryAvatar = styled(Avatar)({
|
||||||
|
borderRadius: '0px',
|
||||||
|
padding: '0',
|
||||||
|
img: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const Repository: React.FC = () => {
|
const Repository: React.FC = () => {
|
||||||
const detailContext = React.useContext(DetailContext);
|
const detailContext = React.useContext(DetailContext);
|
||||||
|
|
||||||
@@ -58,7 +66,7 @@ const Repository: React.FC = () => {
|
|||||||
return (
|
return (
|
||||||
<List dense={true} subheader={<StyledText variant="subtitle1">{'Repository'}</StyledText>}>
|
<List dense={true} subheader={<StyledText variant="subtitle1">{'Repository'}</StyledText>}>
|
||||||
<RepositoryListItem button={true}>
|
<RepositoryListItem button={true}>
|
||||||
<Avatar src={git} />
|
<RepositoryAvatar src={git} />
|
||||||
<RepositoryListItemText
|
<RepositoryListItemText
|
||||||
primary={
|
primary={
|
||||||
<CopyToClipBoard text={repositoryURL}>
|
<CopyToClipBoard text={repositoryURL}>
|
||||||
|
|||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
exports[`<UpLinks /> component should render the component when there is no uplink 1`] = `"<h6 class=\\"MuiTypography-root MuiTypography-subtitle1 MuiTypography-gutterBottom\\">verdaccio has no uplinks.</h6>"`;
|
exports[`<UpLinks /> component should render the component when there is no uplink 1`] = `"<h6 class=\\"MuiTypography-root MuiTypography-subtitle1 MuiTypography-gutterBottom\\">verdaccio has no uplinks.</h6>"`;
|
||||||
|
|
||||||
exports[`<UpLinks /> component should render the component with uplinks 1`] = `"<h6 class=\\"MuiTypography-root css-5wp24z-StyledText e14i1sy10 MuiTypography-subtitle1\\">Uplinks</h6><ul class=\\"MuiList-root MuiList-padding\\"><li class=\\"MuiListItem-root MuiListItem-gutters\\"><div class=\\"MuiListItemText-root css-1pxn9ma-ListItemText e14i1sy12\\"><span class=\\"MuiTypography-root MuiListItemText-primary MuiTypography-body1\\">npmjs</span></div><div class=\\"css-t1rp47-Spacer e14i1sy11\\"></div><div class=\\"MuiListItemText-root css-1pxn9ma-ListItemText e14i1sy12\\"><span class=\\"MuiTypography-root MuiListItemText-primary MuiTypography-body1\\">a year ago</span></div></li></ul>"`;
|
exports[`<UpLinks /> component should render the component with uplinks 1`] = `"<h6 class=\\"MuiTypography-root css-5wp24z-StyledText e14i1sy10 MuiTypography-subtitle1\\">Uplinks</h6><ul class=\\"MuiList-root MuiList-padding\\"><li class=\\"MuiListItem-root MuiListItem-gutters\\"><div class=\\"MuiListItemText-root css-1pxn9ma-ListItemText e14i1sy12\\"><span class=\\"MuiTypography-root MuiListItemText-primary MuiTypography-body1\\">npmjs</span></div><div class=\\"css-t1rp47-Spacer e14i1sy11\\"></div><div class=\\"MuiListItemText-root css-1pxn9ma-ListItemText e14i1sy12\\"><span class=\\"MuiTypography-root MuiListItemText-primary MuiTypography-body1\\">2 years ago</span></div></li></ul>"`;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const colors = {
|
|||||||
black: '#000',
|
black: '#000',
|
||||||
white: '#fff',
|
white: '#fff',
|
||||||
red: '#d32f2f',
|
red: '#d32f2f',
|
||||||
|
orange: '#CD4000',
|
||||||
greySuperLight: '#f5f5f5',
|
greySuperLight: '#f5f5f5',
|
||||||
greyLight: '#d3d3d3',
|
greyLight: '#d3d3d3',
|
||||||
greyLight2: '#908ba1',
|
greyLight2: '#908ba1',
|
||||||
@@ -93,6 +94,7 @@ declare module '@material-ui/core/styles/createPalette' {
|
|||||||
black: string;
|
black: string;
|
||||||
white: string;
|
white: string;
|
||||||
red: string;
|
red: string;
|
||||||
|
orange: string;
|
||||||
greySuperLight: string;
|
greySuperLight: string;
|
||||||
greyLight: string;
|
greyLight: string;
|
||||||
greyLight2: string;
|
greyLight2: string;
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
import React, { forwardRef } from 'react';
|
import React, { forwardRef } from 'react';
|
||||||
import { default as MaterialUITypography, TypographyProps } from '@material-ui/core/Typography';
|
import { default as MaterialUITypography } from '@material-ui/core/Typography';
|
||||||
|
|
||||||
|
import { TextProps } from './TextConfig';
|
||||||
|
|
||||||
type TextType = 'subtitle1' | 'subtitle2' | 'body1' | 'body2';
|
|
||||||
type TextRef = HTMLElementTagNameMap[keyof HTMLElementTagNameMap];
|
type TextRef = HTMLElementTagNameMap[keyof HTMLElementTagNameMap];
|
||||||
|
|
||||||
export interface TextProps extends Omit<TypographyProps, 'variant'> {
|
|
||||||
variant?: TextType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The reference is already from type of the Component, so the any below is not a problem
|
// The reference is already from type of the Component, so the any below is not a problem
|
||||||
const Text = forwardRef<TextRef, TextProps>(function Text(props, ref) {
|
const Text = forwardRef<TextRef, TextProps>(function Text(props, ref) {
|
||||||
return <MaterialUITypography {...props} ref={ref} />;
|
return <MaterialUITypography {...props} ref={ref} />;
|
||||||
|
|||||||
7
src/muiComponents/Text/TextConfig.ts
Normal file
7
src/muiComponents/Text/TextConfig.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { TypographyProps } from '@material-ui/core/Typography';
|
||||||
|
|
||||||
|
type TextType = 'subtitle1' | 'subtitle2' | 'body1' | 'body2';
|
||||||
|
|
||||||
|
export interface TextProps extends Omit<TypographyProps, 'variant'> {
|
||||||
|
variant?: TextType;
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
export { default, TextProps } from './Text';
|
export { default } from './Text';
|
||||||
|
export { TextProps } from './TextConfig';
|
||||||
|
|||||||
@@ -1,24 +1,6 @@
|
|||||||
import { createContext, Consumer, Provider } from 'react';
|
import { createContext, Consumer, Provider } from 'react';
|
||||||
|
|
||||||
import { PackageMetaInterface } from '../../../types/packageMeta';
|
import { DetailContextProps, VersionPageConsumerProps } from './version-config';
|
||||||
export interface DetailContextProps {
|
|
||||||
packageMeta: PackageMetaInterface;
|
|
||||||
packageVersion?: string;
|
|
||||||
readMe: string;
|
|
||||||
packageName: string;
|
|
||||||
enableLoading: () => void;
|
|
||||||
isLoading: boolean;
|
|
||||||
hasNotBeenFound: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface VersionPageConsumerProps {
|
|
||||||
packageMeta: PackageMetaInterface;
|
|
||||||
readMe: string;
|
|
||||||
packageName: string;
|
|
||||||
packageVersion?: string;
|
|
||||||
// FIXME: looking for the appropiated type here
|
|
||||||
enableLoading: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DetailContext = createContext<Partial<DetailContextProps>>({});
|
export const DetailContext = createContext<Partial<DetailContextProps>>({});
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
export {
|
export { DetailContext, DetailContextConsumer, DetailContextProvider } from './context';
|
||||||
DetailContext,
|
export { VersionPageConsumerProps, DetailContextProps } from './version-config';
|
||||||
DetailContextConsumer,
|
|
||||||
DetailContextProvider,
|
|
||||||
DetailContextProps,
|
|
||||||
VersionPageConsumerProps,
|
|
||||||
} from './context';
|
|
||||||
export { default } from './Version';
|
export { default } from './Version';
|
||||||
|
|||||||
19
src/pages/Version/version-config.ts
Normal file
19
src/pages/Version/version-config.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { PackageMetaInterface } from '../../../types/packageMeta';
|
||||||
|
|
||||||
|
export interface DetailContextProps {
|
||||||
|
enableLoading: () => void;
|
||||||
|
hasNotBeenFound: boolean;
|
||||||
|
isLoading: boolean;
|
||||||
|
packageMeta: PackageMetaInterface;
|
||||||
|
packageName: string;
|
||||||
|
packageVersion?: string;
|
||||||
|
readMe: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VersionPageConsumerProps {
|
||||||
|
enableLoading: () => void;
|
||||||
|
packageMeta: PackageMetaInterface;
|
||||||
|
packageName: string;
|
||||||
|
packageVersion?: string;
|
||||||
|
readMe: string;
|
||||||
|
}
|
||||||
@@ -1,17 +1,35 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { PackageList } from '../../components/PackageList';
|
import { PackageList } from '../../components/PackageList';
|
||||||
import { PackageInterface } from '../../components/Package/Package';
|
import API from '../../utils/api';
|
||||||
|
import Loading from '../../components/Loading';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isUserLoggedIn: boolean;
|
isUserLoggedIn: boolean;
|
||||||
packages: PackageInterface[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Home: React.FC<Props> = ({ packages }) => (
|
const Home: React.FC<Props> = ({ isUserLoggedIn }) => {
|
||||||
<div className="container content">
|
const [packages, setPackages] = useState([]);
|
||||||
<PackageList packages={packages} />
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
</div>
|
const loadPackages = async () => {
|
||||||
);
|
try {
|
||||||
|
const packages = await API.request('packages', 'GET');
|
||||||
|
// FIXME add correct type for package
|
||||||
|
setPackages(packages as never[]);
|
||||||
|
} catch (error) {
|
||||||
|
// FIXME: add dialog
|
||||||
|
console.error({
|
||||||
|
title: 'Warning',
|
||||||
|
message: `Unable to load package list: ${error.message}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
loadPackages().then();
|
||||||
|
}, [isUserLoggedIn]);
|
||||||
|
|
||||||
|
return <div className="container content">{isLoading ? <Loading /> : <PackageList packages={packages} />}</div>;
|
||||||
|
};
|
||||||
|
|
||||||
export default Home;
|
export default Home;
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export function handleResponseType(response: Response): Promise<[boolean, Blob |
|
|||||||
return Promise.all([response.ok, response.text()]);
|
return Promise.all([response.ok, response.text()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// unfortunatelly on download files there is no header available
|
// unfortunately on download files there is no header available
|
||||||
if (response.url && response.url.endsWith('.tgz') === true) {
|
if (response.url && response.url.endsWith('.tgz') === true) {
|
||||||
return Promise.all([response.ok, response.blob()]);
|
return Promise.all([response.ok, response.blob()]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,14 +17,18 @@ describe('utils', () => {
|
|||||||
expect(isEmail('')).toBeFalsy();
|
expect(isEmail('')).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getRegistryURL() - should keep slash if location is a sub directory', () => {
|
test('getRegistryURL() - should not change when location change', () => {
|
||||||
|
expect(getRegistryURL()).toBe('http://localhost');
|
||||||
history.pushState({}, 'page title', '/-/web/detail');
|
history.pushState({}, 'page title', '/-/web/detail');
|
||||||
expect(getRegistryURL()).toBe('http://localhost/-/web/detail');
|
expect(getRegistryURL()).toBe('http://localhost');
|
||||||
history.pushState({}, 'page title', '/');
|
history.pushState({}, 'page title', '/');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getRegistryURL() - should not add slash if location is not a sub directory', () => {
|
test('getRegistryURL() - should change when UI options change', () => {
|
||||||
expect(getRegistryURL()).toBe('http://localhost');
|
expect(getRegistryURL()).toBe('http://localhost');
|
||||||
|
window.__VERDACCIO_BASENAME_UI_OPTIONS.base = 'http://localhost/test';
|
||||||
|
expect(getRegistryURL()).toBe('http://localhost/test');
|
||||||
|
window.__VERDACCIO_BASENAME_UI_OPTIONS.base = 'http://localhost';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ export function isEmail(email: string): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getRegistryURL(): string {
|
export function getRegistryURL(): string {
|
||||||
// Don't add slash if it's not a sub directory
|
return window.__VERDACCIO_BASENAME_UI_OPTIONS.base;
|
||||||
return `${location.origin}${location.pathname === '/' ? '' : location.pathname}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extractFileName(url: string): string {
|
export function extractFileName(url: string): string {
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ auth:
|
|||||||
users:
|
users:
|
||||||
foo:
|
foo:
|
||||||
name: test
|
name: test
|
||||||
password: test
|
password: test #pragma: allowlist secret
|
||||||
bar:
|
bar:
|
||||||
name: bar
|
name: bar
|
||||||
password: test
|
password: test #pragma: allowlist secret
|
||||||
security:
|
security:
|
||||||
api:
|
api:
|
||||||
jwt:
|
jwt:
|
||||||
@@ -23,7 +23,7 @@ security:
|
|||||||
web:
|
web:
|
||||||
sign:
|
sign:
|
||||||
expiresIn: 100d
|
expiresIn: 100d
|
||||||
notBefore: 1
|
notBefore: 0
|
||||||
|
|
||||||
uplinks:
|
uplinks:
|
||||||
npmjs:
|
npmjs:
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ module.exports = {
|
|||||||
output: {
|
output: {
|
||||||
path: `${env.APP_ROOT}/static/`,
|
path: `${env.APP_ROOT}/static/`,
|
||||||
filename: '[name].[hash].js',
|
filename: '[name].[hash].js',
|
||||||
publicPath: '/-/static',
|
publicPath: '/-/static/',
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default {
|
|||||||
}),
|
}),
|
||||||
new HTMLWebpackPlugin({
|
new HTMLWebpackPlugin({
|
||||||
__UI_OPTIONS: JSON.stringify({
|
__UI_OPTIONS: JSON.stringify({
|
||||||
base: '/',
|
base: new URL('/', 'https://localhost:4872'),
|
||||||
}),
|
}),
|
||||||
title: 'Verdaccio Dev UI',
|
title: 'Verdaccio Dev UI',
|
||||||
scope: '',
|
scope: '',
|
||||||
|
|||||||
@@ -24,9 +24,23 @@ export interface PackageMetaInterface {
|
|||||||
type?: string;
|
type?: string;
|
||||||
url?: string;
|
url?: string;
|
||||||
};
|
};
|
||||||
|
description?: string;
|
||||||
|
funding?: Funding;
|
||||||
|
maintainers?: Array<Developer>;
|
||||||
|
contributors?: Array<Developer>;
|
||||||
};
|
};
|
||||||
_uplinks: {};
|
_uplinks: {};
|
||||||
}
|
}
|
||||||
|
export interface Developer {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
avatar: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Funding {
|
||||||
|
type?: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
interface LicenseInterface {
|
interface LicenseInterface {
|
||||||
type: string;
|
type: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user